Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to "concat" two options

Tags:

f#

I'm looking through the F# docs and am wondering about a "missing" function in the Option module. There is a map2 that takes two options and seems to do a logical AND on it. If either is None, the result is None. Otherwise if you've got a Some(a) and Some(b), it returns a Some(f(a,b)).

Where is the logical OR function? I would expect a similar function where if just one is Some, return it. But if both are Some, it would do the Some(f(a,b)) like for the logical AND case. I can write this out manually no problem with a pattern matching.

let concat f a b =
    match a, b with
    | Some(a), Some(b) -> Some(f a b)
    | Some(a), None -> Some(a)
    | None, Some(b) -> Some(b)
    | _ -> None

Practically this could be useful if you've got a list of optional numbers and you want to add them up. The answer is None if the list is empty or only contains None values. None doesn't affect the result, like a zero. Or a couple optional strings and you want to concatenate them.

I'm assuming since F# doesn't seem to have this function in the standard library, I must be thinking about things the wrong way. Or is there a more concise way of doing it?

like image 319
JustinM Avatar asked Oct 12 '25 10:10

JustinM


1 Answers

What you are looking for would be a function of the following type (Note that the type is different than that of map2 because the two options provided as argument need to have the same type (so that you can return one or the other when the other one is not available):

('a -> 'a -> 'a) -> 'a option -> 'a option -> 'a option

As far as I can see, this does not exist in the standard F# library, but it sounds like a potentially useful function. For example, this exists in Haskell as unionWith (see unionWith in the reducers package) - though, of course, in a more generalized form.

I think it is a very reasonable idea to add an implementation of this to your "utils" library or suggest an addition to the F# core libraries. I would also implement this using match as you did. Although it is perhaps interesting that you can also get this behaviour using map2 and orElse (which is, I think, less readable and probably slower):

let unionWith f a b = 
  Option.map2 f a b |> Option.orElse a |> Option.orElse b
like image 166
Tomas Petricek Avatar answered Oct 16 '25 04:10

Tomas Petricek