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

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

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

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 или С#? Это в чем-то принципиально испорчено? Я понимаю, что вы можете подклассифицировать MyClass и реализовать интерфейс или использовать шаблон дизайна Adapter, чтобы выполнить одно и то же, но эти подходы просто выглядят как ненужный шаблонный код.

Ответ 1

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

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

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

Ответ 2

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

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

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

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

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

Ответ 3

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

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

Я действительно не скомпилировал этот код, но я считаю его работоспособным и довольно тривиальным С++-ization исходного предлагаемого (но нерабочего) кода.

Ответ 4

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

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

Ответ 5

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

Ответ 6

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

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

Ответ 7

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

Выдержка:

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

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

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

Ответ 9

TypeScript!

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

enter image description here

Ответ 10

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

Ответ 12

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

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

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

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

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

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

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

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

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

Ответ 13

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
    ^

Ответ 15

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

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

Вы пишете:

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

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

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