Могут ли черты в D использоваться для классов типов?

Я новичок в 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? Было бы очень хорошо.

18
задан stakx supports GoFundMonica 3 January 2012 в 15:05
поделиться