LINUX.ORG.RU

Занимаясь веб-девелопментом


0

1

Занимаясь веб-девелопментом, я задумался: зачем столько разнородных средств? Ведь можно объединить всё в один язык. И описание, и логику - клиентскую и серверную. Языки разметки вполне годятся для программирования. Вот, например, вычимление 2 + 2 * 2:

<+>2 <*>2 2</*></+>

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

На лиспе в подобном стиле писать не получится: система типов не подходит.

Это фундаментальное непонимание. S-выражения это способ записи данных и программ, представление, в которое строится изоморфизм из любого другого представления. Зачем строится - чтобы было одно единственное представление которое 1) является *данными* 2) всегда может быть *напечатано* / *прочитано*. Всё. Наличие в языке системы типов, абстракций лямбда исчисления, средств алгебраического и категориального программирования - всё это строго ортогонально «лиспам» и их s-выражениям. Все эти вещи в «лиспах» как раз легко могут быть библиотеками.

Можете представить программу на Haskell в скобочном синтаксисе (в тех местах где инфикс и layouts)? Монады от этого не пропадут. А вот на вопрос «как реализовать пользовательскую специальную форму со стратегий вычисления call-by-macro-expansion» будет конкретный ответ.

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

> Наличие в языке системы типов, абстракций лямбда исчисления, средств алгебраического и категориального программирования - всё это строго ортогонально «лиспам» и их s-выражениям. Все эти вещи в «лиспах» как раз легко могут быть библиотеками.

Ну так они в любом языке могут быть библиотеками.

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

Ну так они в любом языке могут быть библиотеками.

В том то и дело, что нет. Будет уровень когда реализовать нечто будет возможно только с помощью бустрапа. В CL я не вижу такоого потолка (ну просто это моё такое наблюдение, всё может быть расширением с компиляцией в CL -> и т.д.).

Шаблоны в си? Алгебраические типы с автоматическим построением катаморфизма в Python? А в рамках принятой в СL схемы мета-вычислкений (reader -> macroexpander -> evalutor -> compiler, причём это цепочка может раскручиваться рекурсивно, на любом этапе) это возможно.

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

> Наличие в языке системы типов, абстракций лямбда исчисления, средств алгебраического и категориального программирования - всё это строго ортогонально «лиспам» и их s-выражениям. Все эти вещи в «лиспах» как раз легко могут быть библиотеками.

Если взять цветной бумаги,
Краски, ножници, и клей,
И немножечко отваги --
Можно сделать сто рублей.

Успехов в приделывании к лиспам таких библиотек :-)

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

>> Ну так они в любом языке могут быть библиотеками.

В том то и дело, что нет.

...

А в рамках принятой в СL схемы мета-вычислкений (reader -> macroexpander -> evalutor -> compiler, причём это цепочка может раскручиваться рекурсивно, на любом этапе) это возможно.

Похоже, наличие компилятора в стандартном рантайме - это одновременно +10 к мегаломании -10 к интеллекту.

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

Успехов в приделывании к лиспам таких библиотек :-)

Считаешь что оно нужно? Может стоит пойти ещё дальше.

Например, на CL уже есть реализация metamath.

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

> Можете представить программу на Haskell в скобочном синтаксисе (в тех местах где инфикс и layouts)? Монады от этого не пропадут.

Получится template haskell, не?

А вот на вопрос «как реализовать пользовательскую специальную форму со стратегий вычисления call-by-macro-expansion» будет конкретный ответ.

call-by-macro-expansion это однозначно зло; полезные вещи, которые с его помощью пытаются сделать, надо делать по-другому

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

> Но ты ведь на самом деле молчишь, да?

Да. Ты подслушал мои мысли.

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

> Считаешь что оно нужно? Может стоит пойти ещё дальше.

я же сказал — это примерно как делать купюру в 100 рублей из цветной бумаги

щас кстати читаю Let Over Lambda

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

насчет кривизны гигиеничных — там тоже есть интересная ссылка

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

> reader -> macroexpander -> evalutor -> compiler

а лучше reader -> macroexpander -> compiler

Ну так они в любом языке могут быть библиотеками.

В том то и дело, что нет. Будет уровень когда реализовать нечто будет возможно только с помощью бустрапа.

Почему вдруг?

Да и тупые средства бутстрапа (типа s/.../.../ ) никто не отменял.

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

>всё это строго ортогонально «лиспам» и их s-выражениям

::facepalm::

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

> это плюсовикам то про кривизну говорить?

уж точно говорить не с теми, кто считает опытных программистов хомячками; так что тебе пока что отвечать не буду

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

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

Скорее всего изоморфизма нет.

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

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

И главное, кусок S-выражения при копировании и вставке в другое место приобретает возможно *совсем* другое значение.

Ридеровские макры и их вычисление как-то борется с этим, но это явно костыль.

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

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

предыдущий пост был про программы, а этот про данные

как в S-выражении запаковать данные, где имеются 2 ссылки на один мутабельный байт в памяти?

можно предложить некий синтаксис опять поверх S-выражений, но это опять костыль

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

Получится template haskell, не?

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

call-by-macro-expansion это однозначно зло

Как же сделать что-нибудь такое? Или не нужно? И ведь это самый простейший пример.

полезные вещи, которые с его помощью пытаются сделать, надо делать по-другому

Ну чисто практически - есть over ~ 10^5 строк кода где используется такой подход - поколения программистов были не правы? Как делать правильно?

щас кстати читаю Let Over Lambda

А я вот так и не собрался :) гы!-макры это, имхо, выпендрёшь (а.к.а. пока не осилил). По крайней мере обычные макросы который везде используются покрывают все задачи, которые вообще нужно решать макросами. А вот какого практическое применение g!-штук пока не понятно (хотя, может next 50 years).

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

Ты подслушал мои мысли.

Ну просто пост есть, а фактических возражений не слышно. Зато есть мутабельность - -10/+10, гастрономия какая-то :)

Qi же.

Ну Qi можно написать на чём угодно, как и metamath, впрочем, - там есть и на Си, и на Python, и на Haskell реализации. Неудачный пример, но есть, к примеру, CLOS - расширение которое без бустрапа расширяет базовый CL. Попытка сделать подобное на Си - построение отдельного рантайма, и использование будет выглядеть как вызовы функций (а не использование родного языка) и выполнение будет происходить в рантайме. В CL же есть возможность провести произвольную трансляцию кода в compile-time (раз) + новый язык выглядеть будет как родной (два). Да , звучит как типичные мантры, но всё же :)

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

> Ну просто пост есть, а фактических возражений не слышно.

Фактическим возражением был бы, например, компилятор расширенного Си, написанный на Си... OH SHI~, их уже не один и не два. Может быть, в Лиспе это сделать проще, но и в других языках тоже можно.

Ну Qi можно написать на чём угодно, как и metamath, впрочем, - там есть и на Си, и на Python, и на Haskell реализации.

О чем и речь.

В CL же есть возможность провести произвольную трансляцию кода в compile-time (раз) + новый язык выглядеть будет как родной (два). Да , звучит как типичные мантры, но всё же :)

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

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

> В CL же есть возможность

Это очень плохо говорить о сферических возможностях. Если говорить о чём-то, то только на конкретных полезных примерах. А то возможности есть, а применения нет.

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

компилятор расширенного Си, написанный на Си...

ЁПР~ это и есть бустрап. Вот если бы форма расширяющая си шаблонным образом превращалась в код на си и процесс компиляции продолжался бы дальше. Т.е. определение самой по себе расширяющей формы это ведь кода - кот наплакал, но в случае Си это потребует или реализации расширенного языка на языке (много кода), или runtime-like расширения (неудобно, тормоза, так как не задействуется стадия компиляции), либо препроцессинга, вроде m4 (нет доступа к типам и прочим internals - опять не то).

Ты говоришь не столько о свойствах языка, сколько о программной среде.

Эти свойства ассоциируются у меня с конкретным языком только потому что ничего подобного я больше нигде не вижу (even Smalltalk!).

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

Ну я не особо какой практик CL :) Но все проекты среднего и большого размера которые я видел активно используют макросы, т.е. расширяют CL пользовательскими специальными формами, где-то даже производится конверсия - трансформации кода.

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

> Как же сделать что-нибудь такое? Или не нужно? И ведь это самый простейший пример.

Щас отвечу, не особо думая (т.е. потом могу мнение поменять).

Зацепившись за кривое свойство захвата переменных, в лиспе из макросов и лямбды удается сделать let. Эка невидаль.

Кривое оно потому, что работает 1 раз, а при попытке взять его композицию обрушится.

С моей точки зрения, ввод переменных должен быть свойством структуры данных «правильная-замена-s-выражениям». И вероятно лямбда как раз определится через нее. Но даже если и не определится, не страшно.

Т.е. из линейно зависимого набора «let macro lambda» я выбираю два первых, а не 2 последних в качестве базиса.

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

Скорее всего изоморфизма нет.

Он не может не есть - он доказывается. Я говорил о том что АТД для AST изоморфно s-expression. На этом уровне ещё нет семантики (никаких переменных и т.д.), семантика привязывается уже к конкретному дереву с семантическими атрибутами (на этом уровне уже не важно представление, также как не важно, для контекстно-свободных языков, чем мы описываем синтаксис - BNF, продукциями по Хомскому - эквивалентные вещи).

топологическое (+)                                  -> (:or ...)      ;; вариантные типы
конструктор, а.к.а. топологическая n-арная операция -> (:operate .n.) ;; композитные типы
топологический элемент                              -> :keyword

И главное, кусок S-выражения при копировании и вставке в другое место приобретает возможно *совсем* другое значение.

Ну, произвольный терм, если его вставить не в то место тоже приведёт к *совсем* другим значениям. Программируют правильно - разные композиции термов просто дают разные функции, разные s-выражения производят разное AST у которых разный смысл. Я вижу тут какое-то предубеждение (типа «нет типов, потому что лисп», «всё нафиг сломается, потому что макросы», «фсё список», «всё можно сделать, потому что лисп» :) и т.д.).

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

как в S-выражении запаковать данные, где имеются 2 ссылки на один мутабельный байт в памяти?

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

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

> Но все проекты среднего и большого размера которые я видел

активно используют макросы


Вот ведь интересный вопрос, а с чем это связано? С тем, что макросы настолько могучи? Или с тем, что, как указывает Страуструп, в языке много недостатков? или много недостатков в программистах?

Поскольку, значительная часть сообщества CL-разработчиков, как бы там помягче сказать, не совсем вменяема, то одна из причин широкого использования макросов как бы на поверхности. А поскольку мы не имеем много примеров впечатляющего софта на CL, то причина номер два - недостатки языка, то же как бы напрашивается.

Так что судить о силе макросов на основе подобных наблюдений как бы не очень разумно. Говорить об этом имеет смысл только на конкретных реальных примерах.

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

> Он не может не есть - он доказывается. Я говорил о том что АТД для AST изоморфно s-expression.

и при этом ни полслова не сказал, что за операции определены у AST — почему я и сказал «Скорее всего»

да, ты можешь ограничить набор операций и радоваться изоморфизму, только на практике (в т.ч. практике лиспа в LoL) от AST нужно больше

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

Зацепившись за кривое свойство захвата переменных, в лиспе из макросов и лямбды удается сделать let. Эка невидаль.

Выражение

let x = s in t ≡ (λ x . t) s

это из чистого лямбда-исчисления, о том что let - сахар над чистым лямбда-исчислением (см. John Harrison, Introduction to Functional Programming). Вот в LC вводят такой сахар (последний пост) а в Haskell есть такие средства, только для прямых рук?

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

точно так же, как ты, можно было бы утверждать, что AST изоморфно последовательности байтов, и сказать:

На этом уровне ещё нет семантики (никаких переменных и т.д.), семантика привязывается уже к конкретному дереву с семантическими атрибутами

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

и при этом ни полслова не сказал, что за операции определены у AST — почему я и сказал «Скорее всего»

Там вроде как про представление языков было - там всегда деревья.

да, ты можешь ограничить набор операций и радоваться изоморфизму, только на практике (в т.ч. практике лиспа в LoL) от AST нужно больше

Главное на этом месте остановится и прокричать, что у LoL нет никакой практики - нет пока такой :) Настоящие AST - да, они вообще алгебраические, из них нельзя ничего вылепить другое (тот самый data в Haskell, который вводит обще-топологическое отображение между категориями). А вот в случае деревьев - есть отображение в, минимальные, s-деревья.

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

точно так же, как ты, можно было бы утверждать, что AST изоморфно последовательности байтов

Извини, так сказать я не могу, потому что это не правда (по крайней мере не вижу связи тут).

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

>> и при этом ни полслова не сказал, что за операции определены у AST — почему я и сказал «Скорее всего»

Там вроде как про представление языков было - там всегда деревья.

Лисп головного мозга.

Там точно не деревья. Про что я уже и талдычу *давно*.

Самое близкое — это «дерево, дополненное обратными ссылками на место определения переменной».

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

Вот сейчас увидел взаимооднозначное преобразование. Но в математике всегда есть разные формы говорить об одних и тех же вещах. А вот в программировании выбирают самую простую/очевидную/удобную (как для решения прикладных квантово-механических задач используют самую подходящую её, КМ, формулировку). S-выражение это минимальное дерево, плюс оно читаемое, вот Хомский в своих работах по лингвистике использовал такую форму, а МакКарти использовал для создания того самого абстрактного искусственного языка.

Вот поставят перед тобой ТЗ - разработать протокол передачи HTML в бинарной форме, ты уже на этот экзотический изоморфизм (AST <-> байт-код) иначе будешь смотреть :) Конечно можно о нём просто не знать - просто решать задачу (как например, если мы заговорили об AST, о том что read/eval/print это катаморфизмы на ADT определяющих AST - можно ведь просто не знать).

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

> (по крайней мере не вижу связи тут).

что у тебя в дерево, что у меня в строку байтов не запихнута жизненно важна семантика; а без нее пофиг, что выбрать

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

Там точно не деревья. Про что я уже и талдычу *давно*.

Ты просто не знаешь. Как и с тем примером выражающим let через лямбду. Ты знаешь о том что такое продукции Хомского для контекстно-свободного языка? Или семантические атрибуты по Кнуту? Не то что бы я считал это важными знаниями - но нельзя обсуждать затронутые темы «просто так», на манер «а я вот так считаю», там есть прописные истины - их преподают, либо юзают самообразование.

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

Самое близкое — это «дерево, дополненное обратными ссылками на место определения переменной».

Эти графы, со связями переменных, возникают позже. Их не используют в синтаксических рассуждениях. Семантика - да, s-выражения и AST не имеют отношения к семантике.

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

> Ты просто не знаешь. Как и с тем примером выражающим let через лямбду.

С этой идеей я знаком года с 1995 (в виде «нужна локальная переменная? ну так определи новую функцию с лишним формальным параметром»).

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

Лучше ты объясни, зачем нужны лет-подобные формы? Какие задачи ты решишь с их помощью?

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

Кстати, с чего бы это в аббревиатуре AST - слова «синтаксис» и «дерево», не знаешь? ;)

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

недревестность.

Цу! We need it! Недревестность :)

Лучше ты объясни, зачем нужны лет-подобные формы?

(mama has let, papa has let, so you has it too, or you not a human?) В haskell уже есть let, тот пример - вопрос на тему «как ввести простейшую специальную форму своими руками (на примере let)» - не покрывают ленивые функции этого, так как им есть дело до семантики (те самые переменные), а чтобы уметь в языке опереровать AST (макросы) нужно оперировать на уровне синтаксиса (да, TH, Liskell, etc. но заумно).

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

С этой идеей я знаком года с 1995

Кстати, круто :)

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

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

Я кстати сказал - ты связываешь «недревестность» («графичность», графы) с семантикойм - это правильно. Но не имеет отношения к синтаксису. Мы говорили об «Абстрактных Семантических Деревьях».

Если нужна порция абсолютной истины:

1) Продукции Хомского, или BNF, параметризируются с помощью алгебраического РТД, этим определяется дерево вывода.

2) Чтение, вычисление, печать и прочие трансформации это катаморфизмы над этим АРТД, они же наделяют дерево семантическими атрибутами (они же могут использовать графические представления, та же методы graph reduction).

Это если очень формально.

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

> Ты знаешь о том что такое продукции Хомского для контекстно-свободного языка? Или семантические атрибуты по Кнуту? Не то что бы я считал это важными знаниями - но нельзя обсуждать затронутые темы «просто так», на манер «а я вот так считаю», там есть прописные истины - их преподают, либо юзают самообразование.

можно, но при условии что собеседнику (т.е. тебе) интересно, где он обламается со своим подходом; тогда бы быстро меня понял

вначале ты писал

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

заметим — программ, а не синтаксической структуры программ, на которую ты съехал в последнее время

______________________________

я тут, кстати, интересуюсь, где меня может встретить облом

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

алгебраического РТД

Не обязательно Р, это в общем случае.

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

заметим — программ, а не синтаксической структуры программ, на которую ты съехал в последнее время

Т.е. я в начале не ясно выразился и только теперь ты более менее понял о чём я говорил? Когда я говорил «запись» я, очевидно, имел ввиду синтаксис. Синтаксис представления данных, программ. Вот теперь прояснили этот момент.

Ну и вообще s-выражения относятся только к синтаксису сами по себе. Когда говорят «Антоновка» - имеют ввиду яблоки, не груши :)

Смотрю - что это за топик вообще? :) Ага, ну тут с самого начала был посыл про то что нужно, мол, поменять синтаксис на абракадабру. А Macil любит придумать ветряные мельницы с обязательными действующими лицами «типы», «скобочки», «языки», «не взлетит».

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

>mama has let, papa has let, so you has it too, or you not a human?

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

2. let-формы вносят совершенно неожиданную вещь — внезапно переменная оказывается определенной; такие случаи должны быть известны *заранее и всем*, и значит, фиксированы языком программирования

3. ну допустим у тебя есть способ делать let-формы; ты сможешь на их основе сделать хотя бы модификатор private? (и не плеваться потом на язык, да)

не проще ли сделать, например, интерфейс к таблице символов?

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

> Т.е. я в начале не ясно выразился и только теперь ты более менее понял о чём я говорил? Когда я говорил «запись» я, очевидно, имел ввиду синтаксис.

Записывать можно не только 1. код, скармливаемый компилятору, но и 2. код как результат распечатки кода программ — допустим, активных замыканий (что актуально в теме про лисп).

Ну и запись 1 и 2, очевидно, желательно иметь на одной основе. И для 2 деревья не годятся — значит, не годятся и для 1.

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

С тем, что макросы настолько могучи?

Ну ты сам знаешь как они работают и когда их следует использовать. Тут рядом ты говорил, что используешь их в рамках одного файла. Не вижу причин не использовать их и между файлами (macros.lisp в начале системы - и всё). Каждый раз когда начинает появляться код (канонiческий, без макросов) с одинаковым рисунком - этот паттерн (рисунок, условно) можно сделать макросом. А так как наличие такого повторяющегося кода в больших проектах - типичное дело, вот макросы и появляются. Вот есть код который вводит какой-то протокол, причём есть набор протоколов код которых отличается не параметрами а алгоритмическими кусками - делают defprotocol и используют по всему проекту.

Вообще, не вижу в макросах чего-то сверх особенного чтобы ставить вопрос - «надо или не надо». Хотя, для меня «не надо» это заведомо не-ъ позиция - очень странная позиция :) Да, не серебренная пуля, но пригодится ведь - пусть будет (только без g!-макрологий).

как указывает Страуструп

Это где? Или он про си-макросы говорил?

Поскольку, значительная часть сообщества CL-разработчиков, как бы там помягче сказать, не совсем вменяема, то одна из причин широкого использования макросов как бы на поверхности. А поскольку мы не имеем много примеров впечатляющего софта на CL, то причина номер два - недостатки языка, то же как бы напрашивается.

Так что судить о силе макросов на основе подобных наблюдений как бы не очень разумно. Говорить об этом имеет смысл только на конкретных реальных примерах.

Я говорю именно о конкретных реальных примерах - CFFI, IOLib, Slime, Hemlock, Maxima, SBCL - примерно расположил в порядке возрастания количества кода и, соответственно, macrologyes в проекте. Возможно, дело в том что практика разработки на CL небольших программ и практика разработки больших - это две разных практики (так как не могу похвастаться такой серьёзной практикой - могу только предполагать).

Ты хочешь сказать, что CL гораздо более выгоден как *современный* язык в случае если в нём спрятать макросы от постороннего взора? Или вообще выкинуть. Выкинуть макросы - выкинуть SBCL, фактически макросы это «благородные костыли» SBCL, которые позволяют чего-то там компилировать без особых формализаций (там есть комментарий - CL большой, так что no way тут BNF разводить, будем делать «на макросах»).

Думаешь «осовремененный» CL без макросов позволит написать больше более качественного софта?

По крайней мере не вижу прецедента - где такой софт? Hunchentoot, возможно, там макросы играют вторичную роль. Навороты hunchentoot, которые ни раз обсуждались, тоже не принисут туда много макросни. Но сравнивать этот веб-сервер с SBCL или Maxima - как-то не получается.

З.Ы. И чего лисперы опять полезли не в тот трэдъ? :)

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

Про let - я не понимаю, ты говоришь как человек, который много времени проводит наедине с C или C++.

let люто-бешено используется в CL (Scheme, ELisp, et all.), неявно он там присутсвует и в defun/define и в lambda и много где ещё. Локальная область видимости всё-таки.

let он же where так же люто-бешено используется в любой программе на Haskell.

Т.е. не понимаю насколько, насколько ты бы меня не понимал если бы я говорил:

1) <typedef|struct|if|for...> нужны слишком редко чтобы давать их прикладным прогаммистам.

2) <typedef|struct|if|for...> вносят совершенно неожиданную вещь — внезапно программа оказывается написанной и ведёт себя хрень знает как.

3) ну допустим у тебя есть способ делать <typedef|struct|if|for...>; ты сможешь на их основе сделать хотя бы модификатор LazyIO?

не проще ли сделать интерфейс к STG?

Как-то так :)

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

> Ты хочешь сказать, что CL гораздо более выгоден как *современный*

язык в случае если в нём спрятать макросы от постороннего взора?


Нет, я просто против обожествления макросов и s-выражений, которое имеет место быть. Я за взвешенный и критический подход. А ещё за простоту.

И чего лисперы опять полезли не в тот трэдъ? :)


Одни полезли, а другие стали возражать, что не надо s-выражения куда ни поподя пихать, для лиспа же это будет лучше.

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

обожествления макросов и s-выражений

И кто их обожествляет? Я просто говорю что они есть и обладают такими-то свойствами. Обожествлять что-то (т.е. утвердить как серебряную пулю в программировании) - очень сложно, не вижу такого.

Одни полезли, а другие стали возражать, что не надо s-выражения куда ни поподя пихать, для лиспа же это будет лучше.

Не пихаются они сами по себе это от нас не зависит.

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

>Смотрю - что это за топик вообще? :)

Ну все, млин. Подавил интеллектом. Ажно до катаморфизмов докатился. Ну понятно, типа катаморфизм — светртка списков в алгебраические типы данных и типа в лиспе мы о-го-го могем.

Только есть одно «но». Математиков вопрос «Зачем?» не волнует. А прикладников, представь себе. Может быть где-то в идеале и можно с блекджеком и шлюхами свернуть произвольное выражение в произвольный алгебраический тип данных, но зачем?

От того что между объектами есть морфизмы, идентичными они от этого не станут. И если математику это пофиг, то программисту - нет.

И есть хоть одна практическая реализация? Типа свернули S-выражение в алгебраический тип и быстренько проверили его структуру?

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

> И кто их обожествляет?

Не пихаются они сами по себе это от нас не зависит.


Да я же не про тебя и говорю. Но если почитать ЛОР за последние лет 5 (что раньше было не знаю) на предмет лисп-срачей, то очевидно, что имеется стойкое поверье среди некоторых его посетителей, что макросы и s-выражения спасут мир от Java, индусов и вообще всех напастий.

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

> let он же where так же люто-бешено используется в любой программе на Haskell.

наличие where однозначно полезно, но возможность написать my_where_with_banana_smell вредна

Т.е. не понимаю

поскольку прочел п.1 слишком буквально; а если бы еще и вспомнил, что я воспринимаю определение функции в си/с++ как let-форму, то все бы понял

ладно, п.1 читать как:

1. иные, чем в хаскеле let-формы нужны слишком редко, чтобы была необходимость давать их в руки прикладным программистам (ну может еще одну сделать про запас, и хватит)

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

Т.е. ты не видишь необходимости в средствах которые позволяют оперировать не предметной областью (то зачем нужны ЯП - делаем типы, пишем функции, кладём по модулям/классам и т.д. - код, код, код) а самим кодом? Как #define или defmacro? Вот повторяется один и тот же рисунок в коде, но не от параметров зависит (чтобы сделать функцией) а от кусков кода - хорошо бы иметь «функцию» которая из кода составляет код. Опять же как ты предлагаешь расширять компилятор пользовательскими трансляциями/трансформациями? Или прикладник не должен этим заниматься?

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

> Вот повторяется один и тот же рисунок в коде, но не от параметров

зависит (чтобы сделать функцией) а от кусков кода


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

как ты предлагаешь расширять компилятор пользовательскими

трансляциями/трансформациями?



Боже упасти встретить такое в прикладном коде. Это ведь вообще не нужно. Ибо можно слать патчи в рассылку, или форкнуть под свои нужды.

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

И есть хоть одна практическая реализация? Типа свернули S-выражение в алгебраический тип и быстренько проверили его структуру?

Ты продолжаешь говорить об s-выражениях как о сущностных элементах в лиспах, тогда как это просто форма. Форма записи программы, тоже что и отступы в Python или Haskell. Единственное отличие - можно работать с этой формой, т.е. можно работать с тем кодом, который мы видим на экране, безотносительно предметной области. Это синтаксис, а сущность и семантика - типы, АТД, объекты-стрелки, - стоит за формой.

Вот типичный пример:

data Tree a = Leaf a | Branch (Tree a) (Tree a)

type TreeAlgebra a r = (a -> r, r -> r -> r)

cata :: TreeAlgebra a r -> Tree a -> r
cata (f, g) (Leaf x)     = f x
cata (f, g) (Branch l r) = g (cata (f, g) l) (cata (f, g) r)

treeSum :: (Num a) => Tree a -> a
treeSum = cata (\x -> x, \l r -> l + r)

Настоящий АТД, и катаморфизм для него. Мы же не будем смотреть на синтаксис - как это записано, тут главное сущности, которые мы ввели.

Но для CL тоже самое - форма, s-выражения, в данном случае, не играют роли, а сущности за записью стоят те же:

(defdata (tree a)
  (leaf a)
  (branch
    (left  (tree a))
    (right (tree a))))

(defun cata (f g tree)
  (typecase tree
    (leaf   (funcall f (leaf tree)))
    (branch (funcall g (cata f g (left  tree))
                       (cata f g (right tree))))))

(defun tree-sum (tree)
  (cata #'identity #'+ tree))

Надо заметить - это по-прежнему CL, по-прежнему динамический, также нет pattern-matching и point-free. Но я ещё хочу сказать, что можно ввести то чего нет - и это будет по-прежнему CL. А вот введённые АДТ и катаморфизм такие же «железные» как и в Haskell.

Теперь немного дальше - нужно определить

data Tree2 a = Leaf a | Branch (Tree a) (Tree a)
data Tree3 a = Leaf a | Branch (Tree a) (Tree a) (Tree a)
data Tree4 a = Leaf a | Branch (Tree a) (Tree a) (Tree a) (Tree a)
data Tree5 a = Leaf a | Branch (Tree a) (Tree a) (Tree a) (Tree a) (Tree a)
data Tree6 a = Leaf a | Branch (Tree a) (Tree a) (Tree a) (Tree a) (Tree a) (Tree a)
data Tree7 a = Leaf a | Branch (Tree a) (Tree a) (Tree a) (Tree a) (Tree a) (Tree a) (Tree a)

не с точки зрения предметной области, тут рисунок кода можно унифицировать - начинаются s-выражения и макрология:

(macrolet ((deftree (n)
             `(defdata (tree a) (leaf a) (branch ,@(each (s (gensym-list n)) `(,s (tree a)))))))
  (many deftree 2 3 4 5 6 7))

но сами АДТ (сущности) тут ни при чём - s-выражения/макросы это про форму.

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

Вот повторяется один и тот же рисунок в коде, но не от параметров зависит (чтобы сделать функцией) а от кусков кода - хорошо бы иметь «функцию» которая из кода составляет код

эти куски кода представляют собой что-то, значит имеют какой-то тип данных; соответственно для параметризации достаточно функции, принимающией этот тип данных, или шаблона (шаблоны, в общем-то, те же функции)

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

скажем, LoL-ский макрос, позволяющий задавать сон в единицах измерения (сек, мин, час, ...), по эффективности вполне эквивалентен функции

inline void my_sleep(uint time, enum TimeUnit time_unit) { sleep(time*time_unit); }

при этом онa позволяет написать my_sleep(1, very_slow?DAY:HOUR), в отличие от макроса

понятно, что не хватает синтаксического сахара — тогда берем с++0х и вводим литералы 42_hour, 37_min, 40_ms

в общем, ридеровские макры, система типов и partual evaluation делают обычные макры ненужными <--- вот это критиковать :-)

впрочем, плюсовый подход как всегда недостаточно развит в с++, его нужно двинуть дальше; конкретно, плюсовый шаблон (для литералов из с++0х потребуется шаблон) не может легко быть одновременно и функцией

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от quasimoto
-             `(defdata (tree a) (leaf a) (branch ,@(each (s (gensym-list n)) `(,s (tree a)))))))
+             `(defdata (,(symbolicate 'tree- n) a) (leaf a) (branch ,@(each (s (gensym-list n)) `(,s (tree a)))))))
quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

data Tree7 a = Leaf a | Branch (Tree a) (Tree a) (Tree a) (Tree a) (Tree a) (Tree a) (Tree a)

здесь видимо нужны dependent types, а без них получается обычный, хотя и типизрованный (т.е. лучше лисповского) макрос :-(

template<int i>struct Tree { virtual ~Tree(); };
template<int i>struct Leaf: public Tree<i> { };
template<int i>struct Branch: public Tree<i> { Tree<i> value[i]; };
www_linux_org_ru ★★★★★
()
Ответ на: комментарий от quasimoto

> `(defdata (,(symbolicate 'tree- n) a) (leaf a) (branch ,@(each (s (gensym-list n)) `(,s (tree a)))))))

ахренеть; по-моему плюсовые шаблоны понагляднее и там вероятность ошибки поменьше

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

шаблоны, в общем-то, те же функции

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

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

Шаблоны это подмножество CL-макросов, а не наоборот (ясчитаю :)).

LoL-ский макрос, позволяющий задавать сон в единицах измерения

Очевидно, что такой лоловский макрос - глупость. sleep всегда был обычной функцией. Вот пример, когда тащат макросы куда не нужно.

в общем, ридеровские макры, система типов и partual evaluation делают обычные макры ненужными <--- вот это критиковать :-)

Ну так где тот язык (даже в виде proof of concept) в котором эти три заменяют CL-like-макросы? Неужно в с++0х - тогда show me the code, в а в Haskell оно очень непрозрачно.

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

Не надоело ветряные мельницы строить? А то ночь уже :)

здесь видимо нужны dependent types, а без них получается обычный

Это обычный параметрический тип.

хотя и типизрованный (т.е. лучше лисповского)

Опять старый миф «нет типов, потому что ЛИСП», ага.

ахренеть; по-моему плюсовые шаблоны понагляднее и там вероятность ошибки поменьше

Так и знал, что кто-нибудь это скажет - «ага, патчик, лисповый код, вероятность ошибки over 90%» или «такое количество повторяющихся слов в ЛИСПе - очевидная ошибка в архетектуре».

А вообще - дело привычки. Мне вот C++ шаблоны гораздо сложнее понимать чем CL макросы.

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

> Очевидно, что такой лоловский макрос - глупость. sleep всегда был обычной функцией. Вот пример, когда тащат макросы куда не нужно.

ВСЕ макросы ничем не лучше этого :-)

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

ВСЕ макросы ничем не лучше этого :-)

Тогда man http://www.linux.org.ru/jump-message.jsp?msgid=5513477&cid=5517719

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

Поясню - рисуночный код может быть размером 100 строк, и возникать этот рисунок может 100 раз - что лучше 100*100 строк кода или 100+100+1 строк?

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

> А вообще - дело привычки. Мне вот C++ шаблоны гораздо сложнее понимать чем CL макросы.

Есть объективная оценка.

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

В плюсовом случае надо тупо расставить template<int i> и <i>

В случае лиспа надо ЕЩЕ знать про новые функции, gensym-list например.

___________________________________

А что делать, если тебе надо в лиспе tree<i>, где i вводится с клавиатуры? Макрос обломается, придется писать еще и функцию. Нынешний с++, правда, не лучше (хотя его можно и подтянуть).

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

В случае лиспа надо ЕЩЕ знать про новые функции, gensym-list например.

Поэтому я и говорю что макросы - более сложны чем шаблоны, по мощности. Можно не то что gensym-list, но и произвольным образом обрабатывать отданный макросу код.

А что делать, если тебе надо в лиспе tree<i>, где i вводится с клавиатуры? Макрос обломается, придется писать еще и функцию.

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

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

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

Tree7 и компания что ли? Уже показал на плюсах

хотя там все же ошибка — забыл вытащить тип «а» в шаблон

template<class A, int i>struct Tree { virtual ~Tree(); };
template<class A, int i>struct Leaf: public Tree<A,i> { A value; };
template<class A, int i>struct Branch: public Tree<A,i> { Tree<A,i>* value[i]; };

и параметрический полиморфизм так просто не получить.

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

Эта штука вводит 6 разных классов (или шаблон, или что там)?

И что, можно таким образом произвольный код рисовать? Я знаю, что нет :)

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

> Эта штука вводит 6 разных классов (или шаблон, или что там)?

столько, сколько потребуется (делает она это лениво — только когда ты объявишь переменную нужного класса)

И что, можно таким образом произвольный код рисовать? Я знаю, что нет :)

можно, но дальше области применимости выглядеть это будет жутко, и кое-что, известное компилятору, из шаблонов не «прочитать»

но я и не предлагал это как идеал — макросы (кроме ридеровских) не нужны :-)

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

> Ну что ж, буду иметь это ввиду :)

это надо не иметь в виду, а критиковать :-)

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

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

Это ж нужно про C++ думать - лениво. Мне что-то ясно относительно CL, тебе - относительно C++ и мы пытаемся со своих точек зрения думать про «вообще».

У меня с С++ не очень. Вот на си простейший пример (навеяно memory-accessors из CFFI):

/*
 * Skit #1
 */

