Есть ли какие-нибудь статичные языки типа утка? - PullRequest
17 голосов
/ 14 ноября 2008

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

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

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 и реализовать интерфейс или использовать шаблон проектирования адаптера для достижения той же цели, но эти подходы кажутся ненужным стандартным кодом.

Ответы [ 15 ]

18 голосов
/ 11 ноября 2009

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

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

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

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

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

13 голосов
/ 24 ноября 2008

Как насчет использования шаблонов в 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 ++ - оригинализация предложенного (но неработающего) кода.

10 голосов
/ 14 ноября 2008

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

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

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

10 голосов
/ 14 ноября 2008

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

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

9 голосов
/ 23 ноября 2008

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
4 голосов
/ 27 ноября 2014

машинопись!

Хорошо, хорошо ... Так что это расширенный набор javascript и, возможно, он не является "языком", но этот тип статической утки очень важен для TypeScript.

enter image description here

4 голосов
/ 24 октября 2009

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

Наиболее популярные языки в этой семье, которые приходят на ум, включают в себя: 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 не являются строго необходимыми. Объективный Caml может вывести все в вашем примере без каких-либо объявлений типов.

2 голосов
/ 09 мая 2014

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

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

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

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

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

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

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

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

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

2 голосов
/ 29 декабря 2008

Структурные типы в Scala делают что-то подобное.

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

2 голосов
/ 14 ноября 2008

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

Выдержка:

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

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

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

...