Существуют ли статические языки с утиным типом?

Могу ли я указать интерфейсы при объявлении члена?

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

public interface IMyInterface
{
  public void MyMethod();
}

public class MyClass  //Does not explicitly implement IMyInterface
{
  public void MyMethod()  //But contains a compatible method definition
  {
    Console.WriteLine("Hello, world!");
  }
}

...

public void CallMyMethod(IMyInterface m)
{
  m.MyMethod();
}

...

MyClass obj = new MyClass();
CallMyMethod(obj);     // Automatically recognize that MyClass "fits" 
                       // MyInterface, and force a type-cast.

Знаете ли вы какие-либо языки, поддерживающие такую ​​функцию? Было бы полезно на Java или C #? Есть ли в нем какие-то фундаментальные изъяны? Я понимаю, что вы можете создать подкласс MyClass и реализовать интерфейс или использовать шаблон проектирования адаптера для достижения той же цели, но эти подходы кажутся ненужным шаблонным кодом.


person Cybis    schedule 14.11.2008    source источник


Ответы (15)


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

Теперь вы можете встроить в компилятор больше интеллекта, чтобы он мог выводить типы, вместо того, чтобы программист явно объявлял типы; компилятор может увидеть, что MyClass реализует метод MyMethod(), и обработать этот случай соответствующим образом, без необходимости явно объявлять интерфейсы (как вы предлагаете). Такой компилятор может использовать вывод типа, например Hindley-Milner.

Конечно, некоторые статически типизированные языки, такие как Haskell, уже делают что-то похожее на то, что вы предлагаете; компилятор Haskell может определять типы (в большинстве случаев) без необходимости их явного объявления. Но очевидно, что у Java / C # такой возможности нет.

person mipadi    schedule 14.11.2008
comment
Спасибо. Я только начал изучать Haskell около недели назад, и пока это кажется довольно крутым. Однако это немного требует обучения - никогда раньше не разбирался в функциональных языках. В любом случае, его система вывода типа очень похожа на то, о чем я говорю. - person Cybis; 14.11.2008
comment
В C #, похоже, есть вывод типа. Например, об этом говорится в этой статье. Разработчик . com / net / csharp / article.php / 3601646 / - person John K; 24.10.2009

Совершенно новый ответ на этот вопрос: Go имеет именно эту функцию. Я думаю, что это действительно круто и умно (хотя мне будет интересно посмотреть, как это отразится в реальной жизни), и спасибо за то, чтобы подумать об этом.

Как описано в официальной документации (как часть Tour of Go, с примером кода):

Интерфейсы реализованы неявно

Тип реализует интерфейс, реализуя свои методы. Нет ни явного объявления намерений, ни ключевого слова «реализует».

Неявные интерфейсы отделяют определение интерфейса от его реализации, которая затем может появиться в любом пакете без предварительной договоренности.

person Grumdrig    schedule 11.11.2009
comment
Не могли бы вы дать ссылку на действительную часть документации Go, в которой объясняется, как это работает? А еще лучше привести сюда пример? - person Paŭlo Ebermann; 08.03.2016
comment
Как хотите (но только со ссылкой на пример) - person Grumdrig; 15.03.2016

Как насчет использования шаблонов в C ++?

class IMyInterface  // Inheritance from this is optional
{
public:
  virtual void MyMethod() = 0;
}

class MyClass  // Does not explicitly implement IMyInterface
{
public:
  void MyMethod()  // But contains a compatible method definition
  {
    std::cout << "Hello, world!" "\n";
  }
}

template<typename MyInterface>
void CallMyMethod(MyInterface& m)
{
  m.MyMethod();  // instantiation succeeds iff MyInterface has MyMethod
}

MyClass obj;
CallMyMethod(obj);     // Automatically generate code with MyClass as 
                       // MyInterface

На самом деле я не компилировал этот код, но считаю, что это работоспособная и довольно тривиальная C ++ - изация исходного предложенного (но неработающего) кода.

