Я новичок в D, и я ищу хороший способ программирования с использованием классов типов, подобных Haskell, например. Функторы, моноиды и т. Д. В D.
Реализовано ли что-то подобное в Tango или Phobos?
Я слышал о свойствах, которые позволяют проверять типы во время компиляции для определенных свойств. Могут ли они использоваться для классов типов?
Я немного попробовал со специализацией шаблонов и придумал следующее:
// 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;}
}
Общий алгоритм, параметр типа которого должен быть Monoid, может быть выражен как:
template genericfunctions() {
T TestMonoid(T,N = Monoid!T)(T a) {
return N.mappend(N.mzero(),a);
}
}
Однако, если вы хотите опустить параметры шаблона, вы должны импортировать все необходимые экземпляры Monoid и смешать шаблон genericfunctions
.
import Monoid;
import std.stdio;
import std.conv;
mixin genericfunctions;
void main() {
writefln(to!string(TestMonoid(3)));
writefln(to!string(TestMonoid(3.3243)));
}
Теперь вы можете использовать int и double как Monoids.
Однако все получается. более сложным, если у вас есть класс типа, например Functor, экземпляры которого сами по себе являются универсальными:
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));
}
}
Один алгоритм будет выглядеть следующим образом:
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);
}
}
К счастью, вы можете опустить четыре параметра шаблона, когда используете его:
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));
}
Но когда вы хотите чтобы использовать другой экземпляр Functor для Type, вы должны указать все 4 параметра типа fmap. Есть ли способ, которым вам нужно указать только экземпляр, и другие параметры могут быть выведены из этого?
Есть ли альтернатива неуклюжему обходному пути миксина?
Есть ли другие недостатки этого подхода, которые я не делаю. не вижу?
А как насчет других способов?
Спасибо, что дочитали до этого места и что нашли время подумать и ответить :)
Редактировать:
Можно ли определить ограничения, подобные законам функторов, с помощью unittest в D? Было бы очень хорошо.