HigherLogics es mi blog, y he pasado mucho tiempo investigando esta cuestión. La limitación es, de hecho, la abstracción sobre los constructores de tipo, también conocidos como "genéricos sobre genéricos". Parece que lo mejor que puedes hacer para imitar los módulos ML y los funtores requiere al menos un lanzamiento (semi seguro).
Básicamente se trata de definir un tipo abstracto y una interfaz que corresponde a la firma del módulo que opera en ese tipo. El tipo abstracto y la interfaz comparten un parámetro de tipo B que denomino "marca"; la marca generalmente es solo el subtipo que implementa la interfaz del módulo. La marca asegura que el tipo pasado es el subtipo correcto esperado por el módulo.
// 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; }; }
}
}
Como se puede ver, el reparto es sobre todo seguro, ya que el sistema de tipos asegura la marca del tipo de expresión coincide con la marca del intérprete. La única forma de arruinar esto es si el cliente crea su propia clase Exp y especifica la marca de Intérprete. Existe una codificación más segura que también evita este problema, pero es demasiado difícil de manejar para la programación ordinaria.
Más tarde used this encoding and translated the examples de uno de los documentos de Oleg escritos en MetaOCAMl, para usar C# y Linq. El intérprete puede ejecutar de forma transparente programas escritos usando este lenguaje incrustado en el lado del servidor en ASP.NET o en el lado del cliente como JavaScript.
Esta abstracción sobre los intérpretes es una característica de la codificación final sin etiquetas de Oleg. Los enlaces a su documento se proporcionan en la publicación del blog.
Las interfaces son de primera clase en .NET, y como utilizamos interfaces para codificar firmas de módulos, los módulos y las firmas de los módulos también son de primera clase en esta codificación. Por lo tanto, los funtores simplemente usan la interfaz directamente en lugar de las firmas de los módulos, es decir. Aceptarían una instancia de ISymantics <B> y le delegarían cualquier llamada.
¡Gracias por el enlace! – t0yv0