typedef struct {
  ...
} Object;

#define TYPE(O) ...

static inline fn_1 (Object *object_1) { ... }
static inline fn_2 (Object *object_1, Object *object_2) { ... }
static inline fn_3 (Object *object_1, Object *object_2, Object *object_3) { ... }
...
static inline fn_n (Object *object_1, Object *object_2, Object *object_3, ..., Object *object_n) { ... }

SomeType
foo (Object *object_1, Object *object_2, Object *object_3, ..., Object *object_n) {
  SomeType result;
  switch (TYPE(object)) {
    case TYPE_1 : result = fn_1(object_1); break;
    case TYPE_2 : result = fn_2(object_1, object_2); break;
    case TYPE_3 : result = fn_3(object_1, object_2, object_3); break;
    ...
    case TYPE_N : result = fn_n(object_1, object_2, object_3, ..., object_n); break;
  }
  return result
}

Нужно построить макрос-генератор такого - в зависимости от n, и возможно от (...)-точий:

macro(3, ...тут-кусок-кода...или-не-нужно?...в-продакшене-не-поймут-да?...);
macro(5, ...);
macro(7, ...);

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

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

хрен с системой типов (я не понял твой код и откуда взялось macro)

лучше разберемся — какой из лисповских макросов неудобно или невозможно представить в виде partial evaluation какого-то кода? предполагаем, что partial-eval действует разумно, не не гениально

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

