Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper way of applying two (or many) option values to a function in F#

Recently I discovered a style of programming which is very useful (and pretty) in functional world called Railway oriented programming. For example when want to create a pipeline of functions that produce option type, and we want to return None if any of them fails we can do something like this:

someOptionValue // : 'a option
>>= func1       // func1: 'a -> 'b option
>>= func2       // func2: 'b -> 'c option
and so on...

where (>>=) : 'a option -> (a' -> 'b option) -> 'b option operator applies a value to the left hands side if it's Some value or None otherwise.

But, here is my problem, what if we have a function that "takes" two (or many) option types, let say funcAB : 'a -> 'b -> 'c option, valA : 'a option and valB : 'b option and we still want to create this pipeline or use some nice operator (not create a new one specifically for this, but use some standard approach, in particular I don't want to use match ... with to "unpack" option values)

Currently I have something like this:

valA
>>= (funcAB >> Some)
>>= (fun ctr -> valB >>= ctr)

But is doesn't seem 'correct' (or fun is the better word ;] ), and it doesn't scale well if a function takes more parameters or we want to create a longer pipeline. Is there a better way to do this?

I've used F# syntax but I think this question can be applied to any functional programming language, like OCaml and Haskell.

EDIT (Solution):

Thanks to the chi's answer I've created following code F# which is much more idiomatic then what I previously had:

funcAB <!> valA <*> valB |> Option.flatten

And it looks well if we have more values: funcAB <!> valA <*> valB <*> valC <*> ....

I've used operators defined in YoLo.

like image 356
mateuszlewko Avatar asked Apr 16 '17 16:04

mateuszlewko


2 Answers

In Haskell, we can use Applicative syntax for that:

If

valA :: f a
valB :: f b
funAB :: a -> b -> f c

then

join $ funAB <$> valA <*> valB :: f c

provided f is a monad (like Maybe, Haskell's option).

It should be adaptable to F# as well, I guess, as long as you define your operators

(<$>) ::   (a -> b) -> f a -> f b
(<*>) :: f (a -> b) -> f a -> f b
join  :: f (f a) -> f a

The above trick is a poor man's version of Idris !-notation (bang notation).

Another common option is using do

do a <- valA
   b <- valB
   funAB a b

but this is comparable with using >>=, indeed:

valA >>= \a ->
valB >>= \b ->
funAB a b

is not much more complex.

like image 99
chi Avatar answered Oct 12 '22 23:10

chi


One option is to use computation expressions. For option there is no standard one but you can easily create your own:

type OptionBuilder() =
    member this.Bind (x, f) = Option.bind f x
    member this.Return x = Some x

let optional = OptionBuilder()

let a, b = Some(42), Some(7)
let f x y = x + y

let res = optional {
    let! x = a
    let! y = b
    return f x y
}

which closely resembles Haskells do notation. For more advanced features, have a look at F#+ which also has a generic applicative functor operator <*>.

like image 43
CaringDev Avatar answered Oct 12 '22 23:10

CaringDev