February 23rd, 2012

glider

макросы + ооп

Возник интересный вопрос. Одной из наших идей относительно макросов было дать программистам возможность объявлять макросы внутри классов. Вот хорошая иллюстрация того, где это может быть нужно:
class Queryable[T, Repr](query: Query) {
  macro def filter(p: T => Boolean): Repr = scala"""
    val b = $newBuilder
    b.query = Filter($query, ${reify(p)})
    b.result
  """
}
В данном случае то, что макрос объявлен внутри класса-коллекции дает возможность вызывать его при помощи обычной инфиксной нотации (q.filter вместо Queryable.filter(q)). Еще один бонус такой фичи - прозрачная интеграция в язык. Без дополнительных усилий можно добавить макросы в существующие классы, сохраняя естественность нотации. Например, можно сделать Range.foreach макросом, который заинлайнится в цикл while, и это все не ломая обратную совместимость с уже существующим циклом for.

Звучит классненько, но есть одна проблема. Понятное дело, что вне квазицитаты (но внутри макроса), мы не можем сослаться на query потому, что для этого нужен инстанс класса, который существует только в рантайме, а макросы раскрываются во время компиляции. С другой стороны, если из макроса мы вызываем статический хелпер (который, например, объявлен в компаньоне), то все ок, ибо такой вызов вполне можно разрулить.

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