person John Zwinck    schedule 23.11.2008
comment
Кто-то упомянул шаблоны C ++, но я думаю, что этот ответ был удален. В любом случае, шаблоны - это просто расширенные макросы. Новый CallMyMethod создается для каждого типа, который вы ему передаете, поэтому на самом деле это не определение типа. - person Cybis; 24.11.2008
comment
Кроме того, я не знаю хорошей IDE C ++ с intellisense, которая не запуталась бы в шаблонах (Visual Studio не всегда перечисляет все варианты, а иногда и полностью останавливается). - person Cybis; 24.11.2008
comment
Честно говоря, исходный постер вообще не упоминал о выводе типа per se. Я просто немного изменил его код и показал, что он может работать на C ++ практически как есть. - person John Zwinck; 24.11.2008
comment
Я не могу понять, как люди до сих пор думают, что шаблоны C ++ - это просто расширенные макросы, это полный механизм программирования по Тьюрингу, а не инструмент для замены текста. - person swegi; 22.12.2009
comment
@swegi настоящие (синтаксические) макросы тоже завершены по Тьюрингу. И даже больше. Например, в Clojure вы можете делать практически все во время компиляции. - person Display Name; 26.07.2014
comment
Помимо прочего, шаблоны C ++ даже не являются расширенными макросами. На самом деле это в лучшем случае очень простые макросы. Ни один язык (или метаязык), на котором вы должны использовать свой if, не может всерьез считаться продвинутой средой программирования. - person Leushenko; 14.08.2014
comment
@Leushenko, начиная с C ++ 11, вам не нужно: en.cppreference. com / w / cpp / types / conditional. - person chbaker0; 12.03.2017

Я не вижу в этом смысла. Почему бы не указать явно, что класс реализует интерфейс и что с ним покончено? Реализация интерфейса - это то, что сообщает другим программистам, что этот класс должен вести себя так, как это определяет интерфейс. Простое наличие у метода того же имени и подписи не дает никаких гарантий того, что намерение дизайнера состояло в том, чтобы выполнить аналогичные действия с методом. Может быть, но зачем оставлять это для интерпретации (и неправильного использования)?

Причина, по которой вы можете успешно «уйти» с этим в динамических языках, больше связана с TDD, чем с самим языком. На мой взгляд, если язык предлагает возможность давать такого рода указания другим, кто использует / просматривает код, вы должны его использовать. Это на самом деле улучшает ясность и стоит нескольких дополнительных символов. В случае, если у вас нет доступа для этого, адаптер служит той же цели, явно объявляя, как интерфейс относится к другому классу.

