An example of typeclasses/traits/protocols/concepts in D with interfaces

sighoya sighoya at gmail.com
Wed Dec 23 14:20:39 UTC 2020


```D
import std.stdio;

public interface Number(T)
{
     T add(T)(T n1,T n2);
     T sub(T)(T n1,T n2);
     T mul(T)(T n1,T n2);
     T div(T)(T n1,T n2);
}

public class DoubleNumber:Number!double
{
     static double add(double n1,double n2)
     {
         return n1+n2;
     }
     static double sub(double n1,double n2)
     {
         return n1-n2;
     }
     static double mul(double n1,double n2)
     {
         return n1*n2;
     }
     static double div(double n1,double n2)
     {
         return n1/n2;
     }
}


class IntNumber:Number!int
{
     static int add(int n1,int n2)
     {
         return n1+n2;
     }
     static int sub(int n1,int n2)
     {
         return n1-n2;
     }
     static int mul(int n1,int n2)
     {
         return n1*n2;
     }
     static int div(int n1,int n2)
     {
         return n1/n2;
     }
}

//Typeclasss instances
alias Instance(T:Number!int) = IntNumber;
alias Instance(T:Number!double) = DoubleNumber;


T squareCompiletime(T, alias Implementor = Instance!(Number!T))(T 
number)
{
     return Implementor.mul(number,number);
}

T squareRuntime(T)(T number,Instance!(Number!T) instance = new 
Instance!(Number!T))
{
     return instance.mul(number,number);
}

int main()
{
     writeln(squareCompiletime(3));
     writeln(squareRuntime(3));
     writeln(squareCompiletime(3.0));
     writeln(squareRuntime(3.0));
     return 0;
}
```

Some things that irk me:
1.) Can't we import aliases from other modules or am I just too 
dumb to see how?
2.) it would be nice to unscope the methods of a type, e.g. `T 
square(T, unscope alias Implementor = ...)` s.t. we don't have to 
prefix it all the time with Implementor, the same for the runtime 
version of square.

This should also work with operators, I've left them out here.

Clearly, language site implicit instance resolution is often more 
convenient, but D people like it more explicit. Moreover, 
language site instance resolution has to be implemented carefully 
to choose the appropriate instances with regard to the current 
scope (a.k.a global coherence problem) and most languages simply 
don't get that right, we can do it better, manually :).

I think it is also possible to require typeclass dependencies 
like in Rust, e.g. if A gets implemented then B also, or each 
Number must be Equatable:

`public interface Number(T) where Instance!(Eq!T)`


More information about the Digitalmars-d mailing list