Работаете на F # и не можете понять разницу в следующих двух элементах:

type A<'k, 'v when 'v : comparison and 'k : comparison> = 
    { Keys: Map<'v, 'k> } with 
        member this.length = Map.count this.Keys
module A =
    let empty = { Keys = Map.empty }

type B<'k, 'v when 'v : comparison and 'k : comparison>(keys: Map<'v, 'k>) =
    member __.length = Map.count keys
module B =
    let empty = new B<'k, 'v>(Map.empty)

Если я посмотрю на предполагаемые типы A.empty и B.empty, я получу следующее:

val empty : A<'a,'b> (requires comparison and comparison)
val empty : B<System.IComparable,System.IComparable>

Почему они разные? И k, и v назначаются карте F # и используются одинаково. Единственное отличие - это ключевое слово «новое» на B, но почему это изменит типы, выведенные для карты?

f#
1
mrkmg 6 Окт 2018 в 07:45

1 ответ

Лучший ответ

В случае B.empty у вас есть значение с привязкой к let, где вы вводите параметры типа 'k и 'v с правой стороны, не обращаясь к ним с левой стороны. Механизм вывода типа тогда не может автоматически обобщить empty и вместо этого присваивает ему определенный тип.

Если вы попытаетесь использовать его в разных экземплярах, вы увидите, что второй не работает:

let t1 : B<int, string> = B.empty 
let t2 : B<string, int> = B.empty // <- getting a type mismatch here.

Это происходит из-за свойства механизма вывода типов, называемого ограничением значений. Это сообщение в блоге есть хорошее подробное описание этого, включая тот самый случай, который вы сейчас рассматриваете, реализация empty для общего типа контейнера.

Вкратце - B.empty здесь нельзя обобщать, потому что выражение с правой стороны не дает значения, которое легко идентифицировать как неизменное, и поэтому компилятор проявляет осторожность и решает не делать этого. обобщить тип empty.

С другой стороны, A.empty в правой части имеет выражение, создающее экземпляр неизменяемой записи, что является одним из редких сценариев, отвечающих требованиям автоматического обобщения.

Некоторые способы избежать этого:

  • Создание функции empty:

    let empty () = new B<'k, 'v>(Map.empty)
    
  • Вводя в него явные аргументы типа:

    let empty<'k, 'v when 'v : comparison and 'k : comparison> = new B<'k, 'v>(Map.empty)
    
1
scrwtp 6 Окт 2018 в 17:12