Как сопоставить шаблон в конструкторах объединения в `for.. in`

В Haskell, если у меня есть список типизированных типов объединения, например:

example :: [Either Int Char]
example = [Left 3, Right 'b', Left 6, Left 9, Right 'c']

Я могу использовать небольшой "трюк", чтобы извлечь все результаты, соответствующие определенному шаблону:

lefts :: [Int]
lefts = [l | Left l <- example]

Однако, если я попытаюсь перевести это на F #, я получаю сообщение об ошибке:

let lefts = [for Choice1Of2 l in example -> l]
                 ~~~~~~~~~~~~
Incomplete pattern matches on this expression. (...)

Это имеет большое значение (это может быть даже лучше, чем молча игнорирование значений Right, таких как Haskell!), но в F # есть ли удобный способ извлечения (и совпадения) всех значений сопоставление определенного шаблона в списке/последовательности?

Ответ 1

В F #, если вы не соответствуете всем случаям, вы получите предупреждение во всех сценариях.

Итак, вы можете написать совпадение с обоими случаями внутри выражения, но для вашего примера, а не для понимания, я бы использовал функцию List.choose:

let example = [Choice2Of2 3; Choice1Of2 'b'; Choice2Of2 6; Choice2Of2 9; Choice1Of2 'c']
List.choose (function (Choice1Of2 x) -> Some x | _ -> None) example
// val it : char list = ['b'; 'c']

Эта функция удобна для этих случаев.

Ответ 2

Я думаю, что самая близкая вещь, которую вы можете использовать, используя выражения списка F #, выглядит примерно так:

let lefts example = 
  [ for e in example do
      match e with Choice1Of2 l -> yield l | _ -> () ]

Если я правильно понимаю код Haskell, часть после | используется не только как экстрактор, но и как фильтр - неявно пропускает все вещи, которые не соответствуют шаблону.

F # не имеет такого же понятия в выражениях списков, поэтому вам нужно быть более подробным. Здесь мы просто перебираем все элементы с помощью for, а затем явно используем yield для создания нового значения для каждого Choice1Of2 в исходном списке (и мы просто пропускаем что-либо еще).

В зависимости от того, что вы делаете, использование List.choose (как упоминалось в ответе Густаво) может быть проще. Но выше, вероятно, ближе всего к синтаксису понимания Haskell.