2017-08-14 96 views
1

我正在编写我自己的Writer monad版本,以进行自我教育。我试图有一些普遍性(但不是试图通过静态解析的类型参数或其他解决方法完全实现/方法)。F#静态成员,缺少泛型类型的实例 - 可能的错误?

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

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?我不知道我是否可以忽略这个警告,或者最终是否可以咬我。所以我尝试“的.sum”之前把一个类型参数上的“作家” - 但现在,导致错误:

  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 ;它适用于我,没有错误,没有警告;并尝试非泛型类型不会改变我的问题。)

所以我愚弄名称,区分类型从它的值构造函数,添加“T”现在修复它自己!:

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.) 

Does th是有道理的?为什么很明显,在类型和构造函数中使用相同名称的通常做法似乎在这里引起歧义?

(旁注:“T”是不是一个伟大的选择,我想,因为这不是一个单子转换而对于上应用的所有类型注释的原因是调试。)

更新因托马斯的回答:

奇怪的是,对我来说,这也可以避免错误和警告。 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)  

回答

2

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

另一种方式来解决,这是添加RequireQualifiedAccess属性,它会隐藏类型名称后面的工会的情况下,所以你必须写Writer.Writer和第一名称指的是类型:

[<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,它可以工作,因为类型引用已解析。

+0

谢谢你看Tomas。我想我应该提交一个建议。这对我来说似乎是一个很好的机会,可以亲自尝试查看编译器,看看我是否可以自己调试,我一直在努力学习。 – RomnieEE

+0

现在,它在我的代码中引起了我的注意:无法推断静态“.sum”的Writer类型的警告消失了_wild card_ type注释!我发现在'apply'中将'WriterT'上的所有类型参数更改为'<_,_>'仍然可以避免这种警告。似乎很奇怪,通配符可以修复模糊性。但也许这是因为我不太了解类型推断是如何工作的。 – RomnieEE