F#静态成员,缺少泛型类型的实例化 - 可能的bug?

时间:2017-08-14 04:37:08

标签: generics f# static-members

我正在编写自己的Writer monad版本,用于自学。我试图有一些普遍性(但不是试图通过静态解析的类型参数或其他解决方法来实现/接近)。

我的第一个版本收到警告:

type Writer<'w, 'a> = | Writer of 'a * List<'w>  with 

  static member sum (l1:'w list) (l2: 'w list) = l1 @ l2

[<AutoOpen>]
module WriterMonadMod = 

  module Writer =
      let apply (mf:Writer<'w, ('a -> 'b)>) (ma:Writer<'w, 'a>) : Writer<'w, 'b> =
          let (Writer (f, l1)), (Writer (a, l2)) = (mf, ma)       
          let b = f a
          Writer (b, Writer.sum l1 l2)  // Warning, on "Writer.sum", 
//Instantiation of generic type missing, can't be inferred. 

好的,这是有道理的。我们想要哪个静态总和?对于哪些特定的实例化Writer类型?我不知道我是否可以忽略这个警告,或者它最终是否会咬我。所以我尝试在&#34; Writer&#34;上添加一个类型参数。之前&#34; .sum&#34; - 但现在导致错误:

          Writer (b, Writer<'w, 'b>.sum l1 l2)  //Error, on "<'w, 'b>",
//unexpected type arguments. 

这让我感到困惑,因为它看起来像Why can't F# infer the type in this case?Cell<float>.Create 1.0的另一个SO答案中的语法;这对我有效,没有错误,没有警告;尝试非泛型类型不会改变我的问题)。

所以我愚弄名字,区分类型和它的价值构造函数,添加&#34; T&#34; - 现在它修复了自己!:

type WriterT<'w, 'a> = | Writer of 'a * List<'w>  with 
//...
    static member sum (l1:'w list) (l2: 'w list) = l1 @ l2

[<AutoOpen>]
module WriterMonadMod = 

    module WriterT =
        let apply (mf:WriterT<'w, ('a -> 'b)>) (ma:WriterT<'w, 'a>) : WriterT<'w, 'b> =
            let (Writer (f, l1)), (Writer (a, l2)) = (mf, ma)       
            let b = f a
            Writer (b, WriterT<'w, 'b>.sum l1 l2)  //No warning, no error.
//(With "T" distinguishing the type name from value constructor.) 

这有意义吗?为什么显然在类型和构造函数中使用相同名称的惯常做法似乎导致歧义?

(旁注:&#34; T&#34;我猜不是一个很好的选择,因为这不是monad变换器。所有类型注释的原因都是调试。)

由于Tomas的回答而更新:

奇怪的是,对我而言,这也可以避免错误和警告。 Whildcards 用于解决歧义警告!?

    module WriterT =
        let apply (mf:WriterT<_,_>) (ma:WriterT<_,_>) : WriterT<_,_> =
            let (Writer (f, log1)), (Writer (a, log2)) = (mf, ma)       
            let b = f a
            Writer ( b, WriterT<_,_>.sum log1 log2 )      

1 个答案:

答案 0 :(得分:2)

第二种情况中的“意外类型参数”错误非常混乱(甚至可能是一个错误)。我认为这里发生的是编译器首先解析Writer作为案例名称,然后在找到类型参数时报告错误。然后它意识到你实际上指的是一种类型并改变了计划。重命名类型(在上一个示例中)可以解决这种歧义。

解决此问题的另一种方法是添加RequireQualifiedAccess属性,这将隐藏类型名称后面的联合案例,因此您必须编写Writer.Writer并且第一个名称引用类型:< / p>

[<RequireQualifiedAccess>]
type Writer<'w, 'a> = | Writer of 'a * List<'w>  with     
  static member sum (l1:'w list) (l2: 'w list) = l1 @ l2

module Writer =
  let apply (mf:Writer<'w, ('a -> 'b)>) (ma:Writer<'w, 'a>) : Writer<'w, 'b> =
      let (Writer.Writer (f, l1)), (Writer.Writer (a, l2)) = (mf, ma)       
      let b = f a
      Writer.Writer (b, Writer<_,_>.sum l1 l2)

现在您可以输入Writer<_, _>.sum并且它可以正常工作,因为类型引用已解决。