Здравствуйте, 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
}
}