В кругах дизайна языка раньше были продолжительные дебаты, законченные, должны ли языки использовать структурную эквивалентность или эквивалентность имен. Языки как Алгол или ML или Modula-3 использовали структурную эквивалентность, в то время как... хорошо, в значительной степени большинство языков программирования использует названную эквивалентность (включая Modula-2).
Каковы типичные аргументы в пользу структурной эквивалентности? Каковы типичные аргументы против него? Каковы типичные аргументы в пользу эквивалентности имен? Каковы типичные аргументы против него?
Я думаю, что преимущество систем структурных типов состоит в том, что они побуждают вас создавать детализированные интерфейсы, ориентированные на то, что нужно пользователю интерфейса, а не на то, что предоставляет разработчик.
В системе именительного типа вам нужна общая зависимость от интерфейса. В системе структурного типа это требование устранено: вы можете построить слабосвязанную систему без необходимости создавать общую библиотеку, в которую вы помещаете все интерфейсы. Каждый клиент может независимо объявить интерфейс, который он ожидает от соавтора.
Недостатком систем структурных типов является то, что они сопоставляют классы с интерфейсами, которые могут не реализовывать правильный контракт. Например, если у вас есть этот интерфейс:
public interface IBananaProvider
{
/// Returns a banana, never null.
Banana GetBanana();
}
, то следующий класс будет неявно рассматриваться для реализации IBananaProvider
в системе структурных типов. Однако класс нарушает условие публикации, согласно которому возвращаемый банан никогда не является нулевым:
public class SomeBananaProvider
{
// returns a banana or null if we're all out
public Banana GetBanana()
{
if (bananas.Count > 0)
{
return bananas.RemoveLast();
}
else
{
return null;
}
}
}
Это можно было бы исправить, если бы контракт был каким-то образом определен формально и считался частью структуры типа. Я думаю, что дела идут в этом направлении, например System.Diagnostics.Contracts в .NET 4.0.
Одним из хороших аргументов в пользу строгой эквивалентности имен (например, доступной в Ada) является то, что это позволяет компилятору отклонять код, который случайно смешивает разные единицы, например, сантиметры и дюймы или градусы Цельсия и Фаренгейта.
В языке со строгой эквивалентностью имен у вас может быть два типа
type celsius based on float;
type fahrenheit based on float;
var c : celsius; var f : fahrenheit;
c := f; /* compile time error: incompatible types */
В то время как в языке с утраченной эквивалентностью имен и в языке со структурной эквивалентностью ...
type celsius is float;
type fahrenheit is float;
c := f; /* no error and no warning here */
... вы получите просчет, который приведет к непредсказуемому поведению, в зависимости от типа приложения и системы это может привести к серьезным финансовым потерям или даже смерти. Такую логическую ошибку также очень сложно отследить без строгой эквивалентности имен.