开发者

Can traits in D be used for type classes?

I'm new to D, and I'm looking for a good way to program with Haskell-like type classes e.g. Functors, Monoids, etc. in D.

Is something like this implemented in Tango 开发者_JAVA技巧or Phobos?

I've heard about traits which enable compile-time type checking for certain properties. Can they be used for type classes?

I've tried a little bit with template specialization and come up with this:

// 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;}
}

A generic algorithm whose type parameter needs to be a Monoid could then be expressed as:

template genericfunctions() {
    T TestMonoid(T,N = Monoid!T)(T a) {
        return N.mappend(N.mzero(),a);
    }
}

However, if you want to omit the template parameters, you have to import all needed Monoid instances and mixin the genericfunctions template.

import Monoid;
import std.stdio;
import std.conv;
mixin genericfunctions;

void main() {
    writefln(to!string(TestMonoid(3))); 
    writefln(to!string(TestMonoid(3.3243))); 
}

You can now use ints and doubles as Monoids.

However things get more complex when you have a type class like Functor whose instances are itself generic:

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));
    }
}

One algorithm would look like this:

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);
    }
}

Luckily, you can omit the four template parameters when you use it:

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));
}

But when you want to use another Functor instance for the Type you have to specify all 4 type parameters of fmap. Is there a way in which you only need to specify the Instance and the other parameters could be deduced from this?

Is there an alternative to the clumsy mixin workaround?

Are there other disadvantages of this approach which I don't see?

What about other ways?

Thanks for reading this far and for taking the time to think and answer :)


Edit:

Is it possible to define constraints like the functor laws with unittest in D? That would be very nice.


template genericfunctions() {
  T TestMonoid(T,N = Monoid!T)(T a) {
    return N.mappend(N.mzero(),a);
  }
}

No need for that:

T TestMonoid(T,N = Monoid!T)(T a) {
  return N.mappend(N.mzero(),a);
}

That should suffice. With this, there's no need for the mixin either.

Is it possible to define constraints like the functor laws with unittest in D?

Not entirely sure I understand what you are asking for, but you can define contraints with template functions/classes:

void isEven(T)(T x) if (isIntegral!T) { return x % 2 == 0; }

This template will only then instantiate if T is an integral type.

See the 'Template Constraints' section at the bottom of the Templates page.


Rather than answer your question, as that would require understanding what you have said. I'm just going to ramble on about features of D that you are using and those that might be of use to you.

D doesn't have Type Classes (as you know). Instead it has type specialization (which you are using) and template constraints. Type specialization came prior to template constraints and can in fact be used there.

A template constraint allows you to require certain properties of a type. You will find this is heavily used in std.range and there are templates which help write such constraints in std.traits. I may do a more complicated example but for now, this accepts types which convert to int:

void myFunction(T)(T param) if(is(T:int)) {
}


Is it possible to define constraints like the functor laws with unittest in D? That would be very nice.

Phobos has another concept on top of the language, as monoids and functors and monads would be. And that is Ranges. Now the way Phobos is checking if a type is a range is by defining a template that checks if certain functions can be called on a type. If these functions are generic themselves, the answer of the template would depend on the compiler being able to find a method matching your type.

For refenrence, here's the typecheck for a ForwardRange (Link points to code with more docs):

template isInputRange(R)
{
    enum bool isInputRange = is(typeof(
    (inout int = 0)
    {
        R r = R.init;     // can define a range object
        if (r.empty) {}   // can test for empty
        r.popFront();     // can invoke popFront()
        auto h = r.front; // can get the front of the range
    }));
}

With that you can create a template constraint like so:

template isFunctor(Testant) {
    enum bool isFunctor = is(typeof(
        ()
        {
            Testant t = Testant.init;          // can instantiate that type
            auto result = t.fmap((Testant){}); // can call fmap on it with the type as parameter.
        }
}

Note the UFCS with fmap above, fmap still matches your decalaration.

Also note that it might be better to use structs instead of classes. Since they are value types and we can have Compile Time Function Execution (CTFE) in D, with clever usage of opCall you can use them as if they were functions themselves.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