Сообщений 6 Оценка 885 [+1/-0] Оценить |
После выхода официальной версии Nemerle 1.0 программисты, ранее его не использовавшие, начали частенько задавать вопрос: «Почему в Nemerle есть те или иные синтаксические различия с C#». Вопрос резонный, так как в основном Nemerle близок по синтаксису и семантике к C#. Наверно, если бы Nemerle был полным надмножеством C#, то C#-программистам было бы проще осваивать Nemerle.
В этой статье я попытаюсь описать расхождения и причины, вызвавшие их появление.
C# |
Nemerle (изменяемые переменные) |
Nemerle (неизменяемые переменные) |
|||
---|---|---|---|---|---|
|
|
|
Изменение состояния – главный источник проблем в программах. Использование изменяемых переменных провоцирует появление ошибок, связанных со случайным изменением значений переменных.
В Nemerle считается, что предпочтительнее использовать неизменяемые переменные (def-связывания).
Изменяемые переменные (и поля) объявляются с использованием ключевого слова mutable. Это ключевое слово (ввиду своей длины) хорошо заметно в коде и тем самым акцентирует внимание на изменяемых значениях внутри алгоритма.
Использование ключевых слов (mutable и def) делает синтаксис объявления переменных и параметров более регулярным (последовательным).
C# |
Nemerle |
||
---|---|---|---|
|
|
Типы в Nemerle всегда указываются сзади и через двоеточие. На первый взгляд это похоже на объявление в Pascal, но это не совсем так. В отличие от Pascal, в Nemerle нельзя перечислять переменные внутри объявления:
x, y, x : int// недопустимо! |
Данный синтаксис унаследован у ML.
Выбор в пользу такого синтаксиса сделан из-за его регулярности и логичности. Например, когда вы описываете метод с параметрами типов, то в C# получается, что параметр типов имеет область видимости, отличную от того, что используется в других частях языка. Получается, что вы сначала используете параметр типа (для указания возвращаемого значения метода), а потом уже объявляете его.
В Nemerle в этом плане все логично. Сначала параметр типа объявляется, а потом уже используется.
Кроме того, такой подход позволил сделать синтаксис указания типов одинаковым для всех случаев использования. В Nemerle параметры, поля и локальные переменные всех видов описываются одинаково. Это же решение позволяет легко опускать описание типов и пользоваться мощными возможностями вывода типов Nemerle.
Лексема «:» отлично выделяет описание типов в коде. Его сложнее перепутать, например, с созданием экземпляра объекта и т.п.
Кстати, обратите внимание на то, что параметры функций в Nemerle по умолчанию являются неизменяемыми. Если вы хотите изменять значение параметра внутри тела метода, параметр нужно также объявить с использованием ключевого слова mutable:
Method1(count : int) : void { count++; // ошибка времени компиляции! } Method2(mutable count : int) : void { count++; // OK! } |
Следует также отметить, что модификатор mutable никак не отражается на описании или использовании самого метода. Изменяя значение изменяемого параметра, вы не сможете случайно изменить значение, переданное в качестве этого параметра. Для этого, как и в C#, необходимо воспользоваться модификатором ref. Кстати, обратите внимание на то, что модификаторы ref и out указываются у типа, а не перед параметром. Это логично, так как они относятся к описанию типа параметра.
C# |
Nemerle |
||
---|---|---|---|
|
|
В Nemerle есть два оператора приведения типов:
Введение двух операторов приведения типов позволяет компилятору более детально контролировать код и предотвращать появление некоторых ошибок.
В Nemerle отсутствует оператор «as» языка C#. Точнее, «as» в Nemerle имеет другое значение и может использоваться только в сопоставлении с образцом. Вместо оператора «as» для проверки типа используется сопоставление с образцом и паттерн «is»:
C# |
Nemerle |
||
---|---|---|---|
|
|
Паттерн «is» похоже по смыслу на аналогичный оператор C# (который также присутствует в Nemerle).
Оператор «as» часто неверно используется C#-программистами. Его использование без последующей проверки может привести к появлению трудных в обнаружении ошибок. Многим нравится, как выглядит использование этого оператора и он частенько применяется (особенно не опытными программистами) как замена для оператора приведения типов. Какое-то время такой код может вести себя нормально (не вызвать проблем), но в последствии, в результате рефакторинга тип может измениться и оператор «as» начнет возвращать «null». «null» может быть сохраняет в поле или переменной, что приведет к появлению исключения. Причем исключение это возникнет не в месте реальной ошибки, а совсем в другом месте. Это усложнит ее обнаружение и исправление.
Использование сопоставления с образцом и паттерна «is» гарантирует, что таких ошибок не может появиться.
Кроме того, сопоставление с образцом (т.е. оператор match) позволяет указать несколько образцов «is» подряд (с разными типами), что намного проще читать и поддерживать, нежели цепочку вложенных объявлений переменных и операторов if, как это происходит в C#-коде.
В C# if является так называемым statement-ом (корректного русского перевода, по-видимому, не существует). При этом if можно применять как с else, так и без него. Возможность применять в C# if как c else, так и без него приводит к неоднозначности. Парсер C# решает ее по принципу "else относится к ближайшему if". В Nemerle if является выражением (expression) и может применяться внутри других выражений. Это делает недопустимым наличие неоднозначности. Для устранения неоднозначности в Nemerle было принято решение заменить «if без else» на отдельный макрос when:
C# |
Nemerle |
||
---|---|---|---|
|
|
Кроме того в Nemerle можно использовать void-литерал () для явной декларации отсутствия вычисления:
mutable result2 = 0; if (condition) result = 42; else (); // указываем, что вычисление не придвидится! |
СОВЕТ Кстати, void-литерал можно использовать в целях отладки. В приведенном выше примере можно поставить точку останова на void-литерал. |
Объявление массивов и их литералов в Nemerle сильно отличается от такового в C#. Этому способствуют следующие факторы:
1. В Nemerle, кроме встроенной поддержки массивов (как в C#), также встроена поддержка списков. Список – list[T] – это не изменяемый однонаправленный связанный список.
2. Nemerle поддерживает значительно более мощный алгоритм вывода типов, что позволяет в большинстве случаев не задавать типы явно. Это связано и с параметрами типов (в Nemerle их не обязательно указывать для конструкторов).
3. В Nemerle объявление литералов делается похожим на вызов конструктора. Это делает язык интуитивно понятнее.
Все это привело к тому, что синтаксис литералов массивов очень похож на синтаксис объявления их типов.
C# |
Nemerle |
||
---|---|---|---|
|
|
Для сравнения ниже приведен пример работы со списками:
def ary = [1]; // явное указание типа переменнойdef ary : list[int] = [1]; // список списков (вложенный список)def ary : list[list[int]] = [[42]]; def ary = [[42], [33, 1]]; |
Nemerle поощряет использование функционального стиля программирования внутри кода методов. Для этого стиля характерно создание иерархий объектов, отражающих предметную область. Чтобы сделать синтаксис создания объектов более компактным, в Nemerle было решено отказаться от оператора «new». Конструктор в Nemerle рассматривается как функция, принимающая ноль или более параметров и возвращающая объект некоторого типа. Другими словами, как в C#, но без «new»:
C# |
Nemerle |
||
---|---|---|---|
|
|
Такое решение также позволяет использовать конструктор там, где требуется функция (или делегат).
Параметры типов в Nemerle описываются в квадратных скобках, а не в треугольных (как принято в C++, C# и Java). Это сделано, чтобы устранить неоднозначность грамматики языка, вызванную конфликтом между описанием типов с параметрами типов и операторами сравнения «>» / «<». Конфликт в грамматике C# разрешается за счет нетривиальных эвристик, что усложняет создание расширяемого парсера для языка (а Nemerle принципиально задумывался как расширяемый язык).
C# |
Nemerle |
||
---|---|---|---|
|
|
Определение ко/контрвариантности в параметрах типов появилось в Nemerle значительно раньше нежели в C# (Nemerle поддерживает ко/контрвариантность со второго .NET Framework). Посему было принято взять синтаксис из языков, уже поддерживающих ко/контрвариантность. Выбор был остановлен на нотации «+» / «-», так как она отражает ограничение, накладываемое на тип. «+T» означает, что тип должен быть T или его наследником, а «-T» означает, что тип должен быть T или его предком.
В Nemerle операция присвоения и операции инкремента / декремента имеют тип void. Это не позволяет использовать их в C-стиле – по несколько операций присвоения или инкремента / декремента в одном выражении. Это сделано потому, что подобное использование чревато ошибками и зачастую усложняет чтение кода.
C# |
Nemerle |
||
---|---|---|---|
|
|
На первый взгляд кажется, что код Nemerle от этого становится длиннее, но подобные операции довольно редки в реальном коде. Кроме того выигрыш от применения функционального программирования и макросов настолько велик, что подобное увеличение на его фоне просто незаметно.
Цель этой статьи – не показать все отличия C# и Nemerle, а объяснить те изменения, которые наиболее часто вызывают вопросы у тех, кто только начинает изучать Nemerle и при этом имеет немалый опыт программирования на C# (или похожих на него языках – C++, Java). Если вас интересует более полный список различий, то их можно найти здесь.
Сообщений 6 Оценка 885 [+1/-0] Оценить |