Активный шаблон сломан в F# 3.0
Этот активный шаблон компилируется с F# 2.0:
let (|Value|_|) value = // 'a -> 'T option
match box value with
| :? 'T as x -> Some x
| _ -> None
но в F# 3.0 выдает ошибку:
Активный паттерн '| Значение |_|' имеет тип результата, содержащий переменные типа, которые не определены входными данными. Распространенной причиной является [sic], когда случай результата не упоминается, например, 'let (|A|B|) (x:int) = A x'. Это можно исправить с помощью ограничения типа, например 'let (|A|B|) (x:int): Choice = A x'
Я старался:
let (|Value|_|) value : 'T option = ...
а также:
let (|Value|_|) (value: 'U) = ...
Как это можно исправить?
Среды: Visual Studio 2012 (RTM) и FSI v11.0.50727.1
РЕДАКТИРОВАТЬ: Вот более простое воспроизведение:
let (|X|) x = unbox x
4 ответа
В компиляторе F# 2.0 была ошибка, из-за которой компилятор неправильно анализировал и генерировал некорректный код для некоторых активных шаблонов с переменными свободного типа в результате; простое воспроизведение
let (|Check|) (a : int) = a, None
//let (|Check|) (a : int) = a, (None : int option)
let check a =
match a with
| Check (10, None) -> System.Console.WriteLine "10"
| Check (20, None) -> System.Console.WriteLine "20"
check 10
check 20
который генерирует странное предупреждение во время компиляции и компилирует в на первый взгляд некорректный код. Я предполагаю, что наша попытка исправить эту ошибку (и ограничить некоторые сумасшедшие случаи) в F# 3.0 также нарушила некоторый правовой кодекс как побочный ущерб от исправления.
Я напишу еще одну ошибку, но для F# 3.0 кажется, что вам нужно использовать один из обходных путей, упомянутых в других ответах.
Я еще не установил новую версию, но я согласен, что это выглядит немного подозрительно. Я думаю, что для этого ограничения могут быть веские причины, но ваш пример в другом вопросе кажется довольно убедительным.
В качестве обходного пути я думаю, что добавление параметра-свидетеля (который не используется, но намекает, каким будет тип результата) может работать:
let (|Value|_|) (witness:unit -> 'T) value : 'T option =
match box value with
| :? 'T as x -> Some x
| _ -> None
Конечно, это делает использование немного уродливым, потому что вам нужно придумать какой-то аргумент. В приведенном выше, я использовал свидетель типа unit -> 'T
, надеясь, что следующее может скомпилировать:
let witness () : 'T = failwith "!"
match box 1 with
| Value witness 1 -> printfn "one"
Если это не сработает, то вы можете попробовать использовать параметр-свидетель типа 'T
(но тогда вы должны предоставить реальную функцию, а не просто обобщенную функцию).
Ради полноты еще один обходной путь:
type Box<'R> = Box of obj
let (|Value|_|) ((Box x) : Box<'R> ) : 'R option =
match x with
| :? 'R as x -> Some x
| _ -> None
let check t =
match Box t with
| Value 1 -> printfn "one"
| Value 2 -> printfn "two"
check 1 // one
check 2 // two
однако он все еще будет страдать от проблемы, упомянутой @kvb в другом потоке. Лично я предпочту версию @ kvb с параметризованным активным шаблоном.