www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - D's type classes pattern ?

reply "matovitch" <camille.brugel laposte.net> writes:
Hi,

It's been a long time since I coded some d code... sorry I take
the lazy way asking for advices. :D

Lets say I want to implement some generic algorithm. I would like
to checks the types passed to my algorithm implements a specific
interface.

interface IStuff(Stuff)
{
      void foo();
}

class TypeClass(T, I) : I(T)
{
      alias this T;
}

void myAwesomeAlgo(Stuff) (TypeClass!(Stuff, IStuff) stuff)
{
      stuff.foo();
}


Well it seems that I have worked out my question in trying to
formulate it...Would something like this work ?
Mar 24 2015
next sibling parent reply "matovitch" <camille.brugel laposte.net> writes:
More like :

import std.stdio;

interface IStuff(Stuff)
{
      void foo();
}

class TypeClass(T, I) : I(T)
{
      alias this stuff;
      T stuff;
}

void myAwesomeAlgo(Stuff) (TypeClass!(Stuff, IStuff) stuff)
{
      stuff.foo();
}

struct MyStuff
{
     void foo()
     {
         writeln("Hello World !");
     }
}

void main()
{
     alias TypeStuff = TypeClass!(MyStuff, IStuff);
     TypeStuff stuff;
     myAwesomeAlgo(stuff);
}

Doesn't compile btw :

kmeans.d(8): Error: members expected
kmeans.d(8): Error: { } expected following aggregate declaration
kmeans.d(8): Error: Declaration expected, not '('
kmeans.d(12): Error: unrecognized declaration
Mar 24 2015
parent reply "matovitch" <camille.brugel laposte.net> writes:
Well, just follow that link to the code...it almost compile : 
http://dpaste.com/3JNP0QD.
Mar 24 2015
parent reply "matovitch" <camille.brugel laposte.net> writes:
well, alias this is the issue here.

interface I
{
     void foo();
}

struct S
{
     void foo() {}
}

class C : I
{
     S s;
     alias this s;
}

don't compile...if you have any idea to do otherwise I am greatly 
interested. Thanks !
Mar 24 2015
parent "matovitch" <camille.brugel laposte.net> writes:
To resume my goal (last lonely message I promise), how can you 
statically check a type implement an interface without making 
this type inherit the interface (otherwise std.traits would do 
it) ?

ps : it seems to me that this is exactly what the haskell 
compiler do with type classes <- layman opinion
Mar 24 2015
prev sibling next sibling parent reply "weaselcat" <weaselcat gmail.com> writes:
On Tuesday, 24 March 2015 at 15:51:00 UTC, matovitch wrote:
 Hi,

 It's been a long time since I coded some d code... sorry I take
 the lazy way asking for advices. :D

 Lets say I want to implement some generic algorithm. I would 
 like
 to checks the types passed to my algorithm implements a specific
 interface.

 interface IStuff(Stuff)
 {
      void foo();
 }

 class TypeClass(T, I) : I(T)
 {
      alias this T;
 }

 void myAwesomeAlgo(Stuff) (TypeClass!(Stuff, IStuff) stuff)
 {
      stuff.foo();
 }


 Well it seems that I have worked out my question in trying to
 formulate it...Would something like this work ?
interface Foo{ } void Bar(T : Foo)(T t){ } but interfaces enable runtime polymorphism, you can just accept the interface itself void Fun(Foo foo){ }
Mar 24 2015
parent reply "matovitch" <camille.brugel laposte.net> writes:
On Tuesday, 24 March 2015 at 16:44:54 UTC, weaselcat wrote:
 On Tuesday, 24 March 2015 at 15:51:00 UTC, matovitch wrote:
 Hi,

 It's been a long time since I coded some d code... sorry I take
 the lazy way asking for advices. :D

 Lets say I want to implement some generic algorithm. I would 
 like
 to checks the types passed to my algorithm implements a 
 specific
 interface.

 interface IStuff(Stuff)
 {
     void foo();
 }

 class TypeClass(T, I) : I(T)
 {
     alias this T;
 }

 void myAwesomeAlgo(Stuff) (TypeClass!(Stuff, IStuff) stuff)
 {
     stuff.foo();
 }


 Well it seems that I have worked out my question in trying to
 formulate it...Would something like this work ?
interface Foo{ } void Bar(T : Foo)(T t){ } but interfaces enable runtime polymorphism, you can just accept the interface itself void Fun(Foo foo){ }
Thanks, just to be clear : void Bar(T : Foo)(T t){ } is the same as void Bar(T)(T t) if (is(T == Foo)){ } and it is checked only at compile time ? (for the runtime I know that what interface were meant for ;)).
Mar 24 2015
next sibling parent "matovitch" <camille.brugel laposte.net> writes:
Wait no ! In that case my type will have to inherit the 
interface...I don't want that, checking without inheriting...I 
know thats weird.
Mar 24 2015
prev sibling next sibling parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 03/24/2015 09:56 AM, matovitch wrote:

 just to be clear :

 void Bar(T : Foo)(T t){
 }
