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

此生再无相见时 提交于 2019-12-01 18:04:28

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

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 ;-)

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.

I'd go with the match, but as an alternative you can also use the maybe expression from https://www.nuget.org/packages/FSharpx.Extras/

let multiplyTwoOptions option1 option2 : double option =
  maybe {
    let! x = option1
    let! y = option2
    return x * y
  }

While for a known set of inputs the match is more straightforward, you'll note the maybe expression is nicer for longer ad-hoc expressions:

maybe {
  let! a = getAOption()
  let! b = getBOption a
  let! c = getCOption a b
  return! getFinalOption a b c
}

vs

match getAOption() with
| None -> None
| Some a ->
  match getBOption a with
  | None -> None
  | Some b -> 
    match getCOption a b with
    | None -> None
    | Some c -> getFinalOption a b c

This also has an advantage over the applicative functor style in that e.g. the function generating b can depend on a, and c can depend on a and b, and so on, which is not possible in applicative style. The computation expression syntax also makes it easier to understand than applicative functors which have nothing but the raw operators.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!