Functional Inspiration – Irrelevantable, part 2

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.

This entry was posted in Functional Inspiration and tagged , . Bookmark the permalink.

Comments are closed.