You are viewing xeno_by

Excelsior - так ли нужна для счастья мощь макросов?

Feb. 15th, 2013

03:32 pm - так ли нужна для счастья мощь макросов?

Previous Entry Add to Memories Share Next Entry

Обязательна ли для счастья вся мощь манипуляции абстрактными синтаксическими деревьями или же достаточно менее мощного, но более формализованного решения? Эту тему мы на прошлой неделе обсуждали в гостях в udpn: http://udpn.livejournal.com/94146.html и фрагмент этого обсуждения я бы хотел вынести в этот пост.

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

Как народ юзает макросы? По этому поводу я пару месяцев назад писал саммари: http://scalamacros.org/news/2012/11/05/status-update.html. С того времени добавилось еще вот что интересное: 1) тайп провайдеры (http://docs.scala-lang.org/overviews/macros/typemacros.html), 2) программируемый вывод типов (http://docs.scala-lang.org/overviews/macros/inference.html).

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

Классический способ метапрограммирования в Скале - вычисления на типах и имплисит параметрах. Я здесь совсем не специалист, но люди достигают потрясающих результатов. Управляют тайп инференсом, разбирают типы на запчасти, запрещают или разрешают вызовы методов, и т.д. Тут может больше рассказать уважаемый ivan_gandhi. К сожалению, временами получается довольно тяжеловесная жесть: https://github.com/scalaz/scalaz/blob/59cfba6e8e293ec24c8b610ced057e0376c13a3f/core/src/main/scala/scalaz/Unapply.scala.

То, что мы пробовали вначале как основу для макросов, очень похоже на MacroML. Строго типизированные квазицитаты по имени reify (т.е. никаких тебе игр с биндингами), нет поддержки деструктурирования. В теории это, может, и выглядит хорошо (по разговору с Тахой - Walid Taha - я так понял, что он макросы в неконтролируемом смысле вообще не уважает), но на практике пока что народ не нашел способов юзать reify хоть отдаленно эффективно, т.к. почти всегда (см. use cases выше) нужны нетипизируемые квазицитаты (которые по отдельности смысла не имеют, а, будучи слепленными вместе, дают вменяемый результат).

К сожалению, это пока что все. Много времени уходит на реализацию, да и сейчас после двух недель багфиксов для 2.10.1, я буду скорее всего садиться за починку дурной, но очень важной детали реализации. Пока что мой план заключается в том, чтобы добиться легкости Немерле в реалиях нашего компилятора, а потом уже браться за новизну.

Comments:

[User Picture]
From:vit_r
Date:February 15th, 2013 03:37 pm (UTC)
(Link)
Макросы - это как ассемблерные вставки. В некоторых случаях позволяют творить чудеса, но очень легко превращают жизнь в ад. Если для чего-то без них не обойтись, значит язык не достаточно хорошо продуман.

IMVHO, конечно. Некоторый народ в лепёшку разобьётся, но потратит день, чтобы в три строчки записать то, что за пятнадцать минут можно записать в десяти.
(Reply) (Thread)
From:zhengxi
Date:February 15th, 2013 06:57 pm (UTC)
(Link)
Макросы - это как ассемблерные вставки.

Я как раз недавно думал написать макрос для c++ вставок :)
Чтобы макрос добавлял JNI boilerplate, сохранял файл, вызывал компилятор/линкер и подгружал DLL.
(Reply) (Parent) (Thread) (Expand)
[User Picture]
From:migmit
Date:February 15th, 2013 08:05 pm (UTC)
(Link)
> Если для чего-то без них не обойтись, значит язык не достаточно хорошо продуман.

