3
\$\begingroup\$

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:

  1. Is there a better way for the type system to express the fact that ResultForDate is the same structure as InputForDate but with all option values replaced by the underlying types?
  2. 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
Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Jun 19, 2015 at 15:22
\$\endgroup\$
0

1 Answer 1

2
\$\begingroup\$

The code duplication problem can be solve using Operators.defaultArgs, which does pretty much what you're trying to do

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

to

AssetValue = defaultArg input.AssetValue <| previousYear.AssetValue * (1.0 + previousYear.InterestRate)
InterestRate = defaultArg input.InterestRate previousYear.InterestRate

I don't really have an idea for your data type. You both of your type doesn't have the same fields types, so you can't really inherit. And since both of your fields are optional, you can't really compose with another record.

answered Jun 19, 2015 at 18:02
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.