¿Se pueden usar rasgos en D para las clases de tipos?
Soy nuevo en D, y estoy buscando una buena manera de programar con clases tipo Haskell, p. Ej. Functores, monoides, etc. en D.
Se implementa algo como esto en Tango o Phobos?
He oído sobre rasgos que permiten la verificación de tipos en tiempo de compilación para ciertas propiedades. ¿Se pueden usar para clases de tipos?
He intentado un poco con la especialización de plantillas y se me ocurre esto:
// Monoid.d
// generic Monoid gets called when there is no instance of Monoid for Type T
class Monoid(T) {
pragma(msg, "Type is not a Monoid");
}
// Monoid instance for double
class Monoid(T:double) {
static T mzero() { return 0; }
static T mappend(T a, T b ) { return a + b;}
}
// Monoid instance for int
class Monoid(T:int) {
static T mzero() { return 0; }
static T mappend(T a, T b ) { return a + b;}
}
n algoritmo genérico cuyo parámetro de tipo debe ser un monoide podría expresarse como:
template genericfunctions() {
T TestMonoid(T,N = Monoid!T)(T a) {
return N.mappend(N.mzero(),a);
}
}
in embargo, si desea omitir los parámetros de la plantilla, debe importar todas las instancias de Monoid necesarias y mezclar elgenericfunctions
modelo
import Monoid;
import std.stdio;
import std.conv;
mixin genericfunctions;
void main() {
writefln(to!string(TestMonoid(3)));
writefln(to!string(TestMonoid(3.3243)));
}
Ahora puede usar entradas y dobles como monoides.
in embargo, las cosas se vuelven más complejas cuando tienes una clase de tipo como Functor cuyas instancias son genéricas:
module Functors;
// generic Functor like generic Monoid
class Functor(alias T, A) {
pragma(msg,"Not an instance of Functor");
}
// very simple container to demonstrate functors behavior
class FunctorTest(A) {
public A a;
this(A a) {
this.a = a;
}
}
// instance of Functor for FunctorTest!A
class Functor(alias T:FunctorTest,A) {
static T!B fmap(B)(T!A a, B delegate(A) fn) {
return new T!B(fn(a.a));
}
}
Un algoritmo se vería así:
template genericfunctions() {
T TestMonoid(T,N = Monoid!T)(T a) {
return N.mappend(N.mzero(),a);
}
// F is the Functor, A the functors type before,
// B the functors Type after, N is the instance of Functor
F!B fmap(alias F,A,B,N=Functor!(F,A))(F!A a, B delegate(A) fn) {
return N.fmap!B(a,fn);
}
}
fortunadamente, puede omitir los cuatro parámetros de plantilla cuando lo usa:
mixin genericfunctions;
void main() {
auto a = new FunctorTest!int(3);
auto b = fmap(a,(int b) {return b+ 0.5;});
writefln(to!string(b.a));
}
Pero cuando desee utilizar otra instancia de Functor para el Tipo, debe especificar los 4 parámetros de tipo de fmap. ¿Hay alguna forma en que solo necesite especificar la Instancia y los otros parámetros se puedan deducir de esto?
¿Existe una alternativa a la torpe solución de mezcla?
¿Hay otras desventajas de este enfoque que no veo?
¿Qué hay de otras formas?
Gracias por leer hasta aquí y por tomarse el tiempo para pensar y responder
Editar
¿Es posible definir restricciones como las leyes de functor con unittest en D? Eso seria muy bueno