Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

List of option to a option list in F#

Tags:

f#

Given a list [Some 1; Some 2; Some 3] I would like an output Some [1;2;3] . Given a list [Some 1; None] should yield None

I am beginner in F# till now i have done that takes two options and makes a list option. How can i make a option list to go through this function to get the result?

let lift a b =
    match a, b with
    | Some av, Some bv  -> Some[av;bv]
    | _, _ -> None

lift (Some 2) None
like image 255
Mahmud Hasan Khan Avatar asked Oct 27 '18 21:10

Mahmud Hasan Khan


2 Answers

In the functional world what you describe is typically called sequence. It is the concept of swapping around the order of the generic types:

  • going from List<Option<int>> to Option<List<int>>

Here is a generic way to do it:

module Option =
    let (>>=) r f = Option.bind f r
    let rtn v     = Some v

    let traverseList f ls = 
        let folder head tail = f head >>= (fun h -> tail >>= (fun t -> h::t |> rtn))
        List.foldBack folder ls (rtn List.empty)
    let sequenceList   ls = traverseList id ls

    // val traverseList : ('a -> 'b option) -> 'a list -> 'b list option
    // val sequenceList : 'a option list -> 'a list option

Here sequenceList has a type signature of 'a option list -> 'a list option which can also be expressed as List<Option<'a>> -> Option<List<'a>>. Which is exactly what you wanted.

But, but, but... compared to the other answers this one seems extra complicated. What is the point?

The point is that this solution is generic and can be applied to any monadic (or applicative) type. For instance a similar generic type to Option is Result and the solution is almost the same as before:

module Result =
    let (>>=) r f = Result.bind f r
    let rtn v     = Ok v

    let traverseList f ls = 
        let folder head tail = f head >>= (fun h -> tail >>= (fun t -> h::t |> rtn))
        List.foldBack folder ls (rtn List.empty)
    let sequenceList   ls = traverseList id ls

    // val traverseList : ('a -> Result<'b,'c>) -> 'a list -> Result<'b list,'c>
    // val sequenceList : Result<'a,'b> list -> Result<'a list,'b>

See? Apart from lines 2 & 3 the rest are the same.

This allows you to abstract the concept and put it in the back of your mind. In the future as you program, this is going to happen many times where you need to iterate over a generic type and the types end up inverted as to how you want them.

But there is no need to reinvent the wheel. Armed with the knowledge of how to create sequence (and traverse) you can just reach into your bag of tools and pull it out whenever needed for any monadic or applicative types, whether is Async, Result, Option, etc.

If you want to dig deeper visit this website where the concepts are explained with clear graphics and examples: https://fsharpforfunandprofit.com/posts/elevated-world-4/

like image 156
AMieres Avatar answered Sep 22 '22 12:09

AMieres


let lift l = 
    if List.contains None l then None
    else Some (List.map Option.get l)
like image 36
gileCAD Avatar answered Sep 22 '22 12:09

gileCAD