【问题标题】:Use of typeof<_> in active pattern在活动模式中使用 typeof<_>
【发布时间】:2011-07-21 16:37:50
【问题描述】:

给定以下人为的主动模式:

let (|TypeDef|_|) (typeDef:Type) (value:obj) =
  if obj.ReferenceEquals(value, null) then None
  else
    let typ = value.GetType()
    if typ.IsGenericType && typ.GetGenericTypeDefinition() = typeDef then Some(typ.GetGenericArguments())
    else None

以下内容:

let dict = System.Collections.Generic.Dictionary<string,obj>()
match dict with
| TypeDef typedefof<Dictionary<_,_>> typeArgs -> printfn "%A" typeArgs
| _ -> ()

给出错误:

模式匹配中的意外类型应用。应为“->”或其他标记。

但这有效:

let typ = typedefof<Dictionary<_,_>>
match dict with
| TypeDef typ typeArgs -> printfn "%A" typeArgs
| _ -> ()

为什么这里不允许typedefof(或typeof)?

【问题讨论】:

  • 可能只是解析器中的一个错误;在&gt;&gt; 之间加一个空格有帮助吗?
  • 只是为了清楚并避免“此规则永远不会匹配”的混淆,TypeDef 是否应该是部分活动模式?即(|TypeDef|_|) 而不是(|TypeDef|)

标签: f# active-pattern


【解决方案1】:

即使您使用参数化的活动模式(其中参数是某个表达式),编译器也会将参数解析为模式(而不是表达式),因此语法受到更多限制。

我认为这与此处讨论的问题基本相同:How can I pass complex expression to parametrized active pattern?(我不确定实际的编译器实现,但 F# 规范说它应该解析为一种模式)。

作为一种解决方法,您可以在引号内编写任何表达式,因此您可以这样做:

let undef<'T> : 'T = Unchecked.defaultof<_>

let (|TypeDef|) (typeExpr:Expr) (value:obj) =
  let typeDef = typeExpr.Type.GetGenericTypeDefinition()
  // ...

let dict = System.Collections.Generic.Dictionary<string,obj>()
match dict with
| TypeDef <@ undef<Dictionary<_,_>> @> typeArgs -> printfn "%A" typeArgs
| _ -> ()

【讨论】:

  • 由于活动模式的全部力量似乎在于它们与功能的虚拟互换性,知道为什么存在这种限制吗?
  • 我很困惑为什么表达式在这里有效,但不是函数调用。是因为表达式是常量吗?我不确定 C# 中的 typeof 是如何实现的,但它似乎不像 F# 中那样是一个函数。我猜如果 F# 的 typeof 以类似方式实现,这可能会起作用,因为它的行为更像是一个常量。
  • @Daniel - 我认为这只是一个句法限制。 Stephen 发布的代码可以解析为模式:TypeDef (null:Dictionary&lt;_,_&gt;) typeArgs(类型规范是类型注释)。调用具有泛型类型参数的函数(如typedefof&lt;...&gt;)不能被解析为模式。
  • 这就是我的想法,这使它看起来是一个更微不足道的限制。
【解决方案2】:

除了 Tomas 的回答之外,在这种情况下,麻烦的语法似乎与显式类型参数有关。另一种解决方法是使用虚拟参数来传输类型信息

let (|TypeDef|_|) (_:'a) (value:obj) =
  let typeDef = typedefof<'a>
  if obj.ReferenceEquals(value, null) then None
  else
    let typ = value.GetType()
    if typ.IsGenericType && typ.GetGenericTypeDefinition() = typeDef then Some(typ.GetGenericArguments())
    else None

let z = 
    let dict = System.Collections.Generic.Dictionary<string,obj>()
    match dict with
    | TypeDef (null:Dictionary<_,_>) typeArgs -> printfn "%A" typeArgs
    | _ -> ()

【讨论】:

  • 谢谢。不错的解决方法。我想更好地了解限制和通用解决方法。例如,如果您想将函数调用的结果作为参数传递给活动模式怎么办?
  • 不客气。事实上,我正在考虑制作一个函数,它可以采用像 f&lt;'a&gt; : int -&gt; int 这样的函数并将其转换为 'a -&gt; int -&gt; int 类型的函数,但我发现要么你不能,要么我不知道如何表明这一点函数通过类型注释具有显式类型参数...
  • 你想用一个主动模式来做这件事吗?还是只是一个函数?
  • 我认为没有办法在不暴露类型参数的情况下做到这一点,无论是在返回值中还是通过参数。但是,您可以使用 DU 捆绑此类类型参数,以传递给其他函数。看看这个:pastebin.com/QVarC30C
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-11
  • 1970-01-01
相关资源
最近更新 更多