F #: универсальный член использовался в неоднородном экземпляре до этой программной точки - PullRequest
3 голосов
/ 17 января 2012

У меня есть два рекурсивно определенных класса, которые могут быть скомпилированы в одном порядке, но не могут быть скомпилированы в другом порядке. Упрощенная версия кода выглядит следующим образом:

type Loader() =
    member this.Load path =
        let ref = Ref()
        ref.Wait()
        ref

and Ref<'T>(data: obj) =
    member this.Foo = 1
    member this.Wait () = ()
    member this.Value = data :?> 'T

Ошибка в объявлении this.Wait (); полный текст выглядит следующим образом:

asset.fs(11,21): error FS1198: The generic member 'Wait' has been used at a non-uniform
instantiation prior to this program point. Consider reordering the members so this
member occurs first. Alternatively, specify the full type of the member explicitly,
including argument types, return type and any additional generic parameters
and constraints.

Обратите внимание, что в упрощенной версии рекурсия на самом деле не нужна, однако реальный код требует, чтобы две декларации были рекурсивными.

Я могу решить эту проблему, указав тип для Wait (), как указано в сообщении об ошибке (т.е. элемент this.Wait (): unit = ()) - однако я хотел бы понять, почему мне нужно это сделать.

Ответы [ 2 ]

3 голосов
/ 17 января 2012

Чтобы ответить на ваш вопрос, я цитирую этот удивительный ответ от @Brian:

Очень часто «исправить» - это просто добавить одну полную подпись типа (включая тип возврата) к некоторому ключевому методу, который 'объявлен поздно', но 'вызван рано »(вводит прямую ссылку среди набора членов).

В вашем случае взаимно рекурсивных классов, когда вызывается ref.Wait(), вывод типа F # просматривает объявление типа Ref и его первый член для разрешения сигнатуры типа Wait. Поскольку никакой информации на данный момент нет, она ошибочно выводит подпись Wait как unit -> 'a. Поиск первого члена класса - это мое наблюдение, основанное на следующем примере:

type Loader() =    
    member this.Load path =
        let ref = Ref()
        ref.Wait()        
        ref
    member this.Load2 path =
        let ref = Ref()
        ref.Wait2()        
        ref

and Ref<'T>(data: obj) =        
    member this.Wait () = () // resolve correctly
    member this.Wait2 () = () // fail to resolve     
    member this.Foo = 1
    member this.Value = data :?> 'T

Кстати, есть много способов обойти это ограничение:

  1. Предоставление аннотации типа для методов Declare-late и Call-Early .
  2. Объявите Wait как первый член в Ref (он не масштабируется, как показано в примере выше).
  3. Объявите Ref как первый тип во взаимно рекурсивной паре. Поскольку Ref требуется больше информации от Loader, чем наоборот, лучше сначала разрешить Ref, чтобы предоставить информацию для разрешения Loader.
2 голосов
/ 17 января 2012

Я не знаю достаточно о проверке типов, чтобы объяснить поведение (и я не думаю, что спецификация F # объясняет это, потому что она только кратко упоминает "рекурсивные типы" на стр. 120).

В любом случае, в спецификации сказано, что компилятор проверяет определения рекурсивного типа как одну группу, и поэтому использование метода, подобного Wait, используется для (частично) вывода его типа.Когда вы указываете тип явно, тогда явное определение переопределяет выведенное.

В вашем примере проблема появляется только тогда, когда Ref<'T> является универсальным типом.Когда вы вызываете Wait, компилятор, вероятно, правильно делает вывод, что тип метода unit -> unit, но я думаю, что есть некоторая гибкость в параметре this метода (для некоторых 'T это Ref<'T>).

Исходя из исходного кода компилятора , ошибка сообщается только в случае сбоя добавления какого-либо ограничения, связанного с переменной this (строка 8914), при обработке определения метода Wait,Итак, я предполагаю, что некоторое ограничение, связанное со значением ref внутри Load, позже конфликтует с некоторым ограничением, сгенерированным из Wait (когда компилятор проверяет метод и не имеет полных аннотаций, чтобы знать тип дляуверен).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...