Я это уже сколько лет твержу...
(Reply) (Parent) (Thread) (Expand)
[User Picture]
From:metaclass
Date:February 15th, 2013 03:45 pm (UTC)
(Link)
Таки прикрутили макросы к БД. Придется разгребать вашу скалу, после того, как со всей работой разгребусь :)
(Reply) (Thread)
[User Picture]
From:xeno_by
Date:February 16th, 2013 09:21 am (UTC)
(Link)
Если что, ребе, мой имейл в профиле =)
(Reply) (Parent) (Thread)
[User Picture]
From:stdray
Date:February 16th, 2013 10:10 am (UTC)
(Link)
Я так понимаю, долгожданные Type Providers из F#3.0 оказались никому не нужны в итоге?
(Reply) (Parent) (Thread) (Expand)
[User Picture]
From:krlz
Date:February 15th, 2013 04:12 pm (UTC)
(Link)
А что у вас с виртуализацией? Когда можно будет js генерить отя бы самому?
(Reply) (Thread)
[User Picture]
From:udpn
Date:February 15th, 2013 06:29 pm (UTC)
(Link)
Почитайте здесь посты вместе с комментариями. Это всё целиком про ту виртуализацию, которой для генерации JS достаточно. Непременно потребуйте от первых двух авторов кодца, а то они, кажется, забыли.
http://akuklev.livejournal.com/1032466.html
http://sorhed.livejournal.com/568495.html
http://udpn.livejournal.com/94146.html
(Reply) (Parent) (Thread)
[User Picture]
From:krlz
Date:February 16th, 2013 07:28 am (UTC)
(Link)
Слово виртуализация там не встречается ни разу.
(Reply) (Parent) (Thread) (Expand)
[User Picture]
From:xeno_by
Date:February 16th, 2013 09:22 am (UTC)
(Link)
Можешь подробнее про юзкейс?
(Reply) (Parent) (Thread) (Expand)
From:M E
Date:February 16th, 2013 12:17 am (UTC)
(Link)
> Обязательна ли для счастья вся мощь манипуляции абстрактными синтаксическими деревьями или же достаточно менее мощного, но более формализованного решения?

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

именно, похоже, что обязательным должно быть:

1. удобный экспорт синтаксического дерева, типов, аннотаций и т.п. в какой-то формат
2. удобный импорт снаружи всего этого или их частей

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

как частный случай п.1 получаем type provider-ы -- они однозначно нужны (скажем, генерация типов данных скалы по sql); п.2 -- наоборот, экспорт типов данных скалы в описание таблиц sql

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

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


чисто практический вопрос по ходу дела:

мне лично местами не нравится синтаксис скалы, и я хотел бы погенерить ее -- те средства, что даются в scala.reflect.api.Universe, достаточны для этого?

Edited at 2013-02-16 12:26 am (UTC)
(Reply) (Thread)
[User Picture]
From:xeno_by
Date:February 16th, 2013 05:30 pm (UTC)
(Link)
Погенерить каким образом? Можно пример?
(Reply) (Parent) (Thread) (Expand)
[User Picture]
From:xeno_by
Date:February 16th, 2013 05:36 pm (UTC)
(Link)
"Какой-то формат" это, кстати, интересная идея.

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

Но символы в деревьях нужны, т.к. с помощью них связываются определения идентификаторов и их использования. Поэтому один из вариантов фикса - разработать такое представление, к которому не привязаны невидимыми нитями кишки компилятора. Например, тупые кейс классы.
(Reply) (Parent) (Thread)
[User Picture]
From:xeno_by
Date:February 16th, 2013 10:10 pm (UTC)
(Link)
Кстати, а где именно не нравится синтаксис?
(Reply) (Parent) (Thread) (Expand)
From:M E
Date:February 16th, 2013 12:38 am (UTC)
(Link)
хех, зазевался и запостил ссылку -- в результате мой коммент market as spam (затем похоже xeno_by быстренько ее разбанил, но я комменты удалил, дабы не разводить дубликаты)

> программируемый вывод типов (docs.scala-lang.org/overviews/macros/inference.html).

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

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

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

