F#神秘约束错误消息

Aka*_*ash 0 f#

为了好玩,我一直在玩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)

但是我收到以下错误:

  1. 签名和实现不兼容,因为类/签名中的type参数与成员/实现中的类型参数具有不同的编译时要求
  2. 签名和实现是不兼容的,因为类型参数'T'的声明需要表单的约束(Next或^ T):(静态成员(++):Next*^ T - > ^?155882)

我对第二个错误试图说的更感兴趣.什么神秘的^?155882是什么意思?我猜它可能与无法解决类型有关.此外,假设它试图推断约束为:

(Next or  ^T) : (static member ( ++ ) : Next *  ^T ->  ^T)
Run Code Online (Sandbox Code Playgroud)

甚至可以使用when 'T:...?指定"或"条件?我无法弄清楚有效的语法.

最后,^ T建议使用静态解析的类型参数,但文档说它们不能用于类型.尽管如此,我还是尝试将'T改为^ T但仍然遇到了同样的错误.

我意识到这个例子滥用了类型系统,所以我并不是说编译器应该能够处理它.但是,为了我自己的兴趣,我想知道错误信息的真正含义!

Gus*_*Gus 5

问题是.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.