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
In the functional world what you describe is typically called sequence
.
It is the concept of swapping around the order of the generic types:
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/
let lift l =
if List.contains None l then None
else Some (List.map Option.get l)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With