That means "if T can implicitly be converted to Foo".
 is the same as

 void Bar(T)(T t) if (is(T == Foo)){
 }
That means "if T is exactly Foo".
 and it is checked only at compile time ?
Yes to both. Ali
Mar 24 2015
prev sibling parent reply "anonymous" <anonymous example.com> writes:
On Tuesday, 24 March 2015 at 16:56:13 UTC, matovitch wrote:
 Thanks, just to be clear :

 void Bar(T : Foo)(T t){
 }

 is the same as

 void Bar(T)(T t) if (is(T == Foo)){
 }

 and it is checked only at compile time ? (for the runtime I 
 know that what interface were meant for ;)).
Ali already mentioned the difference between "==" and ":". In addition to that, template specializations (like `Bar(T : Foo)(T t)`) and template constraints (like `Bar(T)(T t) if(is(T : Foo))`) are similar but generally not interchangeable. A template with a specialization is considered a better match than one without. Whereas a template constraint doesn't add to the quality of the match. An example: module test; import std.stdio; void f(T)(T t) {writeln("generic");} void f(T : int)(T t) {writeln("with specialization");} void f(T)(T t) if(is(T : Object)) {writeln("with constraint");} void main() { f("some string"); /* -> "generic" */ f(42); /* -> "with specialization" */ version(none) f(new Object); /* Doesn't compile, because it matches both the generic version and the one with the constraint. */ }
Mar 24 2015
parent reply "matovitch" <cabrugel free.fr> writes:
Thanks for the precisions on template constraint and template
specialization...Indeed wath I want to do look like isInputRange
constraint. Haskell have something like :

//(Pseudo D-Haskell)

void foo(InputRange R)(R r);

//D equivalent

void foo(R)(R r) if (isInputRange(R));

Except they call them type classes instead of template constraint
and it is check at runtime instead of compile time for D.

I am curious to know how isInputRange is implemented since I
wanted to do kind of the same but I am afraid it's full of (ugly)
traits and template trickeries where haskell type classes are
quite neat and essentially a declaration of an interface.

Let say I want to be able to add the type my algo deal with...I
could do an isAddable wich looks if a+b compiles with traits...I
wondered if you could check statically that the type could
implement an interface *if it wanted to* that is, without
inheriting it...
Mar 25 2015
parent reply "bearophile" <bearophileHUGS lycos.com> writes:
matovitch:

 I am curious to know how isInputRange is implemented since I
 wanted to do kind of the same but I am afraid it's full of 
 (ugly)
 traits and template trickeries where haskell type classes are
 quite neat and essentially a declaration of an interface.
Take a look at the sources and learn. They are sometimes tricky to get right, but it's not a problem of ugly syntax.
 I wondered if you could check statically that the type could
 implement an interface *if it wanted to* that is, without
 inheriting it...
Template constraints don't require inheritance. Bye, bearophile
Mar 25 2015
parent "matovitch" <cabrugel free.fr> writes:
On Wednesday, 25 March 2015 at 08:55:14 UTC, bearophile wrote:
 matovitch:

 I am curious to know how isInputRange is implemented since I
 wanted to do kind of the same but I am afraid it's full of 
 (ugly)
 traits and template trickeries where haskell type classes are
 quite neat and essentially a declaration of an interface.
Take a look at the sources and learn. They are sometimes tricky to get right, but it's not a problem of ugly syntax.
 I wondered if you could check statically that the type could
 implement an interface *if it wanted to* that is, without
 inheriting it...
Template constraints don't require inheritance. Bye, bearophile
Yes I know that you don't need to inherit some InputRange interface to be able to check a user defined type is an input range. And template constraints are more powerful/general than type classes but it's harder to get right for the beginner. I will definetly look at the code anyway. I was looking at : interface IInputRange(InputRange) {...} class TypeClass(T,I) : I!T { alias t this; T t; } void foo(InputRange) (InputRange inputRange) if (isInstanciable(TypeClass!(InputRange, IInputRange)));
Mar 25 2015
prev sibling parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 03/24/2015 08:50 AM, matovitch wrote:

 Lets say I want to implement some generic algorithm. I would like
 to checks the types passed to my algorithm implements a specific
 interface.
I think you are looking for template constraints. Look at isInputRange's implementation: https://github.com/D-Programming-Language/phobos/blob/master/std/range/primitives.d#L143 Then you can do: void foo(Range)(Range range) if (isInputRange!Range) // <-- { // ... } I have some explanation and examples here: http://ddili.org/ders/d.en/templates_more.html#ix_templates_more.named%20template%20constraint Ali
Mar 24 2015