Мы можем попытаться понять, как может выглядеть такой язык.Очевидно, что нечто подобное (использование C-подобного синтаксиса в демонстрационных целях) не может быть разрешено или, по крайней мере, не имеет очевидного значения:
int x_plus_(int y) {
return x + y; // requires that x have type int
}
int three_plus_(int y) {
double x = 3.0;
return x_plus_(y); // calls x_plus_ when x has type double
}
Итак, как этого избежать?
Я могу подумать о нескольких подходах не по порядку:
Комментаторы выше упоминают, что Фортран до '77 имел такое поведение.Это сработало, потому что имя переменной определяло ее тип;такая функция, как x_plus_
выше, будет недопустимой, потому что x
никогда не может иметь целочисленный тип.(И точно так же как three_plus_
, в этом отношении, потому что y
будет иметь такое же ограничение.) Целочисленные переменные должны иметь имена, начинающиеся с i
, j
, k
, l
, m
или n
.
Perl использует синтаксис для различения нескольких широких категорий переменных, а именно: скаляры, массивы (регулярные массивы) и хэши (ассоциативные массивы).Переменные, принадлежащие к разным категориям, могут иметь одно и то же имя, поскольку синтаксис определяет, какая из них имеется в виду.Например, выражение foo $foo, $foo[0], $foo{'foo'}
включает функцию foo
, скаляр $foo
, массив @foo
($foo[0]
является первым элементом @foo
) и хеш %foo
($foo{'foo'}
значение в %foo
, соответствующее ключу 'foo'
).Теперь, чтобы быть совершенно ясным, Perl не статически типизирован, потому что существует много различных скалярных типов, и эти типы не отличаются синтаксически.(В частности: все ссылки являются скалярами, даже ссылками на функции, массивы или хэши. Поэтому, если вы используете синтаксис для разыменования ссылки на массив, Perl должен проверить во время выполнения, чтобы увидеть, действительно ли значение является ссылкой на массив.) Но этот же подход можно использовать для добросовестной системы типов, особенно если система типов была очень простой.При таком подходе метод x_plus_
будет использовать x
типа int
и будет полностью игнорировать x
, объявленный three_plus_
.(Вместо этого он будет использовать x
типа int
, который должен был быть предоставлен из любой области, называемой three_plus_
.) Для этого могут потребоваться некоторые аннотации типов, не включенные выше, или может использоваться некоторая форма вывода типа.
Подпись функции может указывать на нелокальные переменные, которые она использует, и их ожидаемые типы.В приведенном выше примере x_plus_
будет иметь подпись "принимает один аргумент типа int
; использует вызывающую область x
типа int
; возвращает значение типа int
".Затем, так же, как функция, которая вызывает x_plus_
, должна будет передавать аргумент типа int
, она также должна предоставить переменную с именем x
типа int
- либо объявив ее сама, либонаследуя эту часть сигнатуры типа (поскольку вызов x_plus_
эквивалентен использованию x
типа int
) и распространяя это требование до его вызывающих абонентов.При таком подходе приведенная выше функция three_plus_
будет недопустимой, поскольку она будет нарушать сигнатуру вызываемого ею метода x_plus_
, так же, как если бы она пыталась передать double
в качестве аргумента.
Вышеуказанное может иметь просто «неопределенное поведение»;компилятор не должен был бы явно обнаруживать и отклонять его, но спецификация не будет налагать какие-либо особые требования на то, как он должен это обрабатывать.Программисты должны были бы гарантировать, что они никогда не вызовут функцию с неправильно набранными нелокальными переменными.
Ваш профессор, вероятно, думал о # 1, так как до '77Фортран был настоящим языком реального мира с этим свойством.Но о других подходах интересно подумать.: -)