Каким должен быть язык программирования? Анализ и критика Описание языка Компилятор
Отечественные разработки Cтатьи на компьютерные темы Компьютерный юмор Новости и прочее

«Двухмерный» синтаксис Python

Судя по отступам, Маяковский писал на Питоне

Язык Python удивителен тем, что синтаксический сахар в нём минимален. Самый частый символ этого сахара — это пробел.

 # поиск простых чисел
 for n in range(2, 10):
     for x in range(2, n):
         if n % x == 0:
             print n, 'равно', x, '*', n//x
             break
         else:
             # циклу не удалось найти множитель
             print n, '- простое число'

            Все гениальное — просто. Синтаксис Python — лучшая иллюстрация к этому. Всё, что правее и ниже ключевых слов for, if, else, является «подчинёнными» операторами. Полностью отпадает необходимость в «begin», «end», «{» и «}» — и так всё понятно. Хотя некоторые программисты, имевшие опыт программирования на Python, жалуются на трудность и непривычность чтения такого кода. «Ужасно! Для чтения программы надо прикладывать линейку к монитору», — и такое доводилось слышать. Хотя проблему мог бы решить редактор IDE, где в качестве фона могли бы нарисовать несколько таких линеек, по числу уровней программы. Тогда всё станет прозрачнее.

            Однако можно заметить, что синтаксис Python хотя и минимален, но не минималистичен. Как минимум две вещи можно смело считать излишеством.

            Первое: выражения for операторы цикла :, if условное выражение :, else : заканчиваются двоеточием, без которого можно обойтись.

            Второе. Двусмысленность оператора «else», которая берёт своё начало из Алгол-60:
 if условие 1
      выражения 1
 if условие 2
      выражения 2
 else
      выражения 3
К какому «if» относится «else»? За правило принято относить его к ближайшему (последнему) «if». В Modula-2 и многих других языках эта двусмысленность устраняется оператором «endif», в Python из-за отступов этот «endif» отсутствует.

            Третье. В разделе «Условные операторы» просматривается возможность замены оператора «elseif» на «if»:
 (if первое условие
       выражения, котрые выполняются при соблюдении первого условия
  if второе условие /* это «elseif» в языках типа Ada */
       выражения, которые выполняются при НЕсоблюдении первого условия
          и соблюдении второго условия
  else
       выражения, которые выполняются при НЕсоблюдении первого 
       и второго условий
 )
            Перечисленное в этих замечаниях не создают особых проблем. Тем не менее, надо признать, что синтаксис Python не идеален, хотя он достаточно удачен. Но попробуем предложить другой стиль для языка программирования.

            «Двухмерный» синтаксис — довольно неожидан на фоне традиционных языков. Можно шляпу перед его изобретателем, вот только кто он? Это кто-то из троицы создателей языка ABC: Leo Geurts, Lambert Meertens, Steven Pemberton. Этот синтаксис нам знаком по Python, но туда он попал из языка ABC.

Что ещё почитать на эту тему

Опубликовано: 2012.09.25, последняя правка: 2016.02.05    16:30

ОценитеОценки посетителей
   █████████████████████████ 6 (60%)
   █████ 1 (10%)
   █████ 1 (10%)
   █████████ 2 (20%)

Отзывы

     2017/10/12 14:12, alextretyak          # 

