Язык без приведения типов - PullRequest
1 голос
/ 21 августа 2011

Мой вопрос в значительной степени совпадает с тем, что написано в заголовке: возможно ли иметь язык программирования, который не допускает явного приведения типов?

Чтобы пояснить, что я имею в виду, предположим, что мы работаем на некотором C # -подобном языке с родительским Base классом и дочерним Derived классом. Понятно, что такой код будет безопасным:

Base a = new Derived();

Поскольку восходящая иерархия наследования безопасна, но

Dervied b = (Base)a;

небезопасно, так как спускаться небезопасно.

Но, независимо от безопасности, такие downcast действительны на многих языках (например, Java или C #) - код скомпилируется и просто завершится ошибкой во время выполнения, если типы не верны. Технически, код все еще безопасен, но с помощью проверок во время выполнения, а не проверок во время компиляции (кстати, я не фанат проверок во время выполнения).

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

Итак, мне интересно, существуют ли в настоящее время какие-либо ОО-языки, которые обеспечивают такую ​​строгую безопасность типов во время компиляции, что приведение к ним является устаревшим? Т.е. они вообще не разрешают небезопасные операции преобразования? Или есть причина, по которой это не сработает?

Спасибо за любой вклад.

Редактировать

Если я смогу уточнить на примере, вот основная причина, по которой я так ненавижу унылых.

Допустим, у меня есть следующее (свободно основанное на коллекциях C #):

public interface IEnumerable<T>
{
     IEnumerator<T> GetEnumerator();

     IEnumerable<T> Filter( Func<T, bool> );
}

public class List<T> : IEnumerable<T>
{
    // All of list's implementation here
}

Теперь предположим, что кто-то решил написать такой код:

List<int> list = new List<int>( new int[]{1, 2, 3, 4, 5, 6} );
// Let's filter out the odd numbers
List<int> result = (List<int>)list.Filter( x => x % 2 != 0 );

Обратите внимание, что приведение необходимо в этой последней строке. Но так ли это? Не в общем. Конечно, имеет смысл, что реализация List<T>.Filter вернет другой List<T>, но это не гарантировано (это может быть любой подтип IEnumerable<T>). Даже если это выполняется в один момент времени, более поздняя версия может изменить это, показывая, насколько хрупок код.

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

Ответы [ 3 ]

2 голосов
/ 07 ноября 2012

Понижающие рейтинги могут быть постепенно устранены путем улучшения возможностей системы типов.

Одно из предложенных решений для приведенного вами примера состоит в том, чтобы добавить возможность объявить возвращаемый тип метода как "такой же, как этот».Это позволяет подклассу возвращать подкласс без необходимости приведения.Таким образом, вы получите что-то вроде этого:

public interface IEnumerable<T>
{
 IEnumerator<T> GetEnumerator();

 This<T> Filter( Func<T, bool> );
}

public class List<T> : IEnumerable<T>
{
    // All of list's implementation here
}

Теперь приведение не требуется:

List<int> list = new List<int>( new int[]{1, 2, 3, 4, 5, 6} );
// Compiler "knows" that Filter returns the same type as its receiver
List<int> result = list.Filter( x => x % 2 != 0 );

В других случаях даункастинга также предлагались решения путем улучшения системы типов, но эти улучшения нееще не было сделано для C #, Java или C ++.

1 голос
/ 21 августа 2011

Ну, конечно, возможно иметь языки программирования, которые вообще не имеют подтипов, и, естественно, там нет необходимости в даункатах. Большинство неOO-языков попадают в этот класс.

Даже в ОО-языке на основе классов, таком как Java, большинство откликов можно формально заменить, просто предоставив базовому классу метод

Foo meAsFoo() {
   return null;
}

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

Конечно, это возможно, только если вы можете изменить родительский класс. Я подозреваю, что вы могли бы считать это плюсом, но учитывая, как часто один может изменить родительский класс и использовать обходной путь, я не уверен, сколько это будет стоить с точки зрения поощрения "хорошего" дизайна (для некоторого более или менее произвольного значения «хорошо»).

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

Shape x = .... ;
switch( x ) {
  case Rectangle r:
    return 5*r.diagonal();
  case Circle c:
    return c.radius();
  case Point:
    return 0 ;
  default:
    throw new RuntimeException("This can't happen, and I, "+
            "the programmer, take full responsibility");
}

Однако на практике может возникнуть проблема, заключающаяся в том, что без предположения о замкнутом мире (которое современные языки программирования, похоже, неохотно делают), многие из этих переключателей потребовали бы default: случаев, которые программист знает никогда не может произойти, что может привести к десенсибилизации программиста к результирующим броскам.

0 голосов
/ 10 сентября 2011

Существует много языков с типизацией утки и / или неявным преобразованием типов. Perl наверняка приходит на ум; тонкости того, как подтипы скалярного типа преобразуются внутри, являются частым источником критики, но также получают похвалу, потому что, когда они работают так, как вы ожидаете, они способствуют ощущению языка DWIM.

Традиционный Лисп - еще один хороший пример - все, что у вас есть, это атомы и списки, и nil, которые оба одновременно. Иначе они никогда не встретятся ...

(Вы, кажется, пришли из вселенной, где языки программирования обязательно объектно-ориентированы, строго типизированы и скомпилированы.)

...