LINUX.ORG.RU

Нужен совет по придумыванию синтаксиса

 ,


0

4

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

ПЕРВЫЙ:

// циклы с предусловием:

while condition do
    statements
end:while

until condition do
    statements
end:until

// циклы с постусловием:

do
    statements
repeat while condition;

do
    statements
repeat until condition;

// вечный цикл:

do
    statements
repeat forever;

// простой блок кода:

block
    statements
end:block

ВТОРОЙ:


// циклы с предусловием:

while condition loop
    statements
end:while

until condition loop
    statements
end:until

// циклы с постусловием:

loop
    statements
repeat while condition;

loop
    statements
repeat until condition;

// вечный цикл:

loop
    statements
repeat forever;

// простой блок кода:

do
    statements
end:do
★★

Последнее исправление: wandrien (всего исправлений: 1)
Ответ на: комментарий от CrX

Смысл следующий.

В Си есть такая достаточно частая идиома:

do {
   бла бла;
   if (что-то) break;
   бла бла;
   if (что-то) break;
   бла бла;
} while (0);

Здесь «цикл» как раз используется не по назначению цикла, а как скоуп для выхода по break из него.

В моём проекте этот юзкейс будет легализован в конструкцию языка, позволяющую напрямую выходить из блока, не только из цикла.

Синтаксис управляющих конструкций строится в соответствии с идеей, что он должен наглядно говорить о том, что происходит. Поэтому все виды разных управляющих конструкций выглядят по-разному, а не просто состоят из () и {}.

Разумеется, такую вещь, как «цикл, который не является циклом», я тоже не мог обойти вниманием в этом ракурсе. Поэтому такой не_цикл и не выглядит как цикл, а вводится отдельным ключевым словом.

wandrien ★★
() автор топика
Последнее исправление: wandrien (всего исправлений: 1)
Ответ на: комментарий от Dr64h

Честно сказать, я помню идею Ады с именованными циклами. Вероятно, я читал об этом, так как с ЯП знакомился, но затем не копировал её напрямую сознательно, а творчески переизобрёл.

wandrien ★★
() автор топика
Ответ на: комментарий от wandrien

То есть, я правильно угадал — идея таки сделать всё именно максимально по-разному, каждое со своими ключевыми словами, и 100500 способов сделать одно и то же. Идея понятна. Не представляю, зачем может понадобиться такой язык, кроме как издеваться над студентами, чтобы надо было как можно больше зубрить, но идея ясна.

Тогда можно ещё then в when добавить, чтобы можно было без блока делать несколько команд!

when condition:
    Print("Oops") then Stop(process);

Ну и не все союзы и наречия английского языка использованы. Есть, куда развиваться. Добавь before и after:

after my_cool_loop do
    Print("The loop just ended")
end:after

my_cool_loop:
while condition do

    when что-то там:
        exit my_cool_loop;
end:while

before my_cool_loop do
    Print("There will be a loop now")
end:before
CrX ★★★★★
()
Ответ на: комментарий от CrX

То есть, я правильно угадал — идея таки сделать всё именно максимально по-разному, каждое со своими ключевыми словами, и 100500 способов сделать одно и то же.

Нет. Идея не в этом. Идея в том, чтобы в максимальном количестве случаев случайные опечатки не выглядели как корректный код.

Тогда можно ещё then в when добавить, чтобы можно было без блока делать несколько команд!

Суть when именно в том, что он относится только к одному statement и не имеет ветки else.

Это родной брат следующих конструкций:

exit when что-то;

в Ада

и

a = 0 if b < 0;

в Ruby.

Однако постфиксная запись условия нарушает принцип максимальной явности кода. Читая текст сверху вниз, её легко не увидеть. Поэтому она переделана в префиксную.

wandrien ★★
() автор топика
Последнее исправление: wandrien (всего исправлений: 2)
Ответ на: комментарий от CrX

100500 способов сделать одно и то же

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

К сожалению, Ruby при отдельных хороших находках в синтаксисе, тоже не избавлен от болезни «натолкать всего побольше, чтобы было красиво». В итоге получается вот это:

irb(main):002:0> puts "Hello!" while false
=> nil
irb(main):003:0> begin puts "Hello!" ; end while false
Hello!
=> nil
irb(main):004:0> 

Сколько из тех, кто пишет на Ruby, знают про этот подводный камень?

wandrien ★★
() автор топика
Ответ на: комментарий от wandrien