Edited at 2013-02-16 12:50 am (UTC)
(Reply) (Thread)
[User Picture]
From:xeno_by
Date:February 16th, 2013 05:39 pm (UTC)
(Link)
А как вообще компилятор может вывести L? Значение L определяется только доменно-специфическими правилами. У нас ведь нет полноценного вывода типов как в Хаскелле, чтобы информация о требуемом типе протекала из строчки 7 в строчку 6 (второй сниппет кода в http://docs.scala-lang.org/overviews/macros/inference.html).
(Reply) (Parent) (Thread) (Expand)
From:M E
Date:February 16th, 2013 01:06 am (UTC)
(Link)
> Классический способ метапрограммирования в Скале - вычисления на типах и имплисит параметрах.

/me заранее принимает корвалольчик

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

> Строго типизированные квазицитаты по имени reify (т.е. никаких тебе игр с биндингами)

биндинг это ввод нового имени в локальный контекст? с одной стороны, да, биндинги нужны; с другой -- ну как обычно (можно получить плюсовые шаблоны)

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

вот мы и подошли к самому интересному -- можно ссылочки поконкретнее (не просто "см. выше")?

там "квазицитаты по отдельности смысла не имеют" из-за того, что квазицитатам требуются имена неких общих (разделяемых) сущностей (переменных) для коммуникации, или еще по какой другой причине?

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

эээ... ммм... а почему его там нет? от него же вроде ну совсем никаких пакостей ожидать нельзя, сплошной safe/typesafe? (я так понял, что деструктурирование = паттерн_матчинг_в_контексте_макросов)

> Много времени уходит на реализацию

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


Edited at 2013-02-16 01:14 am (UTC)
(Reply) (Thread)
[User Picture]
From:xeno_by
Date:February 16th, 2013 05:44 pm (UTC)
(Link)
По поводу имплиситов не все так плохо, но вычисления там производятся на экстравагантном языке, многословном и отличном от Скалы. Это не очень удобно, мне кажется.

На вопросы про биндинги и причину нужды в квазицитатах это ты в точку написал. Все так и есть.

А как типобезопасным образом деструктурировать AST? Вот скажем, у нас есть c.Expr[Int], то есть AST, имеющее тип Int. Как сопоставить какие-то типы его запчастям? Это AST может быть вызовом функции от неизвестного числа аргументов, может быть селектом какого-то филда у какого-то класса и так далее.
(Reply) (Parent) (Thread)
From:M E
Date:February 16th, 2013 01:31 am (UTC)
(Link)
> Обязательна ли для счастья вся мощь манипуляции абстрактными синтаксическими деревьями или же достаточно менее мощного, но более формализованного решения?

дело в том, что "вся мощь" заранее непредсказуема; т.е. если скажем на дженериках sort(my_container) гарантированно скомпилится при условии правильной сигнатуры sort, то с шаблонами с++, дающими почти "всю мощь", это не так -- сигнатура sort может быть подходящая, но где-то в *кишках* sort возникнет облом, *не описанный в сигнатуре* sort

это плохо, от этого пытаются избавиться (например, придумали концепты -- они позволяют уточнить сигнатуру sort), и это потенциально плохо и в макросах

для макросов возможны разные точки зрения, скажем:

1. пока что просто пишем макросы, вопросы их верификации решаем потом или спихиваем на программистов

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

я как-то больше склонен к 2 (и поэтому пытаюсь понять, че там напридумывали sorhed, akuklev или в lms для скалы)

видимо возможны и какие-то промежуточные варианты

Edited at 2013-02-16 01:34 am (UTC)
(Reply) (Thread)
[User Picture]
From:xeno_by
Date:February 16th, 2013 05:29 pm (UTC)
(Link)
Понятно. Это важный поинт, я согласен. Пока что у нас еще решаются детские проблемы реализации.
(Reply) (Parent) (Thread)
[User Picture]
From:ivan_gandhi
Date:February 16th, 2013 02:27 am (UTC)
(Link)
Да, роскошно.

Насчёт меня - You are kidding me. Я только учусь ещё этому делу. Т.к. сейчас происходит революция в программировании, то мы все примерно в одинаковом положении.

Со скалазями какая-то фигня. Я на днях выкинул из проекта. Проще самому написать маленькую вещь по мотивам скалазей, чем тащить весь этот ужас.
(Reply) (Thread)
[User Picture]
From:xeno_by
Date:February 16th, 2013 09:23 am (UTC)
(Link)
С чем именно там ужас? А еще, я слышал, в седьмом неплохо все модуляризовали. (Но сам не юзаю, поэтому просто говорю с чужих слов).
(Reply) (Parent) (Thread)
From:nivanych
Date:May 7th, 2013 06:17 am (UTC)
(Link)
Есть ещё такой взгляд.
Если уж предполагается достаточно частое использование макросов в языке, то незачем городить такие сложности, как язык Скала.
Сделать базу языка из просто-типа-лямбды с индуктивными типами, рекурсией и ленивыми вычислениями в compile time.
И работать дальше над хорошей библиотекой макросов, реализующих всякий полиморфизм, подтипизацию и остальное.
(Reply) (Thread)
[User Picture]
From:xeno_by
Date:May 7th, 2013 07:10 am (UTC)
(Link)
Звучит очень интересно. Вы знаете языки, которые реализованы таким способом? Крайне познавательно было бы познакомиться.

Еще как показывает опыт, есть определенный смысл работать над интеграцией макросов в уже существующий тайпчекер потому что получаются неожиданные пересечения традиционных фич с компайл-тайм вычислениями (например, раздел 4.3 в http://scalamacros.org/news/2013/04/22/let-our-powers-combine.html).
(Reply) (Parent) (Thread)
From:nivanych
Date:May 7th, 2013 09:50 am (UTC)
(Link)
Ну, идея типизированного лиспа очень не новая.
Вроде, dmzlj делал такое, но мало, кто обратил внимание.
(Reply) (Parent) (Thread) (Expand)