HigherLogics - мой блог, и я потратил много времени на изучение этого вопроса. Ограничением действительно является абстракция над конструкторами типов, иначе говоря, «дженерики над дженериками». Похоже, что лучшее, что вы можете сделать для имитации модулей ML и функторов, - это хотя бы одно (полубезопасное) приведение.
В основном все сводится к определению абстрактного типа и интерфейса, который соответствует сигнатуре модуля, работающей с этим типом. Абстрактный тип и интерфейс имеют общий параметр типа B, который я называю «брендом»; бренд - это всего лишь подтип, который реализует интерфейс модуля. Марка гарантирует, что переданный тип является правильным подтипом, ожидаемым модулем.
// signature
abstract class Exp<T, B> where B : ISymantics<B> { }
interface ISymantics<B> where B : ISymantics<B>
{
Exp<int, B> Int(int i);
Exp<int, B> Add(Exp<int, B> left, Exp<int, B> right);
}
// implementation
sealed class InterpreterExp<T> : Exp<T, Interpreter>
{
internal T value;
}
sealed class Interpreter : ISymantics<Interpreter>
{
Exp<int, Interpreter> Int(int i) { return new InterpreterExp<int> { value = i }; }
Exp<int, Interpreter> Add(Exp<int, Interpreter> left, Exp<int, Interpreter> right)
{
var l = left as InterpreterExp<int>; //semi-safe cast
var r = right as InterpreterExp<int>;//semi-safe cast
return new InterpreterExp<int> { value = l.value + r.value; }; }
}
}
Как видите, приведение в основном безопасно, поскольку система типов гарантирует, что марка типа выражения соответствует марке интерпретатора. Единственный способ это испортить, если клиент создает свой собственный класс Exp и указывает марку Interpreter. Существует более безопасное кодирование, которое также позволяет избежать этой проблемы, но оно слишком громоздкое для обычного программирования.
Я позже использовал эту кодировку и перевел примеры из одной из статей Олега, написанных на MetaOCaml, для использования C # и Linq. Интерпретатор может прозрачно запускать программы, написанные с использованием этого встроенного языка на стороне сервера в ASP.NET или на стороне клиента, как JavaScript.
Эта абстракция над интерпретаторами является особенностью окончательного кодирования Олега без тегов. Ссылки на его статью приведены в сообщении в блоге.
Интерфейсы являются первоклассными в .NET, и поскольку мы используем интерфейсы для кодирования сигнатур модулей, модули и сигнатуры модулей также являются первоклассными в этой кодировке. Таким образом, функторы просто используют интерфейс непосредственно вместо сигнатур модулей, т.е. они будут принимать экземпляр ISymantics и делегировать ему любые вызовы.