Нет. Идея не в этом. Идея в том, чтобы в максимальном количестве случаев случайные опечатки не выглядели как корректный код.

ОК, понятно. Но как в этом помогают 100500 способов написать одно и то же? Зачем, например, аж 4 способа написать обычный цикл с условием. Я понимаю, зачем бесконечный отдельно добавлен — это соответствует задаче. Но зачем нужны и префиксные while и until и постфиксные они же? Для реализации этой идеи как раз будет эффективнее оставить хотя бы только два из четырёх. Один стандартный префиксный while, а другой — until, и наверное постфиксный, потому что именно в таком случае никак не получится написать одно вместо другого, чтобы оно было корректно, даже если два раза ошибиться.

CrX ★★★★★
()
Ответ на: комментарий от wandrien

100500 способов сделать одно и то же

Ты говоришь про нечто противоположное проекту.

Ну… Я говорю про то, что я вижу в коде, а не в заявлениях. Я вижу 4 способа объявить цикл с условием (плюс один для бесконечного, но с ним понятно), я вижу два if’а, один из которых для одного выражения, другой для нескольких, и это надо зубрить. Я вижу несколько способов выйти из цикла/блока: по имени или по объявлению block, и затем exit block. И так далее. Это и есть 100500 способов написать одно и то же.

CrX ★★★★★
()
Последнее исправление: CrX (всего исправлений: 1)
Ответ на: комментарий от wandrien

Здесь «цикл» как раз используется не по назначению цикла

В смысле? Цикл тут никуда не делся. Выход из цикла возможен не в одной точке, так и что с того? Я б ещё понял если бы ты вообще выкинул все варианты цикла, оставил только бесконечный loop, а выход всегда пусть лепят через if/break. Но ты вместо этого хочешь навалить ещё больше сахара.

В моём проекте этот юзкейс будет легализован в конструкцию языка, позволяющую напрямую выходить из блока, не только из цикла.

ЛИСП уже изобрели.

no-such-file ★★★★★
()
Ответ на: комментарий от CrX

Но зачем нужны и префиксные while и until и постфиксные они же? Для реализации этой идеи как раз будет эффективнее оставить хотя бы только два из четырёх. Один стандартный префиксный while, а другой — until, и наверное постфиксный, потому что именно в таком случае никак не получится написать одно вместо другого, чтобы оно было корректно, даже если два раза ошибиться.

Это хороший поинт. Сейчас у меня как раз в реализации присутствуют while do end и repeat until, в духе Оберона-2.

Однако выбирая, по какому принципу оставлять 2 из 4 возможных, я бы оставил не такие два, а пред- и пост- циклы оба с прямыми условиями. Как в Си.

Потому что держать в уме инверсию until создаёт лишнюю когнитивную нагрузку.

Поэтому я думаю, что духу проекта больше соответствовал бы выбор while c do s end и do s loop while c.

Добавить еще и until как синоним для while not (expr) — это как раз получается такой отход от идеи проекта и реверанс в сторону Ruby и Basic. Не уверен, оставлю ли я это в финальном дизайне.

wandrien ★★
() автор топика
Ответ на: комментарий от CrX

Я вижу несколько способов выйти из цикла/блока: по имени или по объявлению block, и затем exit block. И так далее. Это и есть 100500 способов написать одно и то же.

Это же один способ — exit. Далее ты указываешь, к какому именно скоупу он относится, то есть к концу какой синтаксической единицы мы выходим.

Можно было бы драконовски потребовать всегда специфицировать, к чему именно относится exit, но это слишком жестко.

По этой же причине после end через двоеточие опционально специфицируется, что именно этот end закрывает. Это помогает на этапе компиляции выявить случайные ошибки из-за опечаток, а также создаёт самодокументируемую структуру кода.

Что лучше читается и помогает отследить логику кода?

Это:

        }
    }
}

или это:

       end:switch
    end:while
end:function

Невесть, сколько раз при рефакторинге мне приходилось писать вот так, чтобы разобраться в коде:

        } /* end switch */
    } /* end while */
}

Вот только если коммент ошибочный или утратил актуальность, компилятору Си плевать. А в нашем случае компилятор укажет на это.

А можно использовать метки для документации кода:

event_loop:
while !fd.eof() and !stop do

// двумя страницами ниже:

end:event_loop

Опять-таки, в отличие от возможного комментария /* end of event loop */, здесь компилятор удостоверится, что цикл действительно закрыт с указанием на правильную метку.

