3

有人可以解释一下为什么在 F# 中类型推断似乎在类方法和函数之间的工作方式不同(或我不理解的其他方面?)。

想象一下(简化):

type Node<'T> = Node2 of 'T * 'T
type Digit<'T> = One of 'T | Two of 'T * 'T
type Tree<'T> =
    | Empty
    | Single of 'T
    | Deep of prefix : Digit<'T> * deeper : Tree<Node<'T>>
    with
    static member Add (value : 'T) (tree : Tree<'T>) : Tree<'T> =
        match tree with
        | Empty -> Single value
        | Single a -> Deep (One value, Empty)
        | Deep (One a, deeper) -> Deep (Two (value, a), deeper)
        | Deep (Two (b, a), deeper) -> Deep (One value, deeper |> Tree.Add (Node2 (b, a)))

let rec add (value : 'T) (tree : Tree<'T>) : Tree<'T> =
    match tree with
    | Empty -> Single value
    | Single a -> Deep (One value, Empty)
    | Deep (One a, deeper) -> Deep (Two (value, a), deeper)
    | Deep (Two (b, a), deeper) -> Deep (One value, deeper |> add (Node2 (b, a)))

请注意,静态方法Add和函数add具有相同的实现,并且都以递归方式调用自身。

然而前者编译得很好,但后者报告错误:

Type mismatch. Expecting a
    Tree<Node<'T>> -> Tree<Node<'T>>    
but given a
    Tree<'T> -> Tree<'T>    
The resulting type would be infinite when unifying ''T' and 'Node<'T>'
4

1 回答 1

3

在自由浮动函数add中,泛型类型参数属于函数本身(add<'T>)。

但是,在静态成员函数中,类型参数实际上属于类(Tree<'T>)。

为什么这很重要?因为当您引用函数本身时,编译器假定类型参数不变,除非另有说明。它不会猜测另一个,因为这可能会隐藏大量类型不匹配的错误。

但是,它不会对函数所属的类型做出相同的假设。

如果检查参数,add则假定调用 to 是调用add<'T>,这会导致无限泛型递归并且无法编译。

但是,调用 toTree.Add被推断为调用Tree<Node<'T>>.Add而不是调用Tree<'T>.Add。这是一个完全不同的函数调用。

如果您显式注释类型:

static member Add (value : 'T) (tree : Tree<'T>) : Tree<'T> =
    // ...
    | Deep (Two (b, a), deeper) -> Deep (One value, deeper |> Tree<'T>.Add (Node2 (b, a)))

您将得到与 free 函数完全相同的类型不匹配/无限类型错误。

同样,如果将其设为实例成员并引用同一实例,则会出现错误:

member this.Add (value : 'T) (tree : Tree<'T>) : Tree<'T> =
    // ...
    | Deep (Two (b, a), deeper) -> Deep (One value, deeper |> this.Add (Node2 (b, a)))

反之亦然,你可以通过注解类型参数使自由函数编译,这样编译器就不会假设“它是同一个符号,所以必须引用同一个值”:

let rec add<'T> (value : 'T) (tree : Tree<'T>) : Tree<'T> =
    // ...
    | Deep (Two (b, a), deeper) -> Deep (One value, deeper |> add (Node2 (b, a)))
于 2016-05-20T12:44:51.410 回答