Сбой наследования интерфейса F # из-за unit

#inheritance #f#

#f# #тип модуля

Вопрос:

Кто-нибудь знает, почему это не удается скомпилировать?

 type MyInterface<'input, 'output> = 
    abstract member MyFun: 'input -> 'output

type MyClass() = 
    interface MyInterface<string, unit> with
        member this.MyFun(input: string) = ()
    //fails with error FS0017: The member 'MyFun : string -> unit' does not have the correct type to override the corresponding abstract method.
type MyUnit = MyUnit
type MyClass2() = 
    //success
    interface MyInterface<string, MyUnit> with
        member this.MyFun(input: string) = MyUnit
  

Ответ №1:

Это выглядит как неприятный угловой случай в языке F #, но я не уверен, квалифицируется ли это как ограничение по дизайну или ошибка в компиляторе. Если это связано с ограничением конструкции, то в сообщении об ошибке должно быть указано это (потому что в настоящее время это не имеет особого смысла).

В любом случае, проблема в том, что компилятор F # не генерирует код, который на самом деле содержит unit тип в IL. Он заменяет его на void (при использовании в качестве возвращаемого типа) или на пустой список аргументов (при использовании в качестве аргумента метода или функции).

Это означает, что в MyClass типе компилятор решает скомпилировать MyFun элемент как метод, который принимает string и возвращает void (но вы не можете использовать void в качестве аргумента универсального типа, поэтому это просто не работает). В принципе, компилятор мог бы использовать фактический unit тип в этом случае (потому что это единственный способ заставить его работать), но это, вероятно, создало бы другие несоответствия в другом месте.

Я думаю, что ваш трюк с созданием MyUnit — это прекрасный способ решить проблему. Даже базовая библиотека F # использует что-то подобное MyUnit в некоторых местах реализации (в асинхронных рабочих процессах), чтобы справиться с некоторыми ограничениями unit (и способом ее компиляции).

Комментарии:

1. Спасибо, Томас. Я не видел этой проблемы где-либо еще (например, в обычных функциях с let;)

2. @Stefan: Если вы используете unit тип в качестве аргумента (для функции или типа), то это, как правило, нормально. Эта ошибка / ограничение, вероятно, появляется только при реализации абстрактных элементов (что является немного сложной областью для компилятора F #, поскольку наследование в .NET удивительно сложное)

3. Интересно, что это будет работать на C #, и я могу использовать функцию на F #. Вероятно, следует сообщить об ошибке.