> (mama has let, papa has let, so you has it too, or you not a human?)

Не, я негров люблю.

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

>Надо заметить - это по-прежнему CL, по-прежнему динамический, также нет pattern-matching и point-free. Но я ещё хочу сказать, что можно ввести то чего нет - и это будет по-прежнему CL. А вот введённые АДТ и катаморфизм такие же «железные» как и в Haskell.

Дык, вот в этом и проблема. Хотя, АТД можно реализовать и на ООП-подсистеме: взять ту же Скалу. Она кстати, такое позволяет творить с ООП, что невольно начинаешь сомневаться в адекватности тех кто был до. И самое главное — формально, в отличие от C++, Java и т.п.

Кроме того, к каждому символу лиспа можно привесить кучу метаданных

Другая проблема, для каждого нашего списка, генерируемого S-выражением, придется определять порождающие алгебры. Ведь у нас будут задачи покруче, чем примитивная свертка. Задачи предметной области никто не отменял. Тоже задачка так-себе, ведь никто не привык смотреть на предметную область с такой позиции. Еще нужен аппарат, который проверял бы наши алгебры на непротиворечивость.

Короче, вопросов пока больше чем ответов.

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

Почему мы продолжаем говорить об s-выражениях? Попробую подойти с другой стороны. В посте на который ты отвечаешь я говорил о том что s-выражения это только форма, но ты видимо не так понял - не в том смысле, что это форма с помощью которой что-то там моделируется. Скажем прямо - можно выкинуть всякие s-выражения и макросы, всё, забыли - нет их. Они не имеют отношения к предмету разговора, и только сбивают с толку. Т.е. такой мысленный эксперимент - допустим на момент создания МакКарти лиспа s-выражения были в чём-то исключительны, но теперь, если взять современный Сommon Lisp и выкинуть s-выражения и макросы - много останется? Так вот - останется почти всё что есть, мало что пропадёт. (До этого я ещё заметил, что можно писать определения тегированых типами деревьев в виде s-выражений, но видимо зря говорил - можно «не впихивать АТД в простенькие списки», а использовать настоящие АТД - на самом деле тут есть выбор).

