LINUX.ORG.RU

LISP и DSL


1

4

Очень часто можно услышать заявления, что разработка удобных DSL'ей (а вернее EDSL'ей, так как сказки о ридере ими зачастую и остаются) и лисп созданы друг для друга.

И тут возникает WTF: а с хрена бы? Чем набор лисповских символов отличается от такого же набора в питоне, например? Какие преимущества пользователю DSL'я дает лисп?

Вопрос собственно разработки оставляем в стороне, макросы-шмакросы, все дела — это хорошо и понятно.

★★★

Ответ на: комментарий от tailgunner

Так ты хочешь аналог оператора match из ML или функцию сопоставления с образцом?

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

Первое, естественно, невозможно - синтаксис Питона нерасширяем.

Вот этим он хуже диалектов лиспа для создания DSL.

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

>> в случае питона можно было было бы рассматривать добавление переменных в текущую область видимости

Этого я не сделал, но, думаю, это теоретически возможно за счет манипуляции стековыми кадрами и словарем locals. Хотя это наверняка can of worms.

Я так делаю регулярно, и у каких то гуру (типа Метца что ли) такое видел. Иногда очень удобно...

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

> Функцию сопоставления с образцом я могу написать на любом языке, в котором есть функции.

Напиши на Си. Очень надо :D

Вот этим он хуже диалектов лиспа для создания DSL.

Embedded DSL. Я, в общем, и не утверждал обратного.

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

>> Первое, естественно, невозможно - синтаксис Питона нерасширяем.

Вот этим он хуже диалектов лиспа для создания DSL.

Э... я в терминологии не силен (DSL это или eDSL или еще чего), но для обертки гнуплота опять таки на питоне делал парсер выражений вводимых с клавы (хотелось поддержку разных типов скобочек что бы формулы лучше читались). В общем даже банальный str.replace, если есть возможность его вызвать перед передачей кода в интерпретатор, позволяет расширять синтаксис до одури;-)

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

>>> добавление переменных в текущую область видимости

Этого я не сделал, но, думаю, это теоретически возможно за счет манипуляции стековыми кадрами и словарем locals. Хотя это наверняка can of worms.

Я так делаю регулярно

А на Python3 ты это не пробовал?

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

Для меня 3х это кошмар (из за потери совместимости), и пока я жив у нас им никто пользоваться не будет!;-)

Там и во втором были какие то сложности, но уже не вспомню - сейчас все работает.

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

> Зависит ли удобство пользования dsl, если не учитывать время потраченное на разработку

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

Кстати, где звезду потерял?

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

> У вас там в дотнете есть linq, который суть монада. Это уже по-серьезней будет.

Ну монада и монада. Один хрен компайлер генерит класс куда укладывает состояние функции и определяет метод который вызывается из LINQ сахара. Для явы есть костыли типа lambdaj.

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

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

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

Короче, я хочу аналог match из ML, если по-твоему проблем нет, как я могу реализовать эту функциональность на питоне?

filter?

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

Попытался найти пять других языков с CTFE и не таких маргинальных как лисп — не смог. Да, это полезная штука, и можно сказать исключительная.

C++ :)

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

Но уж трансформация байткода или AST много где есть. Конечно это не настолько общо, но сходные задачи решает.

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

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

При чём тут filter?

это я так на match среагировал, сейчас просвещаюсь, чую занесло меня немного :)

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

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

Воот. А если лямбды нету? Лямбда на раз ли вкручивается?

Хотя синтаксис для монад тоже нужно бы вкрутить. Потому как,:

[1,2,3] >>= (\ x -> [1,2,3] >>= (\y -> return (x/=y) >>=
   (\r -> case r of True -> return (x,y)
                    _    -> fail "")))

Это ппц полный. А «Один хрен компайлер генерит класс куда укладывает состояние функции и определяет метод который вызывается из LINQ сахара» - вот здорово если можно самому такое вкрутить, а не тупо в компайлер встроено.

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

Возможности для создания eDSL как исключительная черта лиспа - расхожий лисперский миф.

Лисперы говорят, что исключительно удобное создание eDSL - черта CL. Сравни с тем, что ты говоришь.

Подобные так называемые «расширения языка» возможны и в mainstream-языках, хороший пример тому - {JPA,Hibernate} Criteria API. Scala вообще имеет расширяемый синтаксис «искаропки».

Было бы глупо, если за 50 лет такую крутую фичу не реплицировали.

