为了好玩,我一直在玩F#中的类型类,使用这里显示的想法.我创建了一个Next类型类来表示具有"后继"的值,例如下一个1 = 2,下一个今天=明天等:
type Next = Next with
static member (++) (Next, x:int) = x + 1
static member (++) (Next, x:DateTime) = x.AddDays 1.0
let inline next x = Next ++ x
let v1 = next 1 // v1 = 2
let v2 = next DateTime.Now // v2 = Now + 1 day
Run Code Online (Sandbox Code Playgroud)
现在我想在泛型类中使用"nextable"类型:
type UsesNextable<'T>(nextable: 'T) = // Compile error 1
member inline this.Next =
let v = next nextable // Compile error 2
v.ToString()
Run Code Online (Sandbox Code Playgroud)
但是我收到以下错误:
我对第二个错误试图说的更感兴趣.什么神秘的^?155882是什么意思?我猜它可能与无法解决类型有关.此外,假设它试图推断约束为:
(Next or ^T) : (static member ( ++ ) : Next * ^T -> ^T)
Run Code Online (Sandbox Code Playgroud)
甚至可以使用when 'T:...?指定"或"条件?我无法弄清楚有效的语法.
最后,^ T建议使用静态解析的类型参数,但文档说它们不能用于类型.尽管如此,我还是尝试将'T改为^ T但仍然遇到了同样的错误.
我意识到这个例子滥用了类型系统,所以我并不是说编译器应该能够处理它.但是,为了我自己的兴趣,我想知道错误信息的真正含义!
问题是.NET不支持静态约束.F#解析它们内联函数,这些函数被编译为静态方法,但不能将其编码为标准.NET类型.在您的代码中,您要创建的泛型类型将具有带有.NET无法表示的静态约束的类型参数.
您可以将方法更改为静态,它将正常工作:
type UsesNextable() =
static member inline Next(nextable) =
let v = next nextable
v.ToString()
Run Code Online (Sandbox Code Playgroud)
如果需要,您的类型仍然可以是通用的,但是您应该避免使用静态约束引用类型的类型参数,例如这将起作用:
type UsesNextable<'T>() =
static member inline NextOf(n:'U) = let v = next n in v.ToString()
Run Code Online (Sandbox Code Playgroud)
但不是这个:
type UsesNextable<'T>() =
static member inline NextOf(n:'T) = let v = next n in v.ToString()
Run Code Online (Sandbox Code Playgroud)
解决了第一个问题后,您将看到两个错误消息都消失了,因为第二个错误消息与类型系统无法编码约束的第一个相关,但现在它可以单独解决.^?155882表示类型系统无法推断的静态约束类型变量.
请注意,更改'T到ˆT并不能改变什么,其实都指的是同类型的变量,唯一的事情是,你不得不使用帽子,当你调用该方法.
最后关于你不能写的事实:
let inline next v = ((Next or ^T) : (static member ( ++ ) : Next * ^T -> ^T) Next, v)
Run Code Online (Sandbox Code Playgroud)
这显然是一个错误,因为F#可以推断它,但是你不能写出不一致的错误.我已经在不久前报道了它们,他们告诉我他们将来会修复它.
通过强制F#推断类型而不是直接编写它,有很多方法可以解决这个限制,这里是一个单线解决方案:
let inline next v = ((^Next or ^T) : (static member ( ++ ) : ^Next * ^T -> ^T) Next, v)
Run Code Online (Sandbox Code Playgroud)
它将创建一个警告,但您可以通过nowarn指令或通过向包含该类型实例的函数添加参数来禁用它Next.