?

Log in

No account? Create an account

Эмуляция ключевого слова static из D на макросах - Excelsior

Aug. 28th, 2013

02:10 pm - Эмуляция ключевого слова static из D на макросах

Previous Entry Share Next Entry

Очень порадовал комментарий некоего схемиста [1] к посту про compile-time function execution в D.

Если вкратце, в D перед объявлением переменной можно написать ключевое слово static (например, static int r1000 = euler1(1000)) и это приведет к тому, что компилятор попытается посчитать правую часть объявления во время компиляции.

Увидев оригинальный пост, я сразу начал думать, как то же самое можно было бы наколбасить на макросах в Скале. В принципе, что-то похожее можно изобразить уже прямо сейчас, объявив euler1 как макрос, который потом раскроется во время компиляции и дальше понятно. Но, конечно, это далеко не так интересно, как static, т.к. таким образом встраиваемые функции: 1) нужно объявлять специальным образом, 2) в них нужно работать с абстрактными синтаксическими деревьями, а не с обычными значениями, 3) как следствие пункта 2, не получится шарить эти функции между компайл-таймом и рантаймом.

Впрочем, есть решение, которое позволяет изобразить в точности ту же самую семантику, которая доступна в D. Для этого нужно написать хитрый макрос, назовем его ct, который будет использоваться вот так: val r1000 = ct(euler1(1000)). При желании, можно завернуть этот макрос в аннотацию, чтобы все выглядело предельно похоже на D: @static val r1000 = euler1(1000).

Что же будет делать этот макрос? На каждый экспаншен он будет генерить по макросу и тут же эти макросы вызывать. Например:

ct(euler1(1000))

↓

{
  macro temp = lift(euler1(1000))
  temp
}
Крутота неописуемая. Жаль только, что в текущей реализации макросистемы в Скале такие фокусы не пройдут из-за требования раздельной компиляции макросов и кода, их использующего.

[1] http://www.reddit.com/r/programming/comments/cb14j/compiletime_function_execution_in_d/c0rcqac

Tags: ,

Comments:

From:Valentin Budaev
Date:August 28th, 2013 12:58 pm (UTC)
(Link)
А что происходит в D, если euler1 меняет глобальный стейт?
(Reply) (Thread)
[User Picture]
From:thedeemon
Date:August 28th, 2013 02:16 pm (UTC)
(Link)
Такую конпелятор не разрешит в компайл-тайме использовать.
(Reply) (Parent) (Thread)
[User Picture]
From:xeno_by
Date:August 28th, 2013 02:18 pm (UTC)
(Link)
Как он это определит? Есть ли какой-то список правил того, что можно и что нельзя делать в разрешенных функциях?
(Reply) (Parent) (Thread)
From:v_l_a_d
Date:August 28th, 2013 04:06 pm (UTC)
(Link)
http://dlang.org/function.html , см. раздел "Compile Time Function Execution (CTFE)"
(Reply) (Parent) (Thread)
From:Valentin Budaev
Date:August 28th, 2013 07:13 pm (UTC)
(Link)
Я как понял, оно функцию на самом деле не проверяет, а просто начинает ее выполнять до тех пор, пока не встретится запрещенная инструкция, если не встретилась - все ок, если встретилась - бросим ошибку, верно?
(Reply) (Parent) (Thread)
[User Picture]
From:xeno_by
Date:August 28th, 2013 07:14 pm (UTC)
(Link)
Я тоже так понял.
(Reply) (Parent) (Thread)
[User Picture]
From:thedeemon
Date:August 29th, 2013 06:40 am (UTC)
(Link)
Не должно. В D есть вывод и проверка чистоты функции. Можно пометить функцию как pure, и компилятор ее проверит, выдав ошибку, если она не.
(Reply) (Parent) (Thread)
From:Valentin Budaev
Date:August 29th, 2013 09:33 am (UTC)
(Link)
Я вот из этого исходил:

> Note that the above restrictions apply only to expressions which are actually executed.

То есть с одним аргументом она pure (и можно подставлять), а с другим - non-pure, уже нельзя.

Но это не плохо, хорошо даже - корректность гарантируется все равно, проверится все один фиг в компайлтайме, зато гибкость выше.
(Reply) (Parent) (Thread)
[User Picture]
From:xeno_by
Date:August 29th, 2013 09:35 am (UTC)
(Link)
:)
(Reply) (Parent) (Thread)
[User Picture]
From:thedeemon
Date:August 29th, 2013 10:25 am (UTC)
(Link)
А, и правда, судя по приведенному там примеру. Удивительно.
(Reply) (Parent) (Thread)
From:ex_juan_gan
Date:August 28th, 2013 06:32 pm (UTC)
(Link)
Аяяй. Прогресс уже на горизонте, но ещё не с нами.
(Reply) (Thread)
From:zhengxi
Date:August 28th, 2013 09:06 pm (UTC)
(Link)
1. в С++11 тоже такое обещали. Не знаю, если работает.

2. Помимо модных штук, которыми вы там занимаетесь, есть еще простой тупой препроцессинг.
И вот этот пример мне кажется больше для него подходит, чем для макросов.
Простой парсер скалы на скале (без проверки синтаксиса, но создающий из исходника иерархическое дерево, которое можно менять и превращать обратно в компилябельный исходник; назвать его гордым словом AST язык не поворачивается) это всего строк 300. gist.github.com/anonymous/6371180

А дальше тривиально делается препроцессор конкретно под эту фичу:
Прошлись по дереву 1 раз, заменили ct(...) на ???, сдампили обратно в исходник, внутренности ct(...) засунули в println(), дописали в исходник.
Скомпиляли, запустили. Что напечаталось записали на место ct(...).

Edited at 2013-08-28 09:10 pm (UTC)
(Reply) (Thread)
[User Picture]
From:xeno_by
Date:August 29th, 2013 05:33 am (UTC)
(Link)
1) Тут интересно, что схлопывание констант определяется не definition-site аннотацией, а управляется на use-site. Соответственно, становится возможным шарить функции между компайл-таймом и рантаймом. Также, класс функций, поддерживаемых дишечкой в этом плане, гораздо шире, чем в плюсах.

2) Есть мелочи вроде рассинхронизации со скаловским парсером (например, в новых версиях) или факта того, что ct ресолвится не как честный идентификатор из какого-то модуля, а просто как что-то по имени ct. Но самое главное тут в том, что замена ct(...) на ??? может сделать программу некомпилируемой.
(Reply) (Parent) (Thread)
From:Valentin Budaev
Date:August 29th, 2013 05:22 pm (UTC)
(Link)
У call-site есть свои плюсы - за счет того, что при определении такой static функции мы в явном виде работает с компайл-тайм окружением, потенциально можно разрешить сайд-эффекты и ничего не будет ломаться.
(Reply) (Parent) (Thread)
From:rssh
Date:August 29th, 2013 11:17 am (UTC)
(Link)
scala-way будет это написать интерпретатор скалы (принимающей AST) прямо в компиляторе. Кстати - если учесть что все уже распрасено то осталось написать сравнительно немного.
(Reply) (Thread)
[User Picture]
From:xeno_by
Date:August 29th, 2013 11:27 am (UTC)
(Link)
Судя по опыту реализации джит-компилятора для макросов, это совсем не немного. Но да, это, мне кажется, единственный способ сделать макросы действительно удобными.
(Reply) (Parent) (Thread)