Тот код можно написать без скобок:

defdata (tree a)
  leaf a
  branch
    left  (tree a)
    right (tree a)

defun cata (f g tree)
  typecase tree
    leaf   (funcall f (leaf tree))
    branch (funcall g (cata f g (left  tree))
                      (cata f g (right tree)))

defun tree-sum (tree)
  cata #'identity #'+ tree

можно даже сделать такой ридер - от этого ничего не изменится. Т.е. s-выражения тут вообще ни причём. Почему из наличия в CL одной выразительной возможности (s-выражения) ты обесцениваешь автоматически все остальные? Почему «если списки, то не может быть типов»? На основе какой литературы ты сделал вывод, что типы в CL имеют отношения к s-выражениям? Что нет возможности делать АТД? Композитные, вариантные типы, классы типов как first-class объекты несводимые к каким-то там s-выражениям? Где ты прочёл что «всё списки и символы» и прочую муру из 50-70-ых годов прошлого века? :)

Давай сначала:

data Tree a = Leaf a | Branch (Tree a) (Tree a)

эквивалентно

(defdata (tree a)
  (leaf a)
  (branch
    (left  (tree a))
    (right (tree a))))

нет тут списков/s-выражений и т.д. Этот код определяет *настоящие* типы branch, leaf и tree

