Что потеряет Clojure, отказавшись от ведущих скобок, таких как Дилан, Джулия и Сеф?
Три неумелых гомоиконических языка, Dylan, Julia и Seph, все отошли от ведущих скобок - так что вызов гипотетической функции в Common Lisp выглядит так:
(print hello world)
Выглядит как следующий гипотетический вызов функции
print(hello world)
на трех языках, упомянутых выше.
Если бы Clojure пошел по этому пути - чем бы он пожертвовал, чтобы попасть туда?
Обоснование: Помимо удивительных ленивых функциональных структур данных в Clojure и улучшенного синтаксиса для карт и последовательностей, языковой поддержки параллелизма, платформы JVM, инструментария и потрясающего сообщества - отличительной чертой этого "LISP" является то, что ведущая скобка, дающая гомоиконичность, которая дает макросы, предоставляющие синтаксическую абстракцию
Но если вам не нужны ведущие скобки - зачем их? Единственные аргументы, которые я могу придумать для их сохранения,
(1) повторное использование поддержки инструментов в emacs
(2) побуждать людей "думать в LISP", а не пытаться воспринимать это как другой процедурный язык)
9 ответов
Написание макросов стало бы намного сложнее, потому что структура больше не была бы простой, вам понадобился бы другой способ кодирования, где выражения начинаются и заканчиваются, используя некоторый синтаксический символ, чтобы отметить начало и конец выражений, чтобы вы могли написать код, который генерирует выражения, возможно, вы могли бы решить эту проблему, добавив что-то вроде (
чтобы отметить начало выражения...
С другой стороны, стоит посмотреть это видео, в котором рассказывается о разнице между знакомым и простым знакомством с синтаксисом шрифтов, что не облегчит людям процесс обучения и может ввести его в заблуждение, если оно похоже на то, чем оно не является.
даже если вы полностью не согласны, это видео стоит того часа.
(Отдайте должное ответу Эндрю Кука, который предоставил ссылку на "Читаемый проект S-выражений Лиспа" Уилера и Глории)
Ссылка выше представляет собой проект, предназначенный для обеспечения удобочитаемого синтаксиса для всех языков на основе s-выражений, включая Scheme и Clojure. Вывод таков: это можно сделать: есть способ чтения Лиспа без скобок.
По сути, проект Дэвида Уилера состоит в том, чтобы добавить синтаксический сахар к языкам, подобным Lisp, чтобы обеспечить более современный синтаксис таким образом, чтобы не нарушать поддержку Lisp для специфичных для предметной области языков. Усовершенствования являются необязательными и обратно совместимыми, поэтому вы можете включить их столько, сколько захотите, и смешать их с существующим кодом.
Этот проект определяет три новых типа выражений:
- Кудрявые-инфиксное выражение. (+ 1 2 3) становится {1 + 2 + 3} в любом месте, где вы хотите использовать инфиксные операторы любой арности. (Существует особый случай, который необходимо обрабатывать с осторожностью, если встроенное выражение использует несколько операторов, например {1 + 2 * 3} - хотя {1 + {2 * 3} } работает, как и ожидалось).
- Neoteric-выражение. (fxy) становится f (xy) (требует, чтобы между именем функции и ее параметрами не было пробела)
- Сладкое-выражение. Открывающие и закрывающие родители могут быть заменены (необязательно) семантическими отступами, подобными Python. Сладкие выражения можно свободно смешивать с традиционными s-выражениями в скобках.
В результате получается Lisp-совместимый, но гораздо более читаемый код. Пример того, как новый синтаксический сахар улучшает читабельность:
(define (gcd_ a b)
(let (r (% b a))
(if (= r 0) a (gcd_ r a))))
(define-macro (my-gcd)
(apply gcd_ (args) 2))
будет выглядеть так:
define gcd_(a b)
let r {b % a}
if {r = 0} a gcd_(r a)
define-macro my-gcd()
apply gcd_ (args) 2
Обратите внимание, как синтаксис совместим с макросами, что было проблемой с предыдущими проектами, которые предназначались для улучшения синтаксиса Lisp (как описано Wheeler и Gloria). Поскольку это просто сахар, конечной формой каждого нового выражения является s-выражение, преобразованное средством чтения языка перед обработкой макросов, поэтому макросы не нуждаются в особой обработке. Таким образом, "читаемый Лисп" проект сохраняет гомоичность, свойство, которое позволяет Лиспу представлять код как данные внутри языка, что позволяет ему быть мощной средой метапрограммирования.
Простое перемещение скобок на один атом для вызовов функций будет недостаточно, чтобы удовлетворить кого-либо; люди будут жаловаться на отсутствие инфиксных операторов, блоков начала / конца и т. д. Кроме того, вам, вероятно, придется вводить запятые / разделители во всевозможных местах.
Дайте им это, и макросам будет намного сложнее просто написать правильно (и, вероятно, было бы еще труднее написать макросы, которые выглядят и работают хорошо со всем новым синтаксисом, который вы ввели к тому времени). И макросы - это не та приятная особенность, которую вы можете игнорировать или сильно раздражать; весь язык (как и любой другой Лисп) построен прямо над ними. Большинство "видимых для пользователя" вещей, которые есть в clojure.core, включая let, def, defn и т. Д., Являются макросами.
Вам не нужно ничего жертвовать. Дэвид Уилер предлагает очень тщательно продуманный подход, который полностью прозрачен и обратно совместим, с полной поддержкой макросов и т. д.
Вы бы Mathematica. Mathematica принимает вызовы функций как f[x, y, z]
, но когда вы исследуете это дальше, вы обнаружите, что f[x, y, z]
на самом деле синтаксический сахар для списка, в котором Head
(элемент 0) f
и Rest
(элементы с 1 по N) {x, y, z}
, Вы можете создавать вызовы функций из списков, которые очень похожи на S-выражения, и вы можете разложить неоцененные функции в списки (Hold
мешает оценке, так же, как quote
в Лиспе).
Могут быть семантические различия между Mathematica и Lisp/Scheme/Clojure, но синтаксис Mathematica является демонстрацией того, что вы можете переместить левую круглую скобку на один атом и все же интерпретировать ее разумно, построить код с помощью макросов и т. Д.
Синтаксис довольно легко конвертировать с помощью препроцессора (намного проще, чем семантика). Вы могли бы, вероятно, получить нужный синтаксис с помощью некоторого умного подкласса в LispReader от Clojure. Есть даже проект, который пытается решить смещающие скобки Clojure, заменив их все квадратными скобками. (Меня поражает, что это считается большим делом.)
Философия "код - это данные" - это то, что делает возможным надежное метапрограммирование. Сравните с Perl / Ruby, которые имеют сложный синтаксис, их подходы к метапрограммированию надежны только в очень ограниченных условиях. Метапрограммирование на Лиспе настолько надежно, что от него зависит ядро языка. Причиной этого является единый синтаксис, общий для кода и данных (свойство гомоичоничности). S-выражения - это способ реализации этой однородности.
Тем не менее, в Clojure существуют обстоятельства, при которых возможны подразумеваемые скобки:
Например, следующие три выражения эквивалентны:
- (первый (отдых (отдых [1 2 3 4 5 6 7 8 9])))
- (-> [1 2 3 4 5 6 7 8 9] (отдых) (отдых) (первый))
- (-> [1 2 3 4 5 6 7 8 9] сначала отдохни)
Обратите внимание, что в третьем макрос -> в этом контексте может выводить скобки и тем самым исключать их. Есть много особых сценариев, подобных этому. В целом дизайн Clojure ошибается в сторону меньших скобок. Смотрите, например, спорное решение покинуть из скобки в конде. Clojure очень принципиален и последовательн в этом выборе.
Я использовал код C/C#/Java/Pascal, поэтому подчеркиваю, что код на Лиспе немного чужд. Однако это чувство длится всего несколько недель - через довольно короткий промежуток времени стиль Lisp будет казаться очень естественным, и вы начнете ругать другие языки за их "неправильный" синтаксис:-)
Существует очень веская причина для синтаксиса Lisp. Ведущие скобки делают код логически проще для анализа и чтения, собирая как функцию, так и выражения, составляющие ее аргументы, в единой форме.
И когда вы манипулируете кодом / используете макросы, именно эти формы имеют значение: это строительные блоки всего вашего кода. Так что в принципе имеет смысл поместить скобки в место, которое точно разграничивает эти формы, а не произвольно оставлять первый элемент за пределами формы.
Интересно, что существует альтернативный синтаксис Racket:
@foo{blah blah blah}
читается как
(foo "blah blah blah")
Were Clojure to go down this path - what would it have to sacrifice to get there?
Жертвой было бы чувство / умственный способ написания кода путем создания Списка Списка Списка... или, более того, технически "написания AST напрямую".
ПРИМЕЧАНИЕ: есть и другие вещи, которые будут раскритикованы, как указано в других ответах.