В функциональном мире то, что вы описываете, обычно называется sequence
.Это концепция переключения порядка общих типов:
- с
List<Option<int>>
на Option<List<int>>
Вот общий способ сделать это:
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
Здесь sequenceList
имеет сигнатуру типа 'a option list -> 'a list option
, которая также может быть выражена как List<Option<'a>> -> Option<List<'a>>
.Это именно то, что вы хотели.
Но, но, но ... по сравнению с другими ответами этот кажется более сложным.В чем смысл?
Дело в том, что это решение является универсальным и может применяться к любому монадическому (или аппликативному) типу.Например, общий тип, аналогичный Option
, равен Result
, и решение почти такое же, как и раньше:
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>
Видите?Помимо строк 2 и 3, остальные одинаковые.
Это позволяет вам абстрагировать концепцию и поместить ее в глубину вашего ума.В будущем, когда вы будете программировать, это будет происходить много раз, когда вам нужно будет перебирать универсальный тип, и типы в конечном итоге будут инвертированы относительно того, как вы хотите их.
Но нет необходимости заново изобретатьрулевое колесо.Вооружившись знаниями о том, как создать sequence
(и traverse
), вы можете просто достать из своей сумки инструменты и вытаскивать ее всякий раз, когда это необходимо для любых монадических или аппликативных типов, будь то Async
, Result
, Option
и т. Д.
Если вы хотите глубже изучить этот веб-сайт, где концепции поясняются наглядной графикой и примерами: https://fsharpforfunandprofit.com/posts/elevated-world-4/