(type-of (branch (leaf 1) (leaf 2)))
;=> branch

Этот тип branch не является типом list и никакого отношения к спискам вообще не имеет.

(type-of (leaf 1))
;=> leaf

Также - leaf это не список это *настоящий*, введённый, тип.

Haskell    CL

Leaf       leaf
Branch     branch
Tree       tree
:t         type-of

Из стандарта - defstruct

(defstruct foo
  (a nil :type bar)
  (b nil :type fooz))

тоже что и

data Foo = Foo { a :: Bar, b :: fooz }

только defstrcut может агрегировать по другим структурам и имеет десятки разных опций определяющих его поведение.

deftype похож на классы типов (только классы - обычные типы, т.е. могут быть вариантные типы вариантных типов и т.д.)

(deftype one-of-type () '(or type1 type2 type3))

тип one-of-type - вариантный от типов type1 type2 type3.

или

(deftype foo () '(member :a :b :c))

то же что

data Foo = A | B | C

сам тип может зависеть от произвольного количества параметров (объектов) а спецификация типа может быть сгенерирована:

(flet ((equidimensional (a)
         (or (< (array-rank a) 2)
             (apply #'= (array-dimensions a)))))
  (deftype square-matrix (&optional type size)
    `(and (array ,type (,size ,size))
          (satisfies equidimensional))))

т.е. некое подобие параметрических/зависимых типов.

А тот defdata - простейший макрос, от своих определений он генерирует ряд defstruct и один deftype. И кстати, всё это также не имеет отношения к CLOS (можно использовать методы для этих объектов - но сами типы/объекты не являются CLOS-специфичными).

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

при таком подходе тебе видимо надо изучить Qi, потом нам расскажешь

При каком? При чём тут Qi? Не собирался и не собираюсь его изучать, спасибо :)

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

Кстати, вот тебе универсальный тест для языка, чтобы проверить в нём макросы/шаблоны/etc. (любое средство метапрограммирования):

1) Пользователь определяет АТД,

2) Функция-катаморфизм для этого АТД должна определиться автоматически.

Естественно, определится в compile-time и желательно оптимальным образом (с превращением хвостовых рекурсий в циклы и т.д.).

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

Как выглядит?

В первом приближении как-то так:

(defmacro map-each ((element list) &body body)
  `(mapcar #'(lambda (,element) ,@body) ,list))

;;;; implementation of the haskell-like `data' special form

;;; We assume that ADT is a general topological isomorphism in the following form:
;;;
;;;   NewType ~ TypeExpr(NewType)
;;;
;;; Where NewType is
;;;
;;;   NewType ::= TypeName | TypeName [TypeParameter]*
;;;   TypeName, TypeParameter is symbolics
;;;
;;; And TypeExpr is
;;;
;;;   TypeExpr      ::= StructureType <|> StructureType <|> ... <|> StructureType
;;;   StructureType ::= AtomicType | CompositeType
;;;   AtomicType    ::= Constructor
;;;   CompositeType ::= Constructor [Type | Accessor :: Type]*
;;;   Type          ::= NewType (for RDT) | SomeTypeInUniverse
;;;   Constructor, Accessor is symbolics
;;;   SomeTypeInUniverse is valid type expression in host language
;;;
;;; It means that:
;;;
;;;   <|>           ~ topological union
;;;   `Constructor' ~ n-ary topoligical operation
;;;

;;; CL specific notes:
;;;
;;;   Atomic types maps into `defstruct' and `defparameter'
;;;   Composite types maps into `defstruct'
;;;   Variant types maps into `deftype/or'
;;;

(defmacro defdata (new-type &rest type-expression)
  (multiple-value-bind (type-name type-parameters)
      (etypecase new-type
        (symbol (values new-type nil))
        (cons   (values (first new-type) (rest new-type))))
    (let ((constructors (map-each (structure-type type-expression)
                          (etypecase structure-type
                            (symbol structure-type)
                            (cons   (first structure-type))))))
      `(progn
         (deftype ,type-name () '(or ,@constructors))
         ,@(map-each (structure-type type-expression)
             (etypecase structure-type
               ;; atomic type
               (symbol `(progn
                          (defstruct (,structure-type (:constructor ,structure-type ())))
                          (defparameter ,structure-type (,structure-type))))
               ;; composite type
               (cons   (let* ((constructor (first structure-type))
                              (slots       (rest  structure-type))
                              (accessors   (if (and (= 1 (length slots))
                                                    (symbolp (first slots)))
                                               (list constructor)
                                               (mapcar #'first slots))))
                         `(defstruct (,constructor (:constructor ,constructor ,accessors))
                            ,@(if (and (= 1 (length slots))
                                       (symbolp (first slots)))
                                  `((,constructor nil :type ,(first slots)))
                                  (map-each (slot slots)
                                    `(,(first slot) ,(third slot) :type ,(second slot)))))))))))))

Отображается в plain CL это так:

;;; Натуральные числа

                ;; Nat ~ Zero + Nat*
(macroexpand-1 '(defdata nat zero (succ nat)))
;=>
(PROGN
 (DEFTYPE NAT () '(OR ZERO SUCC))
 (PROGN (DEFSTRUCT (ZERO (:CONSTRUCTOR ZERO NIL))) (DEFPARAMETER ZERO (ZERO)))
 (DEFSTRUCT (SUCC (:CONSTRUCTOR SUCC (SUCC))) (SUCC NIL :TYPE NAT)))

(succ (succ zero))
;=>
#S(SUCC :SUCC #S(SUCC :SUCC #S(ZERO)))

;;; Гетерогенное дерево

                ;; Tree ~ Any + Tree * Tree
(macroexpand-1 '(defdata tree (leaf t) (branch (left tree) (right tree))))
;=>
(PROGN
 (DEFTYPE TREE () '(OR LEAF BRANCH))
 (DEFSTRUCT (LEAF (:CONSTRUCTOR LEAF (LEAF))) (LEAF NIL :TYPE T))
 (DEFSTRUCT (BRANCH (:CONSTRUCTOR BRANCH (LEFT RIGHT)))
   (LEFT NIL :TYPE TREE)
   (RIGHT NIL :TYPE TREE)))

(branch (leaf "string") (leaf 1000))
;=>
#S(BRANCH :LEFT #S(LEAF :LEAF "string") :RIGHT #S(LEAF :LEAF 1000))

Аналогом instance Show, Instance Eq и т.д. будет написание методов для этих типов, например:

(defmethod print-object ((leaf leaf) stream)
  (format stream "~A" (leaf-leaf leaf)))

(defmethod print-object ((branch branch) stream)
  (format stream "<~A . ~A>" (branch-left branch) (branch-right branch)))

(branch (leaf "string") (leaf 1000))
;=>
<string . 1000>

Вот и получается - пока мы «внутри» языка, никаких s-expression и макросов нет, есть настоящии типы, классы, методы, динамические и статические привязки, ну и так далее; но если мы хотим выйти «наружу» ,на метауровень, и говорить в терминах расширений CL, то тогда начинаются эти s-выражения и макросы. Например, чтобы сделать defdata - их пришлось привлечь, но «внутри» когда мы начинаем делать эти data - вводятся обычные примитивы (типы, объекты, классы и функции, никаких «списков и символов»).

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

я не понял твой код и откуда взялось macro

Имеется ввиду, что

macro(1);

/* даст */

typedef struct { ... } Object;
#define TYPE(O) ...

static inline fn_1 (Object *object_1) { ... }

SomeType
foo (Object *object_1) {
  SomeType result;
  switch (TYPE(object)) {
    case TYPE_1 : result = fn_1(object_1); break;
  }
  return result
}
macro(3);

/* даст */

typedef struct { ... } Object;
#define TYPE(O) ...

static inline fn_1 (Object *object_1) { ... }
static inline fn_2 (Object *object_1, Object *object_2) { ... }
static inline fn_3 (Object *object_1, Object *object_2, Object *object_3) { ... }

SomeType
foo (Object *object_1, Object *object_2, Object *object_3) {
  SomeType result;
  switch (TYPE(object)) {
    case TYPE_1 : result = fn_1(object_1); break;
    case TYPE_2 : result = fn_2(object_1, object_2); break;
    case TYPE_3 : result = fn_3(object_1, object_2, object_3); break;
  }
  return result
}

Но это плохой пример, давай лучше (АТД -> генерирует функцию-катаморфизм избавленную от страшных рекурсий) покажи :)

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

лучше разберемся — какой из лисповских макросов неудобно или невозможно представить в виде partial evaluation какого-то кода? предполагаем, что partial-eval действует разумно, не не гениально

Нужно отвлечься от CL или C++ и (чего уж там) рассматривать любые языки вообще :)

1) Полагаем, что нам нужны только контекстно-свободные языки. Их можно разбирать грамматиками, задающимися продукциями Хомского.

2) Есть дерево вывода, т.е. AST (синтаксическое и дерево).

3) Его можно представлять в виде АТД:

-- |
-- Для чистого лямбда-исчисления
--
data LCTerm = Symbol | Lambda [Symbol] LCterm | Application LCTerm [LCTerm]

-- Lambda [Symbol] LCterm и Application LCTerm [LCTerm] это сахар, т.е. предполагается каррирование

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


type Symbol = String

data InfixOp = Plus | Minus | Prod | Div -- etc.

-- |
-- define simple AST (Term there)
--        -- ** - primitive types
data Term = -- * - atomic terms
            Token  Symbol
          | String String
          | Bool   Bool
          | Number Integer
            -- * - list/array terms
          | List       [Term]
          | DottedList [Term] Term
            -- etc.
          -- ** - special forms
          | BinaryOp   Term InfixOp Term
          | IfThenElse Term Term    Term
            -- etc.
          -- ** - functional terms
            -- * - foreign functional terms
          | PrimitiveFunction ([Term] -> ThrowsError   Term)
          | IOFunction        ([Term] -> IOThrowsError Term)
            -- * - abstract functional terms
          | Function { variables :: [Symbol], body :: [Term], closure :: Env}
          | Application { functional :: Term, arguments :: [Term]

4) У такого дерева есть функция свёртки с типом

:: ряд функций для каждого конструктора -> Term -> Term

тип функций для конструкторов:

:: ряд типов параметров -> тип конструктора

5) Какие функции возникают над таким АТД:

чтения

parse :: String -> AST
read  :: IO     -> AST

печати

show  :: AST -> String
print :: AST -> IO

Далее есть счётное множество функций с типами AST -> AST, главная:

Eval :: AST -> AST

то, что из себя представляет эта функция (которая выражается через ту свёртку) определяет семантику языка, в том числе стратегию вычисления (энергичную, ленивую и т.д.), именно в этой функции появляются семантические атрибуты у узлов синтаксического дерева. Иначе говоря Eval - естественный вычислитель в терминах самого языка, т.е. интерпретатор. Если постороить другую модель вычислителя (например, регистрово-стековую машину) то можно постороить отображение этой Eval в такую модель и это уже не прямая интерпретация, а трансляция (компиляция). Вот во время такой трансляции может быть удобно строить и графы на том дерево и в SSA его переводить и что угодно ещё.

Далее, из Eval путём каких-то преобразований может быть построин счётный класс функций {PartialEval} :: AST -> AST. Какого исключение класса {PartialEval} из всего множества функций с типом AST -> AST? Мне почему-то очевидно, что непустое. Вот все остальные такие функции - макросы. Функции {PartialEval} являются макросами тоже? Мне почему-то опять очевидно, что да.

Технически это «да» тоже должно быть очевидно:

Term* read(Stream stream) {
  -<- хук на таблицу в которой одна за другой лежат пользовательские
  -<- процедуры чтения/разбора.
  продолжение - стандартный читатель/парсер
}
+ процедура, которая добавляет в таблицу пользовательскую функцию чтения

= макросы чтения.

Term* eval(Term* Term) {
  -<- хук на таблицу в которой одна за другой лежат пользовательские
  -<- процедуры трансляции кода (:: Term -> Term).
  продолжение - стандартный вычислитель, или интерфейс к компилятору.
}
+ процедура, которая добавляет в таблицу пользовательскую функцию трансляции кода

= макросы.

И последнее, весьма важное, замечание. Все эти рассуждения это рассуждения в рамках host языка (или мета-языка) на котором мы делаем модельный язык. Функции target языка, это обычные элементы того АТД - PrimitiveFunction, IOFunction и Function, а вот счётное множество AST -> AST это предметы мета-языка. Что значит наличие в target языке средств PartialEval - это доступ из target языка к классу функций {PartialEval} :: AST -> AST из метаязыка. А наличие в target языке макрсов - доступ вообще к любым функциям типа AST -> AST из мета-языка.

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

А наличие в target языке макрсов - доступ вообще к любым функциям типа AST -> AST из мета-языка.

Соответсвенно, АТД того языка должно быть дополнено

data Term = 
          ...
            -- * - abstract functional terms
          | Function { variables :: [Symbol], body :: [Term], closure :: Env}
          | Application { functional :: Term, arguments :: [Term]
            -- * - meta terms
          | Macro { templates :: [Term], translator :: [Term], macro-closure :: MacroEnv }
          | Reader { firstString :: String, reader :: String -> Term }

И это уже требует от реализации языка с таким АТД, задающим AST (+ семантические атрибуты, которые просматриваются), такой реализации eval (на host языке!), который бы зависел от определений Macro из MacroEnv из target языка, т.е. поведение eval из реализации зависит от того что именно написано в коде целевого языка (это мета-eval). Это и есть макросы в духе CL. Аналогично реализация read из host языка зависит от определений Reader в target языке, и поведение read из реализации зависит от того что написано в коде целевого языка (уф, это мета-read :)). Но в предыдущем посте это тоже есть (техническое «да» где).

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto
-         | Macro { templates :: [Term], translator :: [Term], macro-closure :: MacroEnv }
+         | Macro { templates :: [Term], translator :: [Term], macroClosure :: MacroEnv }
quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

> Но это плохой пример, давай лучше (АТД -> генерирует функцию-катаморфизм избавленную от страшных рекурсий) покажи

да, это хороший пример, и там, можно сказать, случится облом

но учти, я за ридеровские макры или че-то похожее

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

tl;dr

пожалей свое время

Мне почему-то очевидно, что непустое

я напишу набросок доказательства обратного для (гигиеничных?) макр, исходя из него можно искать контрпример

только попозже или завтра

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

я за ридеровские макры или че-то похожее

И мнение не хочешь поменять? Как я выше уже написал - ридеровские макросы это хук на read позволяющий определять _любые_ пользовательские процедуры чтения (тект -> термы), обычные макросы - хук на eval (или compile) позволяющий определять _любые_ пользовательские процедуры трансформации (терм -> терм). Т.е. какая разница? Чем хук на read круче хука на eval/compile? По момему просто два разных инструмента.

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

доказательства

Вот, для меня это o_0 потому что «очевидно» это как «и слону понятно», никак не «доказательство». Ну ладно, посмотрю - если напишешь.

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

не доказательство, а наборосок оного; и вообще это снова откладывается

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

> Как я выше уже написал - ридеровские макросы это хук на read позволяющий определять _любые_ пользовательские процедуры чтения (тект -> термы), обычные макросы - хук на eval (или compile) позволяющий определять _любые_ пользовательские процедуры трансформации (терм -> терм). Т.е. какая разница? Чем хук на read круче хука на eval/compile? По момему просто два разных инструмента.

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

хотя на самом деле там вовсе не текст -> термы, а допустим текст -> AST

терм -> терм это безусловно harmful

компилятор это тоже делает, допустим при оптимизации, но лезть туда грязными лапами категорически воспрещается; если, допустим, нужен «открытый фреймовк для оптимизации», то там будет немного посложнее, чем просто макросы, т.к. 1. преобразования некоммутативны 2. граф применения преобразований часто неограничен или очень велик, и нужны эвристики для последовательности применения

че-то я смотрю, народ в дискуссии не участвует, может нам в жаббер перенести ее?

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

че же там стоит после стрелки вместо термов — весьма важный вопрос

в лиспе необходимость *иметь* возможность «все интрпретировать» приводит к тому, что там ниче кроме термов не может быть; но современные компилируемые языки в такую модель не вписываются и требуют более сложную (и имхо более удобную программисту)

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

Что-то я смотрю градус бреда в этой теме уже превышает всякие пределы.

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

И где эта необходимость прописана? А то вот SBCL нихрена об этой необходимости не знает, и до недавнего времени вообще интерпретатора не имел.

но современные компилируемые языки в такую модель не вписываются и требуют более сложную (и имхо более удобную программисту)

CL - самый современный компилируемый язык. В нем компиляция аж в стандартную библиотеку включена.

Может хватит уже нести бред, совершенно не разбираясь в предмете(в CL)?

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

хотя на самом деле там вовсе не текст -> термы, а допустим текст -> AST

Пока небольшое уточнение - «терм» это тоже не из лиспов а из лямбда-исчисления. Не нахожу более точного термина. Да, под термами я и имею ввиду любой объект AST.

терм -> терм это безусловно harmful

Макротрансформации это ранняя стадия призваная работать с AST как с данностью. Т.е. мы восстановили AST из текста (и не забыли про макросы чтения) мы поработали с AST (макросы, т.е. ввели себе специальные формы, например - нет в CL средства инкрементальной компиляции систем, а мы строим ASDF и определяем макрос defsystem - удобную специальную форму для определения систем, и все довольны).

компилятор это тоже делает, допустим при оптимизации, но лезть туда грязными лапами категорически воспрещается;

Привильно, defmacro вовсе туда и не лезет. Оно даёт работать с AST, оно даёт притянуть сюда «оптимизации», в смысле того что мы что-то упростили в AST, или что-то сгенерировали из DSL (как у swizard-а, в соседней теме было). В SBCL есть и более позние формы, определяющий трансформации - define-source-transformation, deftransform, defoptimizer, def-ir1-transform (примерно, по памяти) и так далее - вот это уже действительно internals и лезть туда нужно только при соответсвующей необходимости.

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

А то вот SBCL нихрена об этой необходимости не знает, и до недавнего времени вообще интерпретатора не имел.

Интерпретатор там был с самого начала - ещё в CMU CL. И одним из нововведений форка (~ 10 лет назад) была не интерпретация, а компиляция форм в top-level по умолчанию. Это как раз на тему - иногда бывает удобнее интерпритировать (eval как интерпретирующая процедура - CL на CL), а иногда компилировать (eval как интерфейс к компилятору, который строит отбражения CL кода в терминах регистрово-стекового вычислителя). Там на этот счёт как раз есть *параметр*.

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