wandrien ★★
() автор топика
Последнее исправление: wandrien (всего исправлений: 1)
Ответ на: комментарий от wandrien

По этой же причине после end через двоеточие опционально специфицируется, что именно этот end закрывает. Это помогает на этапе компиляции выявить случайные ошибки из-за опечаток, а также создаёт самодокументируемую структуру кода.

Что лучше читается и помогает отследить логику кода?

Не-не, этот момент как раз таки вполне понятен. Я же и выше сказал, что это по крайней мере лучше, чем в Lua.

А можно использовать метки для документации кода:

Да, и это тоже понятно.

Вопрос остаётся открытым: зачем нужен block вложенный в if, если ровно та же задача решается меткой?

CrX ★★★★★
()
Ответ на: комментарий от alysnix

Все там понятно, если почитать почитать определение синтаксиса. А если не почитать, то естественно не понятно.

Любые циклы в недоязычках можно выразить через loop.

for:

(loop for i from 0 below 10 do
      (print i))

while

(loop while (condition)
      (print "loop"))

do…while

(loop do
      (print i)
      while (condition))

foreach

(loop for x in '(1 2 3 4 5) do
      (print x))

foreach для хэш-таблиц

(loop with ht = (make-hash-table ...)
      for k being the hash-key of ht using (hash-value v)
      do (format t "~s ~s~%" k v))

итд.

И естественно есть формы циклов, которые не представляются встроенными конструкциями во всяких недоязычках, типа collect, который собирает значения в список

(loop for i from 1 to 10
      if (= 0 (mod i 2))
        collect i into evens
      else
        collect i into odds
      finally (print evens)
              (print odds))

А в библиотеке iterate, синтаксические формы циклов еще и расширяемые, так что их можно определять для своих коллекций и вообще для чего угодно. Такого точно во всяких недоязычках нет.

lovesan ★★★
()
Ответ на: комментарий от wandrien

Я про способ, показанный выше.

Зачем при его наличие нужно

if condition then
    block
       ....
       exit block
       ...
    end:block
end:if

Если уже есть:

my_cool_condition:
if condition then
    ....
    exit my_cool_condition
    ....
end:if
CrX ★★★★★
()
Последнее исправление: CrX (всего исправлений: 1)
Ответ на: комментарий от CrX
  1. Это был конкретный кусок кода из проекта, а не пример, «как делать нужно всегда».

  2. Я пока не планирую добавлять поддержку exit к операторам ветвления. Не вижу в этом смысла.

Блок кода — это некоторый логически обособленный кусок кода, решающий локальную мини-задачу. Он еще не дорос до того, чтобы сделать с ним «извлечение метода» и оформить в отдельную функцию, но уже на полпути к этому, раз ему нужны отдельные пути выхода в конец.

Добавление этой фичи в ветвления только запутает ситуацию.

Суть ветвления не в том, чтобы «задавать логически обособленный объем работы».

wandrien ★★
() автор топика
Последнее исправление: wandrien (всего исправлений: 1)

Ты луа изобрёл? Если заменить end:bla на просто end получится обычный lua код, который вполне сам по себе норм.

end:do

Не имеет смысла так как идея явно идентифицировать конец тела (цикла,блока,условия) сломается уже на втором уровне вложенности

     end:do
   end:do
end:do 

Достаточно просто end который будет вынуждать писаку писать так, дабы было понятно от какого while/do оно лезет.

Условие
while condition do
    ...
end

Постусловие
repeat
  ...
until condition

Это прямой аналог сишных while(){}, do{}while(). Тут изобретать ничего не нужно, а более сложные вещи типа for/foreach это уже частности.

Так что странный вопрос «что больше нравится и почему». Тут скорее лучше тебя прочесть почему ты их сделал такими какие они есть и почему и зачем. Затем попросить ответить критикой на твои мысли и из реализацию в виде синтаксиса из разряда «ты что-то упустил». На деле, каждый символ должен иметь смысл и последствия, использование : как разделителя для доп идентификатора рожает либо невозможность его использования в синтаксисе более нигде, или вводит многозначное его значение что на сложных участках кода может вызвать попную боль когда конец блока кода с таким идентификатором будет через пробел граничить с вызовом медота или чего ещё. Синтаксис языка это как домино, дёрнул в одном месте посыпалась стройная логика в другом.

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

anonymous
()

