Сложенный список опций

Учитывая список [Some 1; Some 2; Some 3], мне нужен вывод Some 6. Для списка [Some 1; None] следует указать None.

Но я нахожу это немного сложнее, чем я думал, чтобы достичь этого чистым способом.

Лучшее, что я мог придумать, это

let someNums = [Some 1; Some 2; Some 3]
someNums 
|> List.reduce (fun st v ->
  Option.bind (fun x ->
    Option.map (fun y -> x + y) st) v )

Ответ 1

Вы можете выполнить это, указав функцию map2 для значений параметра:

let optionMap2 f x y =
    match x, y with
    | (Some x', Some y') -> Some (f x' y')
    | _ -> None

Это позволит вам написать нужную функцию:

let sumSome = List.fold (optionMap2 (+)) (Some 0)

Пример:

> [Some 1; Some 2; Some 3] |> sumSome;;
val it : int option = Some 6
> [Some 1; None; Some 3] |> sumSome;;
val it : int option = None

В настоящий момент функция optionMap2 недоступна в основной библиотеке F #, но вероятно, будет частью модуля Option в будущее.

Ответ 2

let lift op a b =
    match a, b with
    | Some av, Some bv  -> Some(op av bv)
    | _, _ -> None

let plus = lift (+)

[Some 1; Some 2; Some 3]
|> List.reduce plus
// val it : int option = Some 6


[Some 1; None]
|> List.reduce plus
// val it : int option = None

со слоем

[Some 1; None]
|> List.fold plus (Some 0)
// val it : int option = None

[Some 1; Some 2; Some 3]
|> List.fold plus (Some 0)
// val it : int option = Some 6

[Some 1; None; Some 2] 
|> List.fold plus (Some 0)
// val it : int option = None

Ответ 3

Вот наивная реализация sequence Густаво говорила о:

let rec sequence = 
   function 
   | [] -> Some [] 
   | (Some o :: os) -> 
      sequence os 
      |> Option.map (fun os' -> o::os') 
   | _ -> None

(обратите внимание, что это не рекурсивно и не оптимизировано, поэтому вы должны преобразовать его, если вам понадобится его для больших списков)

Что будет работать так же, как Густаво сказал вам:

> sequence [Some 1; Some 2; Some 2] |> Option.map List.sum;;
val it : int option = Some 5

> sequence [Some 1; None; Some 2] |> Option.map List.sum;;
val it : int option = None