Trying to express something more than Nullable<T>
Friday evening, the girls are in bed, the wife is out with friends… It seems that it is the perfect time to finish this post !
This is the third entry of a blog series about how functional programming, and the F# training I recently went to, gives me inspiration in my day-to-day work in C#. This post will show how you can build in F# the Irrelevantable type previously built in C#.
Let’s go straight to the code, here is the structure :
type Irrelevantable<'a> = | Relevant of 'a | Absent | Irrelevant
Using a very simple discriminated union type, we can easily express the three distinct cases needed.
Let’s now see how this type can be used. I first define a Product Type and a sample products list :
type Product = { Name: string; ProductType: string; Strike: Irrelevantable<decimal>; Premium: Irrelevantable<decimal>; TradedPrice: Irrelevantable<decimal> SwapPrice: Irrelevantable<decimal>; } let products = [ { Name = "568745"; ProductType = "Option"; Strike = Relevant(176m); Premium = Relevant(3.72m); TradedPrice = Irrelevant; SwapPrice = Irrelevant } ; { Name = "568746"; ProductType = "Option"; Strike = Relevant(176m); Premium = Absent; TradedPrice = Irrelevant; SwapPrice = Irrelevant } ; { Name = "568747"; ProductType = "Swap "; Strike = Irrelevant; Premium = Irrelevant; TradedPrice = Relevant(15.3m); SwapPrice = Relevant(15.6m) } ]
The goal is now to print a report based on the products. I then define a few utility / formatting functions :
let formatValue formatter value = match value with | Relevant(data) -> (formatter data) | Absent -> "" | Irrelevant -> "-" let padLeft (value:string) = value.PadLeft(7) let formatPrice = formatValue (fun (d:decimal) -> d.ToString("N2")) let getLine vals = System.String.Join(" | ", vals |> Seq.map padLeft) let getValues p = p.Name :: p.ProductType :: (formatPrice p.Strike) :: (formatPrice p.Premium) :: (formatPrice p.TradedPrice) :: (formatPrice p.SwapPrice) :: [] let getProductLine = getValues >> getLine
And I use these functions to generate the lines to print, beginning with a header line :
let headerLine = ( "Ref." :: "Type" :: "Strike" :: "Premium" :: "Price" :: "Swap price" :: []) |> getLine let productLines = products |> List.map getProductLine let allLines = headerLine :: productLines
Finally, I can print the report :
let test = allLines |> List.iter (printfn "%s")
And the output is the following :
Ref. | Type | Strike | Premium | Price | Swap price
568745 | Option | 176,00 | 3,72 | - | -
568746 | Option | 176,00 | | - | -
568747 | Swap | - | - | 15,30 | 15,60
You can then notice the distinction between the the absent value (the Premium column for the second product) and the values that have no meaning.