August 28th, 2013

glider

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

Очень порадовал комментарий некоего схемиста [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