person tvanfosson    schedule 14.11.2008
comment
Я согласен, в этом смысл интерфейса, чтобы описать, что что-то такое. Используйте его везде, где можете, это не шаблонный код, это хорошая практика. - person Mark; 14.11.2008
comment
Почему этот шаблон успешен в динамических языках, но считается плохой практикой в ​​статических языках? Какое отношение к нему имеет TDD, уникальное для динамической типизации? Как упоминал @mipadi, почему языки объектно-ориентированного программирования не могут поддерживать своего рода систему вывода типов в стиле Haskell? - person Cybis; 14.11.2008
comment
Судя по моему ограниченному пониманию, дженерики Java - это просто обходной способ сделать то, что составляет утиный тип. - person James McMahon; 15.01.2009
comment
@ Джеймс: извините, но нет. Дженерики Java - это то, что составляет автоматическое приведение типов к типу Object и обратно. Вы не можете написать код, подобный показанному выше. - person R. Martinho Fernandes; 06.04.2010
comment
Мне неизвестен какой-либо язык или фреймворк, который распознал бы концепцию Коллекции вещей, реализующих IFoo и IBar, unless everything one wanted to hold in the collection also implemented some interface IFooBar`, который наследует IFoo и IBar. Если общего типа не существует, единственное, что может сделать код, - это определить коллекцию как содержащую один тип и при необходимости привести к другому. Со статической точки зрения, утиная типизация позволит определить тип коллекции как {IFoo,IBar}, а ее элементы будут рассматриваться как удовлетворяющие обоим ограничениям. - person supercat; 13.06.2014
comment
что, если код уже скомпилирован, а у вас нет исходного кода, потому что он не ваш? как вы сделаете так, чтобы он унаследовал интерфейс? нельзя ожидать, что другие будут все время принимать правильные решения - person symbiont; 30.07.2016

F # поддерживает статическую утиную типизацию, но с одной уловкой: вы должны использовать ограничения членов. Подробности доступны в этом запись в блоге.

Пример из цитируемого блога:

let inline speak (a: ^a) =
    let x = (^a : (member speak: unit -> string) (a))
    printfn "It said: %s" x
    let y = (^a : (member talk: unit -> string) (a))
    printfn "Then it said %s" y

type duck() =
    member x.speak() = "quack"
    member x.talk() = "quackity quack"
type dog() =
    member x.speak() = "woof"
    member x.talk() = "arrrr"

let x = new duck()
let y = new dog()
speak x
speak y
person Joh    schedule 23.11.2008

Большинство языков в семействе машинного обучения поддерживают структурные типы с логическим выводом и схемами ограниченного типа, что представляет собой забавную терминологию разработчика языков, которая, скорее всего, соответствует тому, что вы подразумеваете под фразой «статическая утиная типизация» в исходный вопрос.

Наиболее популярные языки этого семейства, которые приходят на ум, включают: Haskell, Objective Caml, F # и Scala. Тот, который больше всего соответствует вашему примеру, конечно, будет Objective Caml. Вот перевод вашего примера:

open Printf

class type iMyInterface = object
  method myMethod: unit
end

class myClass = object
  method myMethod = printf "Hello, world!"
end

let callMyMethod: #iMyInterface -> unit = fun m -> m#myMethod

let myClass = new myClass

callMyMethod myClass

Примечание: некоторые использованные вами имена необходимо изменить, чтобы они соответствовали концепции OCaml о семантике регистра идентификаторов, но в остальном это довольно простой перевод.

Также стоит отметить, что ни аннотация типа в функции callMyMethod, ни определение типа класса iMyInterface не являются строго необходимыми. Objective Caml может вывести все, что есть в вашем примере, вообще без каких-либо объявлений типов.

person james woodyatt    schedule 24.10.2009

TypeScript!

Хорошо, хорошо ... Итак, это надмножество javascript и, возможно, не является «языком», но такая статическая утиная типизация жизненно важна в TypeScript.

введите описание изображения здесь

person Alex    schedule 27.11.2014
comment
Я думаю, что Typescript использует структурную типизацию (которая, честно говоря, очень похожа на ducktyping, только строже) - person frankelot; 03.04.2020

Boo определенно является статическим языком с утиным типом: http://boo.codehaus.org/Duck+Typing < / а>

Отрывок:

Boo - это язык со статической типизацией, такой как Java или C #. Это означает, что ваши Boo-приложения будут работать примерно так же быстро, как и те, которые написаны на других статически типизированных языках для .NET или Mono. Но использование статически типизированного языка иногда ограничивает вас жестким и подробным стилем кодирования с иногда необходимыми объявлениями типов (например, «x as int», но это не часто необходимо из-за вывода типа boo) и иногда необходимыми приведениями типов (см. Типы отливок). Поддержка Boo для вывода типов и, в конечном итоге, обобщений здесь помогает, но ...

Иногда уместно отказаться от подстраховки, обеспечиваемой статической типизацией. Может быть, вы просто хотите изучить API, не слишком беспокоясь о сигнатурах методов, или, может быть, вы создаете код, который взаимодействует с внешними компонентами, такими как объекты COM. В любом случае выбор должен быть за вами, а не за мной.

Наряду с обычными типами, такими как object, int, string ... boo имеет специальный тип, называемый «утка». Этот термин вдохновлен функцией набора текста уткой в ​​языке программирования ruby ​​(«Если он ходит, как утка, и крякает, как утка, значит, это утка»).

person torial    schedule 14.11.2008
comment
Статический язык с уткой - это не то же самое, что статический утиный ввод. Как существующий VB, так и грядущий C # 4.0 имеют статическую типизацию и динамическую / позднюю / утиную типизацию. - person Mark Cidade; 14.11.2008
comment
Я не имею в виду сочетание статической и динамической типизации (похоже, вы имеете в виду учебное пособие). Я имею в виду вывод типов - компилятор может сразу увидеть, что MyClass и IMyInterface совместимы, без явного указания взаимосвязи программистом. - person Cybis; 14.11.2008
comment
Виноват. Бу не сделает этого за тебя. - person torial; 16.11.2008

Новые версии C ++ движутся в направлении статической утиной печати. Вы можете когда-нибудь (сегодня?) Написать что-то вроде этого:

auto plus(auto x, auto y){
    return x+y;
}

и он не сможет скомпилировать, если не будет соответствующего вызова функции для x+y.

Что касается вашей критики:

Новый «CallMyMethod» создается для каждого типа, который вы ему передаете, поэтому на самом деле это не вывод типа.

Но это вывод типа IS (можно сказать foo(bar), где foo - шаблонная функция) и имеет тот же эффект, за исключением того, что он более эффективен по времени и занимает больше места в скомпилированном коде.

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

Или вам нужно будет сохранить всю эту информацию о совпадающих интерфейсах и изучить каждый класс, соответствующий интерфейсу, а затем автоматически добавить этот интерфейс.

В любом случае это позволяет неявно и случайно нарушить иерархию классов, что плохо для новой функции, потому что это противоречит привычкам программистов C # / Java. С шаблонами C ++ вы уже знаете, что находитесь на минном поле (и они также добавляют функции («концепции»), чтобы разрешить ограничения на параметры шаблона).

person leewz    schedule 09.05.2014

Структурные типы в Scala делают примерно следующее.

См. Статически проверенный «Утиный ввод» в Scala

person John Nilsson    schedule 28.12.2008

Предварительная версия Visual Basic 9 поддерживала статическую утиную типизацию с использованием динамических интерфейсов, но они отказались от этой функции * для своевременной доставки.

person Mark Cidade    schedule 14.11.2008

D (http://dlang.org) - это статически скомпилированный язык, обеспечивающий утиную типизацию с помощью wrap () и unwrap ( ) (http://dlang.org/phobos-prerelease/std_typecons.html#.unwrap).

person DejanLekic    schedule 15.04.2014

Crystal - это язык со статическим утиным типом. Пример:

def add(x, y)
  x + y
end

add(true, false)

Вызов add вызывает эту ошибку компиляции:

Error in foo.cr:6: instantiating 'add(Bool, Bool)'

add(true, false)
^~~

in foo.cr:2: undefined method '+' for Bool

  x + y
    ^
person emlai    schedule 31.05.2016

Похоже на примеси или черты характера:
http://en.wikipedia.org/wiki/Mixin
http://www.iam.unibe.ch/~scg/Archive/Papers/Scha03aTraits.pdf

person ja.    schedule 28.12.2008

В последней версии моего языка программирования Heron он поддерживает нечто подобное с помощью оператора приведения структурных подтипов под названием as . Так что вместо:

MyClass obj = new MyClass();
CallMyMethod(obj);

Вы бы написали:

MyClass obj = new MyClass();
CallMyMethod(obj as IMyInterface);

Как и в вашем примере, в этом случае MyClass не обязательно явно реализовывать IMyInterface, но если бы это было так, приведение могло произойти неявно, и оператор as можно было бы опустить.

Я написал немного больше о методе, который я называю явным структурным подтипом, в эту статью.

person cdiggins    schedule 22.12.2009