В качестве предисловия. Совсем недавно во время проводимых мной практических занятий (http://proglangs.ru) по языкам программирования, у одного парня не работала программа из-за того, что он поставил `;` сразу после `while (z != 0)`. «Двухмерный» синтаксис, помимо избавления от мелкого синтаксического сахара/мусора, даёт нечто большее. Что ещё так и не увидели/не поняли многие разработчики.

Проиллюстрирую это на примере документации к языку Nemerle (https://github.com/rsdn/nemerle/wiki/The-basics-(tutorial)#Rewriting_Line_Counter_without_the_loop]:

In Nemerle the if expression always need to have the else clause. It's done this way to avoid stupid bugs with dangling-else:

// C#, misleading indentation hides real code meaning
if (foo)
   if (bar)
     m1 ();
else
   m2 ();

If you do not want the `else` clause, use `when` expression, as seen in the example.

Скорее всего, разработчики Nemerle просто скопировали это из Haskell или Standard ML (https://semitwist.com/articles/article/view/nemerle-s-when-bad-idea-easily-solved [The mandatory 'else' is Nemerle's functional language inheritance showing]), не став даже разбираться в причинах указанного "stupid bug with dangling-else". А причина вовсе не в неправильной логике if, допускающем как отсутствие ветви else, так и её наличие, и даже не в забытых фигурных скобочках, а в... расхождении в восприятии данного кода человеком-программистом и компилятором. Человек воспринимает границы блоков визуально, посредством отступов, а компилятор — посредством малоприметных для человека символов, причём компилятор (языков C/C++, C#, Java, Nemerle и др.) объединяет все пробельные символы и разделители, и, таким образом, напрочь игнорирует отступы. Вот в этом и заключается корень проблемы — компилятор и человек видят такой код по-разному.

Поэтому меня удивляют люди, которые жалуются на трудность и непривычность чтения такого кода, а также возражения, что при изменении лишь отступа, меняется логика работы кода. Так в этом и состоит весь смысл отступо-зависимой семантики! Ведь такое изменение отступа будет хорошо заметно человеку (особенно привыкшему к такой семантике).

А для желающих временно добавить if, включающий в себя большой блок кода, который не хочется сдвигать лишний раз, я считаю, что вполне можно оставить поддержку фигурных скобок:
if foo \временно добавленное условие
{
m1() \нет отступа относительно if foo
...
if bar
    m2()
    ...
...
}
... заканчиваются двоеточием, без которого можно обойтись. Согласен.

По сути, это двоеточие не имеет смысла, так как есть операторы (if, switch, for/while, etc.), которые требуют всегда нового scope, и одного признака ‘‘новая строка с отступом’’ вполне достаточно.

Кстати, весьма интересна история появления этого двоеточия (http://python-history.blogspot.ru/2011/07/karin-dewar-indentation-and-colon.html Karin Dewar, "Indentation and the Colon"):

In 1978, in a design session in a mansion in Jabłonna (Poland), Robert Dewar, Peter King, Jack Schwartz and Lambert were comparing various alternative proposed syntaxes for B... Since they couldn't agree, Robert Dewar's wife was called from her room and asked for her opinion... But after the first version was explained to her, she remarked: "You mean, in the line where it says: 'FOR i ... ', that it has to be done for the lines that follow; not just for that line?!" And here the scientists realized that the misunderstanding would have been avoided if there had been a colon at the end of that line.

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

Единственное полезное применение двоеточия — однострочные if'ы (if условие: выражения) и циклы, но выйти из положения можно с помощью обозначения scope в фигурных скобках (запись if условие {выражения}), как в Rust, либо взяв условие в скобки (if (условие) выражение).

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

К слову, насчёт предложенного автором сайта «симметричного скобочного» стиля: если язык программирования будет поддерживать if—then—else as an expression (то есть, когда оператор «if» является не утверждением/statement, а выражением/expression, как в Scala, Ruby, Rust, Nemerle[Everything is an expression] и Nim, а также не будет, как Python, требовать ставить символ «:» после «if условие», тогда можно будет писать так:
result = (if условие
              выражения
          else
              выражения)
При этом «result =», очевидно, можно опустить, и тогда вопрос скобочек вокруг if становится лишь вопросом стиля (так как специальная поддержка скобочек вокруг if-else на уровне компилятора не потребуется, если if-else — это обычное выражение).

Кстати, я готовлю обширный интерактивный опрос/голосование по разным элементам стиля и синтаксису нового языка программирования (нужно ли двоеточие, точка с запятой, фигурные скобки, скобки вокруг условия if, количество пробелов на отступ, выбор обозначения операторов/операций [возведение в степень, целочисленное деление, логические и поразрядные операции, конкатенация строк, массивов и т.д.] и т.п.), и планирую туда включить вариант/вопрос, предлагающий ваш «симметричный скобочный» стиль. Вы не против, если я укажу ссылку на эту страницу: «Стиль языка программирования»? А также можете подумать и предложить какие-то свои вопросы, я ещё успею включить их в опрос (вообще, голосование начнётся не раньше, чем через 3 месяца, так что времени на подумать ещё довольно много). Вопрос, нужен ли else if и какой именно/конкретно (elseif, elsif, elif) также можно поставить на этом голосовании.

Хотя проблему мог бы решить редактор IDE, где в качестве фона могли бы нарисовать несколько таких линеек, по числу уровней программы.

Помимо вертикальных линеек я предлагаю ещё такие горизонтальные пунктирные линии: https://habrastorage.org/webt/59/dd/f4/59ddf464724da447828936.png

     2017/10/26 22:30, Автор сайта          # 

Можете включить в голосование (если Вас не затруднит, дайте на него ссылку) ещё один пункт: «if», имеющий смысл как «else if»:
(if условие1
выражения 1
if условие 2 // здесь «if» играет ту же роль, что «else if»
выражения 2
else
выражения 3
)
В приведённом примере второй «if» не имеет впередиидущей скобки, что говорит о том, что он играет роль «else if». Это одна из гипотез грядущего языка программирования. Но скорее всего, это будет отвергнуто: оно провоцирует путаницу.

Что касается разделителей выражений, то выбрал для себя в качестве таковых «перевод строки» и «;».

     2018/03/07 23:56, alextretyak          # 

если Вас не затруднит, дайте на него ссылку

Голосование откладывается в связи с [крайне] низкой заинтересованности к новому языку программирования, ссылка на статью:
https://habrahabr.ru/post/350694/.

     2018/03/10 20:05, Comdiv          # 

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

     2018/04/30 09:49, geniepro          # 

Синтаксис Питона и правда напоминает синатксис ABC, однако за два десятка лет до ABC двумерный синтаксис был в языке ISWIM, а ещё за два десятка лет до него — в языке Plankalkül.

По поводу важности двумерного синтаксиса со значимыми отступами хорошую статью как-то написал Chris Okasaki в своей статье "In praise of mandatory indentation for novice programmers". Там он упоминал, что значимые отступы вместо всех этих {} begin-end дают значительное преимущество в облегчении изучения программирования. По сравнению с этим не особо важно, статическая типизация или динамическая, функциональный язык или ООП, строгие вычисления или ленивые...

     2018/04/30 14:03, Автор сайта          # 

Из Википедии:

В Python Гвидо ван Россум позаимствовал некоторые наработки для языка ABC (Гвидо участвовал в разработке этого языка, ориентированного на обучение программированию).

Заимствование произошло не напрямую.

Очень даже верю, что отступы — самый сильный инструмент. Спасибо за ссылку.

     2022/12/04 15:58, Автор сайта          # 

Нашёлся автор идеи отступов! «Истоки Python»:

Еще более поразительным является использование отступов. Хотя в программах, написанных на Алгол-60 или его потомках, таких как Паскаль, было принято использовать отступы в качестве типографской особенности оформления для уточнения группировки команд, это был совершенно необязательный выбор внешнего вида, сделанный исключительно для удобства читателя. В статье П. Дж. Плаугера [вот он, автор!] под названием "Сигнал и шум в языках программирования" мы нашли радикальную (тогда) идею: "Пусть компилятор читает тот же сигнал, что и мы, люди, и пусть отступы управляют группировкой" — предложение, которому мы последовали с энтузиазмом. Отступы, указывающие на принадлежность набора команд друг другу, впоследствии стали обязательными в программах B0 [нулевая версия языка ABC, предшественника Питона], и этот выбор в дизайне языка сохранялся на протяжении всех итераций.

А вот ранее писали здесь в комментариях:

двумерный синтаксис был в языке ISWIM, а ещё за два десятка лет до него — в языке Plankalkül.

Эти языки существовали «на бумаге». Программы на них не набирались в текстовом редакторе, отступы не делались пробелами. На бумаге отступы были просто свободным пространством слева. Так что вопрос первенства расплывчат.

Ещё следует учитывать, что отступы всегда играли роль в языках ассемблера. Если в первой позиции отсутствовал пробел, то это была метка. Команды набирались со второй и до какой-то фиксированной позиции. А после определённой позиции начинались комментарии.
МЕТКА  КОМАНДА ОП1, ОП2          КОММЕНТАРИИ
Думается, что идеи просто витали в воздухе. И не исключено, что пришли в голову многим почти одновременно.

Возникает вопрос: «А почему в своём языке не воспользоваться отступами в стиле Python?» Да всё просто. В Python отступы управляют началом и концом блока. А можно сделать всё наоборот: начало и конец блока управляют (формируют нужное количество пробелов) отступами. Это делать, к примеру, в редакторе.

     2022/12/04 16:10, Gudleifr          # 

Ещё следует учитывать, что отступы всегда играли роль в языках ассемблера

Любите Вы, все-таки, изобретать велосипеды. В языках ассемблеров и прочих FORTRAN-ах не "отступы", а разбиение команды на поля. Для удобства перфорирования.

     2022/12/04 17:57, Автор сайта          # 

Как бы это не называлось, по факту приходилось делать отступы.

     2024/02/11 23:20, Неслучайный читатель          # 

Встретил пример, который намекает на преимущества синтаксиса Питона:
// https://habr.com/ru/articles/723400/comments/#comment_25347974
public class Permuter {
private static void permute(int n, char[] a) {
if (n == 0) {
System.out.println(String.valueOf(a)) ;}
else {
for (int i = 0; i <= n; i++) {
permute(n-1, a) ;
swap(a, n % 2 == 0 ? i : 0, n) ;}}}
private static void swap(char[] a, int i, int j) {
char saved = a[i] ;
a[i] = a[j] ;
a[j] = saved ;}}
Мусор сгребли в сторону, хотя и не весь. Все условные выражения заключены в скобки, которые тоже лишние. А если рассмотреть циклы с проверкой в конце?
do              {
операторы ;}
while (условие)
Очевидно, что без «do» тут вполне можно обойтись, компилятор в состоянии распознать (тут же рассматривают идеи?), что это цикл с проверкой внизу:
                {
операторы ;}
while (условие)
Запишем это в стиле Питона:
    операторы
while условие
А что, если перед таким циклом есть условный оператор?
if условие
операторы, которые относятся к условному оператору
. . .
// а между ними могут находиться операторы,
// которые непонятно куда отнести:
// то ли к условию выше, то ли к циклу ниже
// одним словом, казнить нельзя помиловать
. . .
операторы, которые относятся к циклу
while условие
Хорош синтаксис Питона, но идеал недостижим.

     2024/02/12 22:13, Автор сайта          # 

В предлагаемом синтаксисе проверка в конце цикла вполне возможна.
(if условие
операторы)
( операторы
while условие)
«Лишними» в таком синтаксисе являются открывающие скобки. А в случае цикла с проверкой внизу — закрывающая скобка.

     2024/02/15 12:55, Деньги на WWWетер          # 

Для полноты картины неплохо рассмотреть примерно такой код:
public class Permuter                                begin
private static void permute(int n, char[] a) begin
if n == 0 then begin
System.out.println(String.valueOf(a)) ;end
else begin
for (int i = 0; i <= n; i++) begin
permute(n-1, a) ;
swap(a, n % 2 == 0 ? i : 0, n) ;end end end
private static void swap(char[] a, int i, int j) begin
char saved = a[i] ;
a[i] = a[j] ;
a[j] = saved ;end end
Чтоб синтаксис в стиле Си не выглядел таким отстоем.

     2024/02/28 22:12, Бурановский дедушка          # 

На RSDN нашёл статью «Синтаксический оверхед», которой почти 20 лет. В ней сравнивается синтаксис Си и «необходимый и достаточный» синтаксис некого гипотетического языка. Цифрами пытаются показать, как сильно «загрязнён» синтаксис Си лишними лексемами по сравнению с языком с минимальным количеством лексем.

С одной стороны удивляет, почему объектом критики стал Си, а не более многословный Паскаль. Удивляет игнорирование синтаксиса Питона — вот уж где синтаксис минимален или почти минимален. Ну и замечено некоторое жульничество, которое, наоборот не удивляет, потому что в священных религиозных войнах все средства хороши.

Приводят, к примеру, такие примеры. Си:
if(a)
{
x();
}
else
{
y();
}

Необходимый и достаточный синтаксис:

IF a THEN x ELSE y END

синтаксический оверхед = 17/7 = 2.43
перерасход строчек кода = 8/1 = 8.00

Очевидная подтасовка под желаемый результат!
Во-первых, Си-шный пример тоже можно записать в одну строку:
if(a) {x();} else {y();}
Во-вторых, на Си давно принято писать по-другому:
if(a) {
x();
} else {
y();
}
А пример с «необходимым и достаточным» синтаксисом надо развернуть, как положено при программировании ступеньками:
IF a THEN
x
ELSE
y
END
В обоих случаях у нас 5 строк! Перерасхода строк вообще нет! Кроме того, фигурные скобки в данном конкретном примере на Си не нужны, их можно опустить:
if(a)
x();
else
y();
Мало того, что строк становится меньше, так и лексем тоже. Ещё автор статьи ошибается в том, что его синтаксис «необходим», то есть ещё меньший синтаксис просто невозможен. Отнюдь! Синтаксис в духе Питона:
if a
x
else
y
Или в одну строку:
if a; x; else y
Ну и многие разработчики скажут, что скобки куда легче, чем THEN, END. Мало того, что ключевые слова THEN, END длинны, так ещё и набираются на другом регистре! Можно, конечно, жалеть компилятор и не совать ему лишние лексемы. Но лучше пожалеть разработчика и не заставлять набирать его лишний текст. И тут стоит обратить внимание, что в примерах не подсчитано количество набранных символов. Это тоже имеет значение.

В конце статьи задаётся вопрос про Си:

Спрашивается, и как долго этот синтаксис ещё будет существовать?

На Си и на языках, которые синтаксически к нему близки, написано больше половины мирового ПО. Так что этот синтаксис навсегда. Но! Вышел на первые роли Питон. Теперь он будет донором синтаксиса для новых будущих языков. Но никак не языки с THEN и END.

     2024/02/28 23:29, Автор сайта          # 

Можно, конечно, жалеть компилятор и не совать ему лишние лексемы. Но лучше пожалеть разработчика и не заставлять набирать его лишний текст.

Уже высказывался по поводу синтаксиса:

оценка сложности языка зависит от того, кто её делает. У вычислительной машиной одна оценка, а у человека она совсем другая.

Компилятор читает символы слева направо, а человек видит текст или его фрагменты целиком, одновременно. У них разные методы чтения и анализа. Соответственно и их оценка разнится. Количество правил языка влияет на сложность компилятора. Человек зачастую не видит разницы в сложности или его мнение прямо противоположно мнению вычислительной машины.

     2024/03/07 14:16, Неслучайный читатель          # 

Автор статьи «Синтаксический оверхед», который педалирует тему «загрязнения» Си лишними лексемами, ничего не говорит про «лексический оверхед» Паскаля и Оберона. «{» короче «BEGIN», «}» короче «END», а «THEN» вообще не нужен. А Оберон ещё «загрязнён» CAPS LOCK.

Добавить свой отзыв

Написать автору можно на электронную почту
mail(аt)compiler.su

Авторизация

Регистрация

Выслать пароль

Карта сайта


Содержание

Каким должен быть язык программирования?

Анализ и критика

●  Устарел ли текст как форма представления программы

●  Русский язык и программирование

●  Многоязыковое программирование

Синтаксис языков программирования

Синтаксический сахар

●  Некоторые «вкусности» Алгол-68

●  «Двухмерный» синтаксис Python

●  Почему языки с синтаксисом Си популярнее языков с синтаксисом Паскаля?

●  Должна ли программа быть удобочитаемой?

●  Стиль языка программирования

●  Тексто-графическое представление программы

●●  Разделители

●●  Строки программы

●●  Слева направо или справа налево?

●  Комментарии

●●  Длинные комментарии

●●  Короткие комментарии

●●  Комментарии автоматической генерации документации

●●  Нерабочий код

●●  Помеченные комментарии

●  Нужны ли беззнаковые целые?

●  Шестнадцатиричные и двоичные константы

●  Условные операторы

●  Переключатель

●  Циклы

●●  Продолжение цикла и выход из него

●  Некошерный «goto»

●  Изменение приоритетов операций

●  Операции присвоения и проверки на равенство. Возможно ли одинаковое обозначение?

●  Так ли нужны операции «&&», «||» и «^^»?

●  Постфиксные инкремент и декремент

●  Почему в PHP для конкатенации строк используется «.»?

●  Указатели и ссылки в C++

●●  О неправомерном доступе к памяти через указатели

●  Обработка ошибок

●  Функциональное программирование

●●  Нечистые действия в чистых функциях

●●  О чистоте и нечистоте функций и языков

●●  Макросы — это чистые функции, исполняемые во время компиляции

●●  Хаскелл, детище британских учёных

●●  Измеряем замедление при вызове функций высших порядков

●●  C vs Haskell: сравнение скорости на простом примере

●●  Уникальность имён функций: за и против

●●  Каррирование: для чего и как

●●  О тестах, доказывающих отсутствие ошибок

●  Надёжные программы из ненадёжных компонентов

●●  О многократном резервировании функций

●  Использование памяти

●  Почему динамическое распределение памяти — это плохо

●  Как обеспечить возврат функциями объектов переменной длины?

●●  Типы переменного размера (dynamically sized types, DST) в языке Rust

●●  Массивы переменной длины в C/C++

●●  Размещение объектов в стеке, традиционный подход

●●  Размещение объектов переменной длины с использованием множества стеков

●●  Размещение объектов переменной длины с использованием двух стеков

●●  Реализация двухстековой модели размещения данных

●●  Двухстековая модель: тесты на скорость

●●  Изменение длины объекта в стеке во время исполнения

●●  Размещение объектов переменной длины с использованием одного стека

●  Можно ли забыть о «куче», если объекты переменной длины хранить в стеке

●  Безопасность и размещение объектов переменной длины в стеке

●  Массивы, структуры, типы, классы переменной длины

●  О хранении данных в стеке, вместо заключения

●  Реализация параметрического полиморфизма

Описание языка

Компилятор

Отечественные разработки

Cтатьи на компьютерные темы

Компьютерный юмор

Новости и прочее




Последние отзывы

2024/04/23 15:57 ••• Ivan
Энтузиасты-разработчики компиляторов и их проекты

2024/04/23 00:00 ••• alextretyak
Признаки устаревшего языка

2024/04/21 00:00 ••• alextretyak
Постфиксные инкремент и декремент

2024/04/20 21:28 ••• Бурановский дедушка
Русский язык и программирование

2024/04/07 15:33 ••• MihalNik
Все языки эквивалентны. Но некоторые из них эквивалентнее других

2024/04/01 23:39 ••• Бурановский дедушка
Новости и прочее

2024/04/01 23:32 ••• Бурановский дедушка
Русской операционной системой должна стать ReactOS

2024/03/22 20:41 ••• void
Раскрутка компилятора

2024/03/20 19:54 ••• kt
О многократном резервировании функций

2024/03/20 13:13 ••• Неслучайный читатель
Надёжные программы из ненадёжных компонентов

2024/03/07 14:16 ••• Неслучайный читатель
«Двухмерный» синтаксис Python

2024/03/03 16:49 ••• Автор сайта
О неправомерном доступе к памяти через указатели

2024/02/28 18:59 ••• Вежливый Лис
Про лебедей, раков и щук