Здравствуйте, KeyKeeper, Вы писали:
KK>В принципе, основа решения мной получена. Проблема заключается в том, что я не могу избавиться от уродского дублирования типов ключей и значений "оборачиваемых" деревьев:
KK>def wrappedBST = TreeWrapper.Create.[BinarySearchTree[string, int], string, int](keyGenerator);
Если проблема только в этом, то можно сделать так:
def wrappedBST = TreeWrapper.Create.[BinarySearchTree[_, _], _, _](keyGenerator);
Но будет уродливое дублирование "_"

.
А вообще, то что ты хочешь добиться называется "rank-2 polymorphism" полиморфизм. В чистом виде его в Nemerle нет, так что как бы крут вывод типов не был, он не сможет позволить передавать в качестве параметра типов обобщенный тип вместе с параметрами типов. Ну, разве что с констрэйнами поизвращаться, как в твоем примере.
KK>По идее, компилиятору достаточно информации, чтобы уметь работать с таким вызовом или аналогичным (если я не прав, поправьте):
KK>// да, я знаю, что синтаксически неверны оба варианта...
KK>def wrappedBST = TreeWrapper.Create.[BinarySearchTree[string, int]](keyGenerator);
KK>// или
KK>def wrappedBST = TreeWrapper.Create.[BinarySearchTree, string, int](keyGenerator);
Не уверен, что прав на 100%. Тут лучше получить консультацию Nikov-а (который, похоже, всех собак в округе поел по этому поводу). Но полная реализация "rank-2 polymorphism" на дотнете невозможна. Можно реализовать только некоторые фишки.
KK>Иными словами, мой первый вопрос: поддерживает ли Nemerle "частичный вывод типов" (не знаю как правильно назвать)? Или же Nemerle как и C# эксплуатирует подход "всё или ничего"?
Это зависит от того, что понимается под термином "частичный вывод типов"

.
KK>Второй вопрос: можно ли решить мою задачу каким-то изящным способом, не применяя макросов?
Опять же сначала нужно понять что является конечной целью задачи? Если задача — создание универсальной фабрики, то она решается в сто раз проще. Нужно просто передавать функцию создающую нужный объект в качестве параметра вашей фабрике. Упрощенно это можно показать так. Меняем реализацию Create на:
public static Create[K, V](treeCtor : void -> ITree[K, V], keyGenerator : V -> K) : TreeWrapper[K, V]
{
TreeWrapper(keyGenerator, treeCtor())
}
А код его вызова:
def wrappedBST = TreeWrapper.Create(BinarySearchTree, keyGenerator);
Т.е. вместо параметра типов передаем функции конструктор который будет создавать тип.
Как бонус получим резкое ускорение создания дерева, так как будет избавимся от создания его экземпляров через Activator.CreateInstance(), который используется при указании констрэйна new(). Activator.CreateInstance — очень медленный. Если вызовов будет много, можно получить конкретные тормоза.
KK>Можно ли решить хотя бы с макросами (я пока с ними не знакомился, но что-то подсказывает, что они должны спасти)?
Макросами можно решить почти любую проблему. В частности можно написать макру которая будет вставлять все параметры типов автоматом и как надо. Вызов такой макры будет выглядеть похоже на предыдущий вариант:
Create(BinarySearchTree, keyGenerator)
но первый параметр она будет интерпретировать как тип, а не как конструктор. Ну, а результатом будет примерно такой код:
TreeWrapper.Create.[BinarySearchTree[string, int], string, int](keyGenerator)
Кода макроса будет примерно таким:
using Nemerle;
using Nemerle.Collections;
using Nemerle.Compiler;
using Nemerle.Compiler.Parsetree;
using Nemerle.Compiler.Typedtree;
using Nemerle.Text;
using Nemerle.Utility;
using System;
using System.Collections.Generic;
using System.Linq;
namespace MacroLibrary
{
macro Create(type : PExpr, func : PExpr)
{
CreateImpl.DoTransform(Macros.ImplicitCTX(), type, func)
}
module CreateImpl
{
public DoTransform(typer : Typer, type : PExpr, func : PExpr) : PExpr
{
Macros.DefineCTX(typer);
def typeVar1 = <[ $(typer.FreshTypeVar() : typed) ]>; // создаем переменную типа и запаковываем ее в выражение
def typeVar2 = <[ $(typer.FreshTypeVar() : typed) ]>;
<[ TreeWrapper.Create.[$type[$typeVar1, $typeVar2], $typeVar1, $typeVar2]($func) ]>
}
}
}
Макрос можно даже сделать независящим от количества параметров типов. Но это уже посложнее. Кроме того не ясно зачем все это делать

.
ЗЫ
Всегда есть более простые и эффективные решения. Но их можно подобрать только если знаешь конкретную задачу. Так что лучше начать от печки и описать — что привело ко всем этим наворотам.