I’m using vague phrasing here because I haven’t studied this subject properly yet.
I’d like to know if this add function is idiomatic F#? Or can it be expressed in a better way?
let (>>=) o f =
match o with
| Some x -> f x
| None -> None
let add x y =
x >>= fun x ->
y >>= fun y ->
Some (x + y)
let printRes res =
match res with
| None -> printfn "None"
| Some a -> printfn "Some %O" a
// dotnet fsi monads.fsx
printf "\n--- MONADS EXAMPLE ---\n\n"
printf "add (Some 1) (Some 2) => ";; printRes <| add (Some 1) (Some 2);;
printf "add (Some 1) None => ";; printRes <| add (Some 1) None;;
printf "add None (Some 2) => ";; printRes <| add None (Some 2);;
printf "add None None => ";; printRes <| add None None;;
Also, I’m confused about Option.bind. It looks like it’s got the params in the wrong order, I can’t find a way to use it effectively in its original form.
let bindRev o fn = Option.bind fn o
let mult x y =
bindRev x (fun x ->
bindRev y (fun y ->
Some(x * y)))
I think an argument can be made that idiomatically, any monads in F# would be expressed with computation expressions. Though there isn’t a builtin one for option or a lot of other structures, so that might be a controversial statement. There are many implementations for an option CE, however. See f# - is there an option<> computation expression anywhere? - Stack Overflow. So using F#x for example, the add function would look like
let add x y = maybe {
let! x' = x
let! y' = y
return x' + y'
}
as far as using Option.bind, you’re expected to use it with the pipeline operator
let mult x y =
x |> Option.bind (fun x ->
y |> Option.bind (fun y ->
Some (x * y)))
Yeah, the formatting is one of the common reasons for using a computation expression or the infix >>= operator. If you haven’t already started working through it, I’d highly recommend the F# for fun and profit article series on this whole concept:
In general, in other programming languages, I don’t like shadowing variables.
But I find rebinding with let or let! pretty handy and I can’t see any drawbacks to doing that if the types differ and the previous binding can be “thrown away”. Like this:
let minus x y =
maybe {
let! x = x
let! y = y
return x - y
}
I can’t find any “official” advice on shadowing. I would certainly shadow in this case, I just didn’t include it in my example because it’s a separate topic from monads and could add confusion.
Someone else asked the question about shadowing earlier, and I wrote up my personal thoughts on the matter, but as I pointed out there, it’s very opinionated and I don’t have any “official” guidance to point to.