xeno_by (xeno_by) wrote,
xeno_by
xeno_by

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

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

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

***

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

Генерация реифицированных типов работает следующим образом. Если кому-то нужно для тайп-параметра T заполучить реифицированный тип (т.е. чтобы каждый полиморфный вызов сохранял тайп-аргумент T), то он пишет: def foo[T](параметры блаблабла)(implicit t: TypeTag[T]) = [тело функции бла бла]. У этой записи есть синтаксический сахар, т.е. она обычно очень компактная, но сейчас не об этом. В итоге, если программист вызывает foo[Int](...), то компилятор сгенерирует TypeTag[Int]. Если вызывается foo[List[T] forSome { type T <: C }](...) или еще какая-нибудь жесть, компилятор все равно сгенерирует точное представление тайп-аргумента. Наконец, если функция foo расположена внутри метода bar[T] с тайптегом для T и вызывается как foo[T](...), то тайптег из bar протечет в вызов foo. Могу потом подробнее рассказать.

Раньше в Implicits.scala, слое компилятора, который отвечает за имплисит серч, был хардкод типа "if (tp.typeSymbol == definitions.TypeTagClass) synthesizeTypeTag". А теперь там вообще нифига нет. Просто в стандартном prelude у нас есть макрос "implicit def materializeTypeTag[T]: TypeTag[T]" (на самом деле, немного не так, но это технические детали). В итоге: убрали хардкод из тайпчекера (т.е. упростили и сделали красивее) + мне (не как аппликейшен девелоперу, а как мейнтейнеру компилятора) стало гораздо легче менять реализацию materialize, т.к. она теперь связана с API компилятора через интерфейс macro API, а не в стиле "можем использовать все, до чего импорты дотянутся".

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

Также отличной, но мой взгляд, является идея парсер макросов. Вынос левых кусочков синтаксиса, вроде list comprehensions, в отдельные модули представляется мне полезным. Но это вещь, над которой еще надо будет поэкспериментировать.

О. Еще. При помощи макросов можно одновременно и упростить, и обобщить deriving (для тебя это, вероятно, не новость, но для полноты можно упомянуть). Кое-что на эту тему было видно вот тут: http://docs.scala-lang.org/overviews/macros/inference.html, но есть и другой аспект - при помощи макросов можно генерить бойлерплейт-методы в классах. Даже при текущих убогих технологиях народ уже что-то фигачит: https://github.com/dicarlo2/ScalaEquals, а дальше можно будет просто поставить аннотацию на класс, и она сама все сделает.
Tags: macros2011, scala
Subscribe
  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic
  • 3 comments