My code takes a starting position (which must be completed) and a list of updates (each element of which is optional). It then projects future positions, using the appropriate value from each update, if present, or using a formula otherwise.
I'm looking for ways to reduce code duplication, especially as the real data structures will be substantially larger.
In particular:
- Is there a better way for the type system to express the fact that
ResultForDate
is the same structure asInputForDate
but with alloption
values replaced by the underlying types? - Can the duplication be removed from the repetitive code which checks for the existance of an input, uses it if possible and uses a formula otherwise? Perhaps using a monad?
module Demo
open System
type InputForDate = { Date: DateTime; AssetValue: option<float>; InterestRate: option<float> }
type ResultForDate = { Date: DateTime; AssetValue: float; InterestRate: float }
let projection (startingInput: ResultForDate) (futureInputs: InputForDate list) =
let project (previousYear: ResultForDate) (input: InputForDate) : ResultForDate =
{
Date = input.Date;
AssetValue =
match input.AssetValue with
| Some x -> x
| None -> previousYear.AssetValue * (1.0 + previousYear.InterestRate) ;
InterestRate =
match input.InterestRate with
| Some x -> x
| None -> previousYear.InterestRate
}
List.scan project startingInput futureInputs
// example of use
let startingInput = { ResultForDate.Date = new DateTime(2012, 12, 31); AssetValue = 100.0; InterestRate = 0.05 };
let futureInputs = [
{ InputForDate.Date = new DateTime(2013, 12, 31); AssetValue = Some 110.0; InterestRate = Some 0.04 };
{ Date = new DateTime(2014, 12, 31); AssetValue = None; InterestRate = Some 0.03 };
{ Date = new DateTime(2015, 12, 31); AssetValue = None; InterestRate = None } ]
let result = projection startingInput futureInputs