Существуют ли какие-либо статические языки с утиным языком?
Могу ли я указать интерфейсы при объявлении участника?
Подумав об этом вопросе какое-то время, мне пришло в голову, что язык с заданной статикой может работать. Почему не могут быть предварительно определенные классы привязаны к интерфейсу во время компиляции? Пример:
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 имеет специальный тип, называемый "утка". Срок вдохновлено рубиновым программированием языковая утка ( "Если это гуляет, как утка и шарлатки, как утка, это должна быть утка" ).
Ответ 8
Структурные типы в Scala делают что-то вроде этого.
См. Статически проверено "Утиная печать" в Scala
Ответ 9
TypeScript!
Хорошо, хорошо... Таким образом, это надмножество javascript и, возможно, не является "языком", но этот тип статического утиного набора жизненно важен в TypeScript.
![enter image description here]()
Ответ 10
Предварительная версия для Visual Basic 9 поддерживала статическую утиную печать с использованием динамических интерфейсов, но они сокращали функцию *, чтобы корабль вовремя.
Ответ 11
D (http://dlang.org) является статически скомпилированным языком и обеспечивает утиную печать через wrap() и unwrap() (http://dlang.org/phobos-prerelease/std_typecons.html#.unwrap).
Ответ 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
^
Ответ 14
Звучит как миксины или черты:
http://en.wikipedia.org/wiki/Mixin
http://www.iam.unibe.ch/~scg/Archive/Papers/Scha03aTraits.pdf
Ответ 15
В последней версии моего языка программирования Heron он поддерживает что-то подобное с помощью оператора принуждения структурного подтипирования, называемого as
. Поэтому вместо:
MyClass obj = new MyClass();
CallMyMethod(obj);
Вы пишете:
MyClass obj = new MyClass();
CallMyMethod(obj as IMyInterface);
Как и в вашем примере, в этом случае MyClass
не нужно явно реализовывать IMyInterface
, но если бы это произошло, это может происходить неявно, а оператор as
может быть опущен.
Я написал немного больше о методе, который я называю явным структурным подтипом в этой статье.