Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to multiply two (double option)s in F#

Tags:

monads

f#

My code contains quite a few double option types; I've been using the Option.map function quite successfully so far to eliminate the need to have to match on Some and None all over the place and treat them as lifted types but am not sure what to do in the following scenario:

let multiplyTwoOptions option1 option2 : double option =
  if not (option1.IsSome && option2.IsSome) then None
  else Some (option1.Value * option2.Value)

I've read that you shouldn't use IsSome in this way, but the alternative (as far as I can see, to pattern match on both sequentially, seems quite long winded). I'm still quite new to F# so wondered if there is a more idiomatic way? I feel like maybe I need something like an Option.fold2 to act on two options at once but there isn't one.

like image 877
Dutts Avatar asked Aug 22 '16 14:08

Dutts


3 Answers

Patterns can be nested, that's their great power. In this particular case, you can pattern match on the tuple:

match option1, option2 with
| Some x, Some y -> Some (x * y)
| _ -> None
like image 82
Fyodor Soikin Avatar answered Oct 23 '22 08:10

Fyodor Soikin


The correct answer is in fact the following:

https://fsharpforfunandprofit.com/posts/elevated-world/#apply

If one needs some code then it boils down to about the following example:

module Option =

    // The apply function for Options
    let apply fOpt xOpt = 
        match fOpt,xOpt with
        | Some f, Some x -> Some (f x)
        | _ -> None


let (<!>) = Option.map
let (<*>) = Option.apply

let a = Some(4)
let b = Some(5)

let  multiplication = (*)

//Some multiplication function applied on a and resulting function applied on b
let res1 = Some(multiplication) <*> a <*> b
let res2 = Some(*) <*> a <*> b

//Map a onto multiplication function and resulting function applied on b
let res3 = multiplication <!> a <*> b
let res4 = (*) <!> a <*> b


val res1 : int option = Some 20
val res2 : int option = Some 20
val res3 : int option = Some 20
val res4 : int option = Some 20

//The following is without any options to try to clarify the above

let op = (*) //multiplication
//let partialRes = (*) 4
let partialRes = op 4 //make function for multiplying param with 4
let fullres = partialRes 5 //use function for multiplying with 4

val op : (int -> int -> int)
val partialRes : (int -> int)
val fullres : int = 20

The reason for saying this is that with the above one can work in and out of what Wlaschin calls "elevated world", or Option in this case, and mix stuff from both. Kind of. Read the full site or book Wlaschin has written for good measure.

There is no need to make any function taking an Option as a param and the wrapping and unwrapping can be taken care of once and for all.

As the above code shows (shamelessly stolen from link, and somewhat rewritten), the function Richard needs is:

Option.apply

Yes the symbols will possibly confuse especially since we are talking multiplication or * here, but these symbols for map <!> and apply <*> are somewhat 'standard'.

I think the comments in the code are more or less correct with regards to how to read the code.

And yes, I maybe need to work on my teaching styles ;-)

like image 26
Helge Rene Urholm Avatar answered Oct 23 '22 08:10

Helge Rene Urholm


Your fold2 idea is not off the mark. Even if it's not part of standard library, you can easily implement such functions yourself.

Here's a bind2:

module Option =
    let bind2 f a b =
        match a, b with
        | Some a, Some b -> f a b
        | _, _ -> None 

I've seen bind3 used as well, but that's probably stretching it. I doubt there's much practical use for more than 3 arguments. You can implement different arities of map, iter or fold following a similar scheme.

This is not as robust or formally elegant as using applicative functors, but it solves the problem without introducing much conceptual overhead.

like image 3
scrwtp Avatar answered Oct 23 '22 10:10

scrwtp