受到FSharpPlus中plus
函数(相当于mappend)实现的启发,我决定复制该模式并实现一个递增数字的通用函数(使用 LanguagePrimitives
可能会更容易,但这只是一个练习)。
type NumberExtensions =
static member Increment (x:float) = x + 1.0
static member Increment (x:int) = x + 1
//etc
let inline increment number =
let inline call (_:^E) (number:^N) = ((^E or ^N) : (static member Increment: ^N -> ^N) number)
call (Unchecked.defaultof<NumberExtensions>) number //!
let incrementedFloat = increment 100.0
let incrementedInt = increment 200
它按预期工作:
val incrementedFloat : float = 101.0
val incrementedInt : int = 201
不幸的是,我不太明白为什么我们在约束中需要或^N
。据我了解,约束条件是:
There's a static method named
Increment
, which takes an argument of some type (the same type as thenumber
's) and returns another value of the same type. This method must be defined either in^E
or in^N
.
这里我想:我们知道Increment
方法将在^E
中定义,所以我们可以安全地改变(^E或^N)
改为 ^E
。不幸的是,更改导致标有 !
的行出现以下错误:
A unique overload for method 'Increment' could not be determined based on type information prior to this program point. A type annotation may be needed. Candidates: static member NumberExtensions.Increment: x:float->float, static member NumberExtensions.Increment: x:int->int
为什么在约束中添加 或 ^N
可以消除这种歧义?它是否告诉编译器除了方法的可能位置之外的其他信息?
最佳答案
here对此进行了非常好的讨论。 。
一般来说,我们讨论的是如何访问与一系列类型相关的运算符字典,并且以更严格的方式处理用 'a(即质数 a)定义的类型。
一如既往,FSharp source code是一个值得一看的好地方。请参阅:
let inline GenericZero< ^T when ^T : (static member Zero : ^T) > : ^T =
在上面的讨论中,请参阅 Jon Harrop 关于类系统对 Haskell 性能影响的评论。我无法评论 F# 在这方面的 future ,但是,具有显式默认样式会带来某些优势(和权衡)。
在这种情况下,耐心等待这些怪癖,看看默认值是否真的对您有帮助,这可能会很有用。然后弄清楚你可以从这次经历中得出什么结论。不过,这个过程可能需要 6 个多月的时间。当人们固执己见时,也许有充分的理由。有时,如果您提前采取某种做法,很难意识到将会带来哪些好处。到最后,你仍然不必同意。您的情况可能另有要求。
关于.net - 显式类型成员约束中的 "or",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44510166/