Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Performing Calculations on F# option types

Tags:

f#

I'm trying to write some function that handle errors by returning double options instead of doubles. Many of these functions call eachother, and so take double options as inputs to output other double options. The problem is, I can't do with double options what I can do with doubles--something simple like add them using '+'.

For example, a function that divides two doubles, and returns a double option with none for divide by zero error. Then another function calls the first function and adds another double option to it.

Please tell me if there is a way to do this, or if I have completely misunderstood the meaning of F# option types.

like image 524
user3685285 Avatar asked Dec 11 '22 23:12

user3685285


2 Answers

This is called lifting - you can write function to lift another function over two options:

let liftOpt f o1 o2 = 
        match (o1, o2) with
        | (Some(v1), Some(v2)) -> Some(f v1 v2)
        | _ -> None

then you can supply the function to apply e.g.:

let inline addOpt o1 o2 = liftOpt (+) o1 o2
like image 171
Lee Avatar answered Dec 17 '22 14:12

Lee


liftA2 as mentioned above will provide a general way to 'lift' any function that works on the double arguments to a function that can work on the double option arguments.

However, in your case, you may have to write special functions yourself to handle the edge cases you mention

let (<+>) a b =
    match (a, b) with
    | (Some x, Some y) -> Some (x + y)
    | (Some x, None)   -> Some (x)
    | (None, Some x)   -> Some (x)
    | (None, None)     -> None

Note that liftA2 will not put the cases where you want to add None to Some(x) in automatically.

The liftA2 method for divide also needs some special handling, but its structure is generally what we would write ourselves

let (</>) a b =
    match (a, b) with
    | (Some x, Some y) when y <> 0.0d -> Some (x/y)
    | _ -> None

You can use these functions like

Some(2.0) <+> Some(3.0) // will give Some(5.0)
Some(1.0) </> Some(0.0) // will give None

Also, strictly speaking, lift is defined as a "higher order function" - something that takes a function and returns another function.

So it would look something like this:

let liftOpt2 f =
    (function a b ->
        match (a, b) with
        | (Some (a), Some (b)) -> f a b |> Some
        | _ -> None)
like image 28
John Azariah Avatar answered Dec 17 '22 14:12

John Azariah