end:while - это лишнее. Как и end. Чем скобки не устраивают? Избыточно. Не взлетит. Сейчас первый язык питон у зумеров, а ты им паскальщину подсовываешь, совершенно игнорируя эффект утенка для паскакальщиков как причину популярности всяких раби

anonymous
()

В plpgsql красивое, считаю )

Цикл это всегда LOOP...END LOOP;
FOR ли, WHILE ли, FOREACH ли - дело десятое, факультативное и описательное приставкой к циклу. Тут даже ошибиться-то негде, всё очевидно.

и CASE WHEN ... THEN... WHEN ... THEN... ELSE.. END у него прекрасное )

Toxo2 ★★★★
()
Ответ на: комментарий от Toxo2

PL/SQL использует синтаксис Ады почти один в один. Они использовали готовый синтаксис.

Цикл это всегда LOOP...END LOOP; FORли,WHILEли,FOREACH` ли - дело десятое, факультативное и описательное приставкой к циклу. Тут даже ошибиться-то негде, всё очевидно.

Это работает только при условии, что в языке нет конструкции для цикла с пост-условием.

Если записывать цикл с пост-условием не как отдельную синтаксическую конструкцию, а как:

LOOP
   ...
   EXIT WHEN что-то;
END LOOP;

то да, проблемы нет.

Проблема «а как это всё цельно в синтаксисе представить» возникает, если мы хотим ввести туда цикл с пост-условием.

В таком случае тело цикла, очевидно, не может заканчиваться на END. Приходится искать подходящее ключевое слово, чтобы обозначить конец тела цикла.

Вот как эту задачу решили в Basic.

Исторически первым вариантом цикла был простой while, который заканчивался на end while:

https://learn.microsoft.com/en-us/dotnet/visual-basic/language-reference/statements/while-end-while-statement

Но затем придумали универсальный вариант, который покрывает 4 разных формы представления цикла:

https://learn.microsoft.com/en-us/dotnet/visual-basic/language-reference/statements/do-loop-statement

wandrien ★★
() автор топика
Последнее исправление: wandrien (всего исправлений: 1)
Ответ на: комментарий от anonymous

В чем недостаток записи repeat ... until, и почему я хочу её выкинуть из синтаксиса, мы выше уже обсуждали с CrX. Равно как и остальные вопросы, которые ты поднимаешь. Читай тему.

wandrien ★★
() автор топика
Ответ на: комментарий от anonymous

С двоеточием проблема в другом месте возникает.

Вот такая запись выглядит очень цельной:

loop_name:
while i != 0 do
   ...
end:loop_name

Цикл вводится меткой с двоеточием, а завершается двоеточием и меткой. Такие как бы скобки получаются.

Это хороший вариант, потому что красивый.

Но кроме того, я размышлял ввести в язык такой способ объявления переменных:

x: int = 10;

Очевидно, здесь возникает конфликт с объявлением метки.

Тогда нужно либо отказываться от этого способа, либо искать для него другую форму выражения.

wandrien ★★
() автор топика
Последнее исправление: wandrien (всего исправлений: 1)

Еще один пока нерешенный вопрос, как записывать выражения типа и как записывать объявления.

Сейчас пока используется урезанный Си-подобный способ.

Хотелось бы от него избавиться, наверное.

wandrien ★★
() автор топика
Ответ на: комментарий от wandrien

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

Хорошей идеей мне кажется сделать синтаксис максимально похожим на самые популярные ЯП, это снижает порог вхождения. Профит от идеализации синтаксиса будет сильно меньше чем необходимость его учить:-)

AntonI ★★★★★
()
Ответ на: комментарий от AntonI

То есть иными словами, ты предлагаешь зачем-то написать еще один компилятор Си.

Смысл попыток сделать новое состоит именно в том, чтобы оно было новое и имело свойства, отличные от старого.

wandrien ★★
() автор топика
Последнее исправление: wandrien (всего исправлений: 1)
Ответ на: комментарий от wandrien

А Вы хочете сделать си с другим синтаксисом?:-)

С какой целью Вы делаете новый ЯП? Джамт фор фан тоже ответ, но таки прежде чем делать надо определиться с целями.

AntonI ★★★★★
()
Ответ на: комментарий от wandrien

Очевидно, здесь возникает конфликт с объявлением метки.

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

правило обьявления метки будет конфликтовать с левой частью присваивания.

вы возьмете идентификатор и не будете понимать, какое выбрать правило - то ли метка обьявляется, то ли левая часть присваивания началась.

и хотя это разрешимо просмотром вперед, такие вот заковыки затрудняют синтаксическую раскраску в редакторах.

далее. прежде чем восторгаться всякими end:while, вспомните о лямбда функциях, запись которых чем короче, тем лучше.

потому или делайте двойной вариант, где скобки {} тождественны do end, или лучше берите сишный синтаксис со скобками, что и делают почти все совр. язычки.

там дураки сидят что-ли? вашими метаниями вокруг синтаксиса все отболели лет 40 назад.

alysnix ★★★
()
Ответ на: комментарий от alysnix

Снова пишешь такую лютейшую дичь, что даже лень отвечать подробно.

Я понимаю, что с твоей стороны с надетой короной это выглядит так, будто ты на коне. Но по факту, это клоунада.

wandrien ★★
() автор топика
Последнее исправление: wandrien (всего исправлений: 1)
Ответ на: комментарий от wandrien

как ты будешь писать лямбды со своими end:if, они там вообще не нужны.

весь мир давно повернулся к краткой форме и компактности записи. все изобретения end:while есть попытки переизобрести аду.

ведь прям просится end:lambda у вас?

alysnix ★★★
()
Ответ на: комментарий от AntonI

С какой целью Вы делаете новый ЯП? Джамт фор фан тоже ответ, но таки прежде чем делать надо определиться с целями.

Я выше уже писал, я делаю ошибкоустойчивый синтаксис + дружелюбный к анализу и рефакторингу незнакомого или сложного кода. Почитайте тему, там выше есть интересные обсуждения.

wandrien ★★
() автор топика
Ответ на: комментарий от wandrien

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

наличие метки будет определяться символом :(или еще каким, вопрос выбора) после ключевого слова типа оператора.

while:some_label  a and b or c do
  ...
  break some_label;
  ...
end
alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от alysnix

Меня не беспокоит конфликт с присваиванием, он решается просмотром на одну лексему вперёд.

Пока что открытыми вопросами остаются:

  • Циклы с пост-условием, как я подробно рассказывал выше.
  • Синтаксис объявлений и синтаксис выражений типа.

По выражениям типа я пока такие наброски держу в уме:

^int // указатель
[10]int // массив
[20][10]int //массив массивов
function(int, int) -> int // функция
struct(int, int, int) // структура
^const int // указатель на константу
const ^int // константный указатель

Но текущая реализация им не соответствует, это так на будущее, когда наброски более явный вид примут.

wandrien ★★
() автор топика
Последнее исправление: wandrien (всего исправлений: 1)
Ответ на: комментарий от wandrien

я делаю ошибкоустойчивый синтаксис + дружелюбный к анализу и рефакторингу незнакомого или сложного кода.

Это ответ на вопрос ЧТО делается, а предлагаю ответить на вопрос ЗАЧЕМ это делается.

Например, когда то в начале карьеры я понял что не хватает ЯП для управления расчетами (в числодробилках). Всякое задание параметров и т.д. и т.п. Я че то накостылял, причем оно из коробки при желании скриптом управлялось а при желании через форточку параметры вводить позволяло. Но потом я перешел под linux и нашел питон - выяснилось что Гвидо сделал именно то что мне надо, тока лучше чем я.

Другой пример, мне часто графики надо строить и я люблю это делать прям из командной строки. За 20 с гаком лет выкристаллизовался DSL (утилита командной строки с кучей коротких опций) которая это делает, вот давеча циклы и макросы туда завез. Вот тут аналогов нет, это очень узкозаточенная под мои хотелки штука.

Не, свои ЯП писать дело хорошее - развивает кругозор, фантазию и пр. Все эти когда то баловались. Но в 99.(9)% случаев оно в итоге остается невостребованным даже самим автором.

AntonI ★★★★★
()
Ответ на: комментарий от wandrien

Хорошо;-)

В качестве ошибкоустойчивости синтаксис максимально контекстнонезависимый? В т.ч. закрытие блоков зависит от типа блока? Я бы м.б. подумал тогда в сторону того, что бы у блоков были имена а не только типы. Я иногда в сложном коде такое оформляю комментами

for(int ip=0; ip<particles_sz; ip++){ // начало цикла по частицам
...
} // конец цикла по частицам

когда функции большие это помогает видеть структуру кода.

AntonI ★★★★★
()