D中的特征可以用于类型类吗?

KIM*_*IMA 18 functional-programming d typeclass

我是D的新手,我正在寻找一种好的方法来编写类似Haskell的类型类,例如D中的Functors,Monoids等.

这是在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;}
}
Run Code Online (Sandbox Code Playgroud)

类型参数需要为Monoid的通用算法可以表示为:

template genericfunctions() {
    T TestMonoid(T,N = Monoid!T)(T a) {
        return N.mappend(N.mzero(),a);
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,如果要省略模板参数,则必须导入所有需要的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))); 
}
Run Code Online (Sandbox Code Playgroud)

你现在可以使用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));
    }
}
Run Code Online (Sandbox Code Playgroud)

一种算法看起来像这样:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

幸运的是,您可以在使用时省略四个模板参数:

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));
}
Run Code Online (Sandbox Code Playgroud)

但是当你想为Type使用另一个Functor实例时,你必须指定fmap的所有4个类型参数.有没有办法只需要指定实例,其他参数可以从中推导出来?

有没有替代笨拙的mixin解决方法?

这种方法还有其他缺点,我看不出来吗?

其他方式呢?

感谢您阅读这篇文章,并花时间思考和回答:)


编辑:

是否可以在D中定义像单元测试的仿函数法这样的约束?那肯定很不错.

Pet*_*der 4

template genericfunctions() {
  T TestMonoid(T,N = Monoid!T)(T a) {
    return N.mappend(N.mzero(),a);
  }
}
Run Code Online (Sandbox Code Playgroud)

不需要这样:

T TestMonoid(T,N = Monoid!T)(T a) {
  return N.mappend(N.mzero(),a);
}
Run Code Online (Sandbox Code Playgroud)

那应该足够了。有了这个,就没有必要了mixin

是否可以使用 D 中的单元测试来定义像函子法则这样的约束?

不完全确定我理解您的要求,但您可以使用模板函数/类定义约束:

void isEven(T)(T x) if (isIntegral!T) { return x % 2 == 0; }
Run Code Online (Sandbox Code Playgroud)

T如果是整型,该模板才会实例化。

请参阅模板页面底部的“模板约束”部分。