А на создание «большого DSL» в лиспе уйдет не меньше времени, чем в той же Java. В Java все получится еще и быстрее, благодаря наличию ANTLR и особенно JetBrains MPS.

Для лиспов генераторов парсеров, как будто, нет. В LW он вообще встроенный.

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

> Это ппц полный.

Что именно?

здорово если можно самому такое вкрутить, а не тупо в компайлер встроено.

Reflection в помощь.

Может кому интересно будет про монады

http://blogs.msdn.com/b/wesdyer/archive/2008/01/11/the-marvels-of-monads.aspx

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

PS: честно говоря я этим всем пользовался даже не подозревая что эта хрень зовется монадом. Ну да это все упрощает жизнь. Ежели бы не было встроено сильно бы не расстроился, поскольку программирую что бы решать свои. Как оно там внутри реализуется всем пофиг.

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

Что именно?

Отсутсвие do-нотации или from как в дотнете. Сахарок короче.

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

Давай возьмем реальные примеры. racket как набор dsl или апи emacs. Тупо функции.

Кастую реализацию loop в виде тупо функции. Реально интересно посмотреть.

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

> Кастую реализацию loop в виде тупо функции. Реально интересно посмотреть.

А чего тут принципально невозможного? Разве что заэкранировать тело цикла, но это же чистая косметика из сахара Ну потеряем возможность compile-time оптимизаций. Само по себе Наличию реализации это не помешает.

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

> Кастую archimag-а в тред. Он уже не раз заявлял, что написание

макросов в лиспе - это не повседневная штука


Не, не совсем так, я говорил не про макросы, а про eDSL. Макросы же имеют и много других применений, но зачастую они используется для компенсации каких-либо недостатков языка. Например, запись сложных структур данных штатными средствами CL - может оказаться довольно громоздкой, а макросы позволяют сделать это более-менее вменяемо. Или там макросы типа with-open-file - в C++ такие проблемы (с большим успехом) рулятся с помощью деструкторов и управления областью видимости.

archimag ★★★
()

> Какие преимущества пользователю DSL'я дает лисп?

Если говорить о создании «настоящих» eDSL (типа того же iterate), то основные преимущества лиспа связаны с наличием символов как отдельного типа. В Ruby есть символы тоже, но они там слишком примитивные. sexps потом s, что они symbolic. Как-то этот момент был совершенно полностью упущен в текущем обсуждении.

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

Семантика loop (на минуточку) отличается от лямбды

Более того, семантка отличается не только от function (лямбды и.т.д) но вобще от CL, как базового языка. По этой причине реализация для функции и для макроса будут очень похожие.

(loop for i in '(1 2 3) 
      collect)

(loop-function '(for i in '(1 2 3) collect))

Разница вызова косметическая и на DSL-ность сама по себе не влияет. Реализации в силу идетичности задачи тоже будут практически идентичны. И вот для этого примера замена макроса на функцию вполне себе возможна.

Гораздо интереснее стыковка базовой семантики CL и внешней семантики loop-а

(flet
  ((somefunc (i) 
             ...))
  (loop for i in '(1 2 3)
        collect (* (somefunc i)
                          2)))
Вот в этом примере показаны захват контекса вызова (somefunc) и интеграция в наш EDSL(воспринимать иронически:)) возможности выполнения кода с семантикой базовой системы (CL). Вот для такой манипуляции контекстами и разделения логики - макросы приципиальны. А для создания иной семантики сахар - не более.

antares0 ★★★★
()
Ответ на: комментарий от antares0
(loop-function '(for i in '(1 2 3) collect))

Всё это просто чудесно, но как заставить это вычисляться во время компиляции?

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

А семаника loop-а имеет перпендикулярное отношение к «заставить это вычисляться во время компиляции». Резко выделеный compile-time это следствие наличие REPL-а, точнее возможности налиая в нем учасков кода с различной семантикой. Если бы CL компилировал пакет целиком то стратегия опимизации была-бы более автоматической и вручную лазить в compile-time было бы и не так важно и менее удобно.

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

> Всё это просто чудесно, но как заставить это

вычисляться во время компиляции?


Вообще, компиляция в CL это процесс раскрытия макросов, если макросов нет, то и о компиляции говорить не приходится ))

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

т.е. все 25 специальных форм коммон лиспа совершенно никуда не компилируются? Или это с Вашей стороны шутка?

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

