peephole macros
От: xeno.by xeno.by
Дата: 28.09.11 21:03
Оценка:
В свете недавнего обсуждения механизмов работы линка
Автор: xeno.by
Дата: 28.09.11
, пришла мысль обобщить идею Скальского на тему декларативной активации макросов. Не пинайте, если сморожу глупость — я с макросами Немерле знаком совсем недолго.

Итак, к делу. Дополнительно к явному вызову макросов (функционально или атрибутами), возможно, имеет смысл реализовать автоматическую активацию макросов по шаблону. Скажем, встретился в коде вызов <[ $(foo: Foo).Bar(..$args) ]> — вызываем для него макрос, который, возможно, решит немножко подпилить или вообще заменить этот вызов. Некоторым образом, это напоминает лисповский defadvice, но мы не обязаны ограничиваться только подпиливанием определенных методов. С помощью этой фичи можно было бы реализовать "честную" поддержку LINQ — обращения к методам IQueryable автоматически бы вызывали преобразование выражений Немерле в выражения сишарпа.

Какие здесь могут быть проблемы? Будет ли это практично в реализации?
Re: peephole macros
От: VladD2 Российская Империя www.nemerle.org
Дата: 28.09.11 22:44
Оценка:
Здравствуйте, xeno.by, Вы писали:

XB>Итак, к делу. Дополнительно к явному вызову макросов (функционально или атрибутами), возможно, имеет смысл реализовать автоматическую активацию макросов по шаблону. Скажем, встретился в коде вызов <[ $(foo: Foo).Bar(..$args) ]> — вызываем для него макрос, который, возможно, решит немножко подпилить или вообще заменить этот вызов. Некоторым образом, это напоминает лисповский defadvice, но мы не обязаны ограничиваться только подпиливанием определенных методов. С помощью этой фичи можно было бы реализовать "честную" поддержку LINQ — обращения к методам IQueryable автоматически бы вызывали преобразование выражений Немерле в выражения сишарпа.


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

Вот когда в Н2 будет поддержка декларативного описания зависимостей между типами, то (я надеюсь) мы сможем это сделать. И никакой замены для этого не потребуется. Мы просто опишем перегрузку для синтаксиса вызова метода и определим для нее свои правила типизации.

XB>Какие здесь могут быть проблемы? Будет ли это практично в реализации?


Да море. Проще разобраться как оно реально работает, чем объяснять.
Если в двух словах, то конструкция $(foo: Foo).Bar очень перегружена. И без специального кода процесс резрешения перегрузки просто провалится, так как на входе мы имеем ссылку на функцию (с не выведенными типами), а на выходе нам нужно получить дерево выражений. И еще нужно догадаться, что такое преобразование требуется.
def (delegate_tc, exprTree_tc, argsTys, ret) =
  if (IsFunctional(expr))
  {
    def (del, exprTree, args, ret) = TryExtractFunType(target.Hint);

    if (args != null && ret != null)
    {
      def fnTy = FixedType.Fun(TypeVar.FromList(args), args.Length, ret);
      def ok = expr.Type.TryRequire(fnTy);

      if (ok)
      {
        _ = expr.Type.Require(fnTy);
        (del, exprTree, args, ret)
      }
      else (null, null, null, null)
    }
    else (del, exprTree, args, ret)
  }
  else (null, null, null, null);

if (exprTree_tc != null && !SkipExpressionTreeConvertion)
{
  match (expr)
  {
    | TExpr.MacroEnvelope(_, _, TExpr.DefFunctionsIn([fn], _), _)
    | TExpr.DefFunctionsIn([fn], _) =>
      def <[ fundecl: $_name[ ..$typarms1] (..$args1) : $ty1 where ..$tyconstrs1 $body1 ]> = fn.Parsed;
      def trySetType(parm, ty)
      {
        when (parm.Type is PExpr.Wildcard)
          parm.Type = PExpr.TypedType(ty)
      }

      mutable retTy = ty1;

      when (!args1.IsEmpty && argsTys.Length == args1.Length)
      {
        NList.Iter2(args1, argsTys, trySetType);

        when (retTy is PExpr.Wildcard)
          retTy = PExpr.TypedType(ret);
      }

      def lamda = <[ fun [ ..$typarms1] (..$args1) : $retTy where ..$tyconstrs1 $body1 ]>;

      def expr2 = TryTyping(() =>
        TypeExpr(<[ Nemerle.Linq.ToExpression($lamda) ]>));

      if (IsError (expr2) || !expr2.Type.TryRequire(target))
        null
      else
      {
        // The source texpr can be not typed. Help type it...
        def fnTy  = FixedType.Fun(TypeVar.FromList(argsTys), argsTys.Length, ret);
        def ok    = fn.decl.Type.Require(fnTy);

        expr2.Type.ForceRequire(target);
        _ = ok;
        expr2
      }

    | _ => null
  }
}
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: peephole macros
От: xeno.by xeno.by
Дата: 29.09.11 05:52
Оценка:
Большое спасибо за детали! Получается, в сто раз проще сделать поддержку макросов, которые могут вызываться в объектном стиле (например, через аннотацию, или через object macro ...).
Re[3]: peephole macros
От: VladD2 Российская Империя www.nemerle.org
Дата: 29.09.11 15:44
Оценка:
Здравствуйте, xeno.by, Вы писали:

XB>Большое спасибо за детали! Получается, в сто раз проще сделать поддержку макросов, которые могут вызываться в объектном стиле (например, через аннотацию, или через object macro ...).


Да многое можно сделать. Только все это не так уж просто.

В Н2, думаю, мы будем поддерживать такие вещи как линк на уровне макросов (без хардкода в компиляторе). Но там будет очень не простое решение позволяющее описывать типизацию по непреобразованному АСТ. В Н1 такого нет. Есть только АМИ компилятора доступный из макросов. Через него такие вещи делать сложно (если вообще возможно).

По сути для реалзации LINQ в виде макроса нужно ввести специальный тип макроса — операция неявного приведения типов — расширение. Ну, как методы расширения и оператор приведения типов в одном флаконе. Это могло бы выглядеть примерно так:
// несинтаксический макрос описывающий оператор неявного приведения типа
macro method @:[T](expr : T) : Expression[T]
{
  Лифтим в Expression...
}

Этот макрос похож на оператор неявного приведения типов:
public static @:[T](expr : T) : Expression[T]
{
  преобразуем тип...
}

Компилятор будет включать в состав перегрузок в случаях когда требуется вставка неявного приведения типов.
Разрешение перегрузки будет происходить так же как для обычного метода, но в итоге, вместо вставки вызова метода будет вызвано тело макроса которое получит типизированное АСТ выражения (в параметре expr) и сможет произвести его лифтинг в АСТ деревьев выражений.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.