HigherLogics è il mio blog e ho dedicato molto tempo a indagare su questa domanda. La limitazione è in realtà astrazione rispetto ai costruttori di tipi, ovvero "generici rispetto ai generici". Sembra il meglio che puoi fare per imitare i moduli ML ei funtori richiedono almeno un cast (semi-sicuro).
Fondamentalmente si tratta di definire un tipo astratto e un'interfaccia che corrisponde alla firma del modulo che opera su quel tipo. Il tipo astratto e l'interfaccia condividono un parametro di tipo B che io definisco una "marca"; il marchio è generalmente solo il sottotipo che implementa l'interfaccia del modulo. Il marchio garantisce che il tipo passato è il sottotipo corretto previsto dal modulo.
// 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; }; }
}
}
Come si può vedere, il cast è in gran parte al sicuro, dal momento che il sistema di tipo assicura la marca del tipo espressione corrisponde al marchio dell'interprete. L'unico modo per rovinare tutto questo è se il cliente crea la propria classe Exp e specifica il marchio Interpreter. C'è una codifica più sicura che evita anche questo problema, ma è troppo ingombrante per la programmazione ordinaria.
Successivamente, used this encoding and translated the examples da uno degli articoli di Oleg scritti in MetaOCaml, per utilizzare C# e Linq. L'interprete può eseguire in modo trasparente programmi scritti utilizzando questo linguaggio server incorporato in ASP.NET o lato client come JavaScript.
Questa astrazione sugli interpreti è una caratteristica della codifica finale senza tag di Oleg. I collegamenti al suo articolo sono forniti nel post del blog.
Le interfacce sono di prima classe in .NET e poiché utilizziamo le interfacce per codificare le firme dei moduli, i moduli e le firme dei moduli sono di prima classe anche in questa codifica. Pertanto, i funtori utilizzano semplicemente l'interfaccia direttamente al posto delle firme dei moduli, ad es. accetterebbero un'istanza di ISymantics <B> e delegheranno le chiamate ad esso.
Grazie per il link! – t0yv0