Дык, я об этом и говорю. Если макрос, это просто функция, отрабатывающая в компайл-тайм, то как-то странно утверждать, что функция делает то же, что и макрос, только во время исполнения.

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

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

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

Как посмотреть. Предложенная antares0 схема формально не будет называться макросом, но фактически именно им и будет. Считать ли это возможностью или невозможностью --- вопрос схоластический.

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

Вводное примечание: я ни от чего ни бегу.

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

Заметь второй раз повторяю, loop использут семантику не только отличную не только от семантики функций, но и вобще от семантики CL. Поэтому наличие compile-time и макросов ничем не поможет. Для того что бы распарсить и преобразовать все рано придется писать код который для loop-function будет идентичным. Вот with-open-file и iterate (в многом) привязаны к семантике CL поэтому макросы для них практически единственый вариант (не счтая eval:)), а вот loop сам по себе.

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

> А разговор идет о неразумности или невозможности?

разговор идет о

Кастую реализацию loop в виде тупо функции.

то о бишь о не/возможности.

loop при всей его относительно маштабности можно практически весь перенести в другой язык. А вот простенький with-open-file c сохранением семантики и простоты, фиг.

Именно это отгороженность loop-а делает его такой шуткой юмора - относительно полезной конструкцией с дарением на относительно.

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

> т.е. все 25 специальных форм коммон лиспа совершенно никуда не

компилируются? Или это с Вашей стороны шутка?


Просвещайтесь - http://www.lispworks.com/documentation/lw51/CLHS/Body/03_bbb.htm, всё остальное на усмотрение реализации.

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

Здесь вот недавно мелькала статья по этому поводу

http://lists.common-lisp.net/pipermail/pro/2011-July/000621.html

Как то много букв и все не по делу. Проще уж CL понять и какой-нибудm on lisp прочитать.

Здесь это же но с всеми комментариями. http://comments.gmane.org/gmane.lisp.cl-pro/526

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

но и вобще от семантики CL.

Нету такой. Ну или поясни что имеешь в виду.

Вот with-open-file и iterate (в многом) привязаны к семантике CL поэтому макросы для них практически единственый вариант

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

(with-open-file-func '(stream path :output ...

совсем как настояющий with-open-file, но с «косметическим» изменением --- работать будет в рантайме.

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

Нету такой. Ну или поясни что имеешь в виду.

А семантика функций есть? Семантика CL это то как интепритируются lisp-формы. Не претендую на точность определений.

for i in '(1 2 3)

просто не является единым сематическим элементом.

(for i in '(1 2 3)
будет искать макрос/функцию for с аргуметами в текущем контексте. В итоге формы loop не могут применены без специального транслятора.

Макрос и функции --- это одно и то же, просто работают они либо в компайл, либо в ран-тайме.

Нет. Вполне можно обяъвть функцию в compile-time, но как макрос она все равно работать не будет. Макросы это специальный подтип function специальным способом привязваемый к символу.

Поэтому говорить, что макросы могут чего-то, чего не могут функции глупо и не разумно.

Рассуждения о разумности, оставлю на твоей совести:) Макросы обеспечивают захват окружения и результат раскрытия будет передан в eval, к функции это не относится.

(with-open-file-func '(stream path :output ..

Во первых, переврал мой пример. Надо было так

(with-open-file-func '((stream path :output
Во-вторых, что бы реализовать with-open-file, в отличае от loop придется использовать eval, занов изобретая макросы. Loop большей частью реализутся без eval.

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

Семантика CL это то как интепритируются lisp-формы

Есть семантика лямбда выражений. Есть семантика спец-форм. А семантика КЛ --- это х.з. что такое.

Макросы обеспечивают захват окружения и результат раскрытия будет передан в eval, к функции это не относится.

Вот, к чему ты тут описал процесс компиляции?

Надо было так

Согласен.

Loop большей частью реализутся без eval.

Мы, кажется, обсуждаем принципиальную возможность, не?

ugoday ★★★★★
()

Это совокупная и того и другого. Точнее их семантика разделяется по перврму символу.

ЧТо бы мне тут не рассказывали что макросы и фунции, это одно и тоже.

Для его принципиальной реализации, eval не требуется. Я не имел ввиду кокретные реализации.

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

Вообще-то, как минимум, макросы отличаются от функций тем, что первый не вычисляют своих аргументов (автоматически), а вторые вычисляют.

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

> макросы отличаются от функций тем, что первый не вычисляют своих аргументов (автоматически), а вторые вычисляют.

И?

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.