Что не так с сопоставлением ActivePattern с System.Type?
module Reflection =
[<RequireQualifiedAccess>]
module Type =
let isType<'a> = Unchecked.defaultof<'a>
let (|IsEqual|Isnt|) (_:'a) (t:Type):Choice<unit,unit> =
let t' = typeof<'a>
if t = t' then IsEqual else Isnt
let (|TypeOf|_|) (_:'a) (t:Type) :unit option =
if t = typeof<'a> then Some ()
else
//printfn "did not match %A to %A" typeof<'a> t
None
open Reflection
match typeof<string> with
// compiles just fine
| Type.TypeOf (Type.isType:int) as x -> Some x.Name
// does not compile
| Type.IsEqual (Type.isType:string) as x -> Some x.Name
| _ -> None
дает Type mismatch. Expecting a Type -> Choice<'a,'b> but given a Type -> 'c -> Choice<unit,unit> The type 'Choice<'a,'b>' does not match the type ''c -> Choice<unit,unit>' (using external F# compiler)
2 ответа
По какой-то причине подобные шаблоны просто запрещены. Только шаблоны с ровно одним результатом могут принимать дополнительные параметры.
Это законно:
let (|A|) x y = if x = y then 5 else 42
let f (A "foo" a) = printfn "%A" y
f "foo" // Prints "5"
f "bar" // Prints "42"
И это законно:
let (|B|_|) x y = if x = y then Some (y+5) else None
let f = function
| B 42 x -> printfn "%d" x
| _ -> printfn "try again"
f 42 // prints "47"
f 5 // prints "try again"
Но это все. Все остальные активные шаблоны должны быть без параметров. Оба из них являются незаконными:
let (|A|B|) x y = ...
let (|A|B|_|) x y = ...
Если бы мне пришлось размышлять, я бы сказал, что это связано с предсказуемой производительностью во время выполнения. Когда шаблон либо совпадает, либо нет, компилятор может запустить его ровно один раз для каждого значения параметра. Но если шаблон возвращает несколько вещей, и некоторые из этих вещей присутствуют в выражении соответствия, а другие нет, и не все из них имеют один и тот же параметр - становится очень сложно найти лучший способ сделать минимум количество вызовов функций.
Чтобы добавить к ответу Федора, в спецификации очень четко указаны действительные формы активных шаблонов (по крайней мере, в большей степени, чем MSDN) - подробности см. В параграфе 7.2.3 "Активные шаблоны".
Пять действительных форм:
- Единичный случай -
(|CaseName|) inp
- Частично -
(|CaseName|_|) inp
- Multi-case -
(|CaseName1|...|CaseNameN|) inp
- Отдельный случай с параметрами -
(|CaseName|) arg1 ... argn inp
- Частично с параметрами -
(|CaseName|_|) arg1 ... argn inp
Другие активные функции шаблона не разрешены.
Что здесь наиболее важно, так это то, что нет способа объединить шаблон с несколькими случаями с дополнительными параметрами.