www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - static interface

reply Leandro Lucarella <llucax gmail.com> writes:
Why not? ;)

I know you might want to hit me for bringing just another feature request
inspired in Google's Go, but please do read it without preconceptions,
because I think the features I'm suggesting are mostly already in D, but
in a less convenient way.

With ranges, D is already using some kind of duck-typing. It's implemented
with template, template constraints and __traits(compiles)/is(typeof())
mostly. The range module makes extensive use of this trick.

All those features are great, and general and useful for a lot of things
besides duck-typing. But (compile-time) duck-typing is a very nice feature
which might deserve a little magic to avoid the boilerplate (or at least
odd implementation).

What I'm suggesting is just some syntax sugar for compile-time
duck-typing. My suggestion is to make this (a real example from the range
module):

static interface InputRange(T) {
	bool empty();
	T front();
	void popFront();
}

size_t walkLength(InputRange range, size_t upTo = size_t.max)
{
	// implementation
}

struct Stride(T): InputRange(T) {
	// implementation
}


Be somehow equivalent to:

template isInputRange(R)
{
    enum bool isInputRange = is(typeof(
    {
        R r;
        if (r.empty) {}
        r.popFront;
        auto h = r.front;
    }()));
}

size_t walkLength(Range)(Range range, size_t upTo = size_t.max)
if (isInputRange!(Range))
{
	// implementation
}

struct Stride(T) if (isInputRange!(Range)) {
	// implementation
}


The former syntax is much pleasant for both defining 'static interfaces'
and writing algorithms for them.

I didn't thought about this too much, and I'm sure there are plenty of
holes to be worked out, but I wanted to throw the idea out there to see
what people think about it.

What do you think?

-- 
Leandro Lucarella (AKA luca)                     http://llucax.com.ar/
----------------------------------------------------------------------
GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145  104C 949E BFB6 5F5A 8D05)
----------------------------------------------------------------------
I always get the feeling that when lesbians look at me, they're thinking,
'*That's* why I'm not a heterosexual.'
	-- George Constanza
Nov 16 2009
next sibling parent Frank Benoit <keinfarbton googlemail.com> writes:
Leandro Lucarella schrieb:

 What do you think?
 
I like it.
Nov 16 2009
prev sibling next sibling parent bearophile <bearophileHUGS lycos.com> writes:
Leandro Lucarella Wrote:

 static interface InputRange(T) {
much about D, has asked me if in D there are static interfaces. I think he was thinking about something similar. I think that's a nice idea, or it can become a nice idea after some improvement. Bye, bearophile
Nov 16 2009
prev sibling next sibling parent reply Jesse Phillips <JesseKPhillips+D gmail.com> writes:
Is there a reason not to have all interfaces compile time checked? It would
still be allowable to state a class implements an interface and have the
compiler check it.

The reason I say this is it would remove the complexity of having the two types
of interfaces. And if you wanted to be able to check for the interface at
run-time the same interface can be used.
Nov 16 2009
parent Leandro Lucarella <llucax gmail.com> writes:
Jesse Phillips, el 16 de noviembre a las 13:19 me escribiste:
 Is there a reason not to have all interfaces compile time checked? It
 would still be allowable to state a class implements an interface and
 have the compiler check it.
 
 The reason I say this is it would remove the complexity of having the
 two types of interfaces. And if you wanted to be able to check for the
 interface at run-time the same interface can be used.
The problem is "regular" interfaces provide dynamic dispatch. Andrei could implement all the range stuff using interfaces, but that would mean: 1) You have to inherit from the interface (i.e., you can't use arrays) 2) All calls to ranges functions are virtual (inefficient; this is particularly relevant since they are called inside loops => lot of times) A static interface don't have those problems, and I don't see a way to mix static and dynamic interfaces without introducing a "new" type of interfaces (static interface). -- Leandro Lucarella (AKA luca) http://llucax.com.ar/ ---------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------- Si pudiera acercarme un poco más, hacia vos Te diría que me tiemblan los dos pies, cuando me mirás Si supieras todo lo que me costó, llegar Hoy sabrías que me cuesta respirar, cuando me mirás
Nov 16 2009
prev sibling next sibling parent reply grauzone <none example.net> writes:
Leandro Lucarella wrote:
 Why not? ;)
The actual question is if Andrei/Walter are interested at all in this. Because they didn't show any interest so far. D will probably be doomed to compile time bug-typing I mean duck-typing forever.
 static interface InputRange(T) {
 	bool empty();
 	T front();
 	void popFront();
 }
 
 size_t walkLength(InputRange range, size_t upTo = size_t.max)
 {
 	// implementation
 }
 
 struct Stride(T): InputRange(T) {
 	// implementation
 }
Or just: interface InputRange(T) { ... } struct Stride(T) : InputRange(T) { ... } size_t size_t walkLength(Range : InputRange)(Range range, ... The main purpose of the interface is to allow the compiler to do some type checking. (Interestingly, you could get an actual interface from a struct pointer. The compiler just needs to statically allocate a vtable (similar to a TypeInfo when writing typeid(T)). But I don't know if it'd be useful and is besides the point.) Actually I'd prefer your proposal, because walkLength could access only members that are actually in InputRange. But the declaration looks too much like an untemplated function. On the other hand, I remember that D2 once wanted to make this possible: void foo(static int x, int y) { ... } becomes void foo(int x)(int y) { ... } What happened to this? If it gets/got implemented, it would provide a nice opportunity to add syntax for such static interfaces.
Nov 16 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
grauzone wrote:
 Leandro Lucarella wrote:
 Why not? ;)
The actual question is if Andrei/Walter are interested at all in this. Because they didn't show any interest so far. D will probably be doomed to compile time bug-typing I mean duck-typing forever.
There's an embarrassment of riches when it comes to finding stuff to work on for D2. I think we need to resign ourselves to the idea that we need to focus only on one subset of the stream of good ideas that are being proposed. FWIW I was keen on structs interacting in interesting ways with interfaces (and submitted a number of enhancements in that directions) but then realized there are a number of problems that structs/interfaces cannot solve. I believe that a better path to pursue in checking interface conformance is via reflection. Andrei
Nov 16 2009
next sibling parent reply grauzone <none example.net> writes:
Andrei Alexandrescu wrote:
 grauzone wrote:
 Leandro Lucarella wrote:
 Why not? ;)
The actual question is if Andrei/Walter are interested at all in this. Because they didn't show any interest so far. D will probably be doomed to compile time bug-typing I mean duck-typing forever.
There's an embarrassment of riches when it comes to finding stuff to work on for D2. I think we need to resign ourselves to the idea that we need to focus only on one subset of the stream of good ideas that are being proposed.
That's understandable of course.
 FWIW I was keen on structs interacting in interesting ways with 
 interfaces (and submitted a number of enhancements in that directions) 
 but then realized there are a number of problems that structs/interfaces 
 cannot solve. I believe that a better path to pursue in checking 
 interface conformance is via reflection.
That's a possible solution (and an easy way out to get rid of language design problems), but I'm sure it's not the best solution. (It also should be a little bit extended to give the user better error messages.) I hope cleaner solutions will be considered later, possibly after D2 is done. Which are the problems you mentioned?
 
 Andrei
Nov 16 2009
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
grauzone wrote:
 Andrei Alexandrescu wrote:
 grauzone wrote:
 Leandro Lucarella wrote:
 Why not? ;)
The actual question is if Andrei/Walter are interested at all in this. Because they didn't show any interest so far. D will probably be doomed to compile time bug-typing I mean duck-typing forever.
There's an embarrassment of riches when it comes to finding stuff to work on for D2. I think we need to resign ourselves to the idea that we need to focus only on one subset of the stream of good ideas that are being proposed.
That's understandable of course.
 FWIW I was keen on structs interacting in interesting ways with 
 interfaces (and submitted a number of enhancements in that directions) 
 but then realized there are a number of problems that 
 structs/interfaces cannot solve. I believe that a better path to 
 pursue in checking interface conformance is via reflection.
That's a possible solution (and an easy way out to get rid of language design problems), but I'm sure it's not the best solution. (It also should be a little bit extended to give the user better error messages.) I hope cleaner solutions will be considered later, possibly after D2 is done. Which are the problems you mentioned?
I knew this was going to be asked. One issue I remember was that it's difficult to talk about "a struct must define a type with certain properties". You will say "but you can express that type as another interface" but that solves the problem by increasing the size of the feature. It gets even more complicated when you want to say "defines a type with these or these other properties, depending on the properties of this type parameter." I think that all can be solved through something that is very similar to C++ concepts, and I would want to avoid going that route. Andrei
Nov 16 2009
parent reply grauzone <none example.net> writes:
Andrei Alexandrescu wrote:
 grauzone wrote:
 Andrei Alexandrescu wrote:
 grauzone wrote:
 Leandro Lucarella wrote:
 Why not? ;)
The actual question is if Andrei/Walter are interested at all in this. Because they didn't show any interest so far. D will probably be doomed to compile time bug-typing I mean duck-typing forever.
There's an embarrassment of riches when it comes to finding stuff to work on for D2. I think we need to resign ourselves to the idea that we need to focus only on one subset of the stream of good ideas that are being proposed.
That's understandable of course.
 FWIW I was keen on structs interacting in interesting ways with 
 interfaces (and submitted a number of enhancements in that 
 directions) but then realized there are a number of problems that 
 structs/interfaces cannot solve. I believe that a better path to 
 pursue in checking interface conformance is via reflection.
That's a possible solution (and an easy way out to get rid of language design problems), but I'm sure it's not the best solution. (It also should be a little bit extended to give the user better error messages.) I hope cleaner solutions will be considered later, possibly after D2 is done. Which are the problems you mentioned?
I knew this was going to be asked. One issue I remember was that it's difficult to talk about "a struct must define a type with certain properties". You will say "but you can express that type as another interface" but that solves the problem by increasing the size of the feature. It gets even more complicated when you want to say "defines a type with these or these other properties, depending on the properties of this type parameter." I think that all can be solved through something that is very similar to C++ concepts, and I would want to avoid going that route.
In other words, a general solution inside the language would be too complex (because it had to support all sorts of type interfaces), so you'll just leave it to the user, who will implement the same thing with is() and friends?
 Andrei
Nov 16 2009
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
grauzone wrote:
 Andrei Alexandrescu wrote:
 grauzone wrote:
 Andrei Alexandrescu wrote:
 grauzone wrote:
 Leandro Lucarella wrote:
 Why not? ;)
The actual question is if Andrei/Walter are interested at all in this. Because they didn't show any interest so far. D will probably be doomed to compile time bug-typing I mean duck-typing forever.
There's an embarrassment of riches when it comes to finding stuff to work on for D2. I think we need to resign ourselves to the idea that we need to focus only on one subset of the stream of good ideas that are being proposed.
That's understandable of course.
 FWIW I was keen on structs interacting in interesting ways with 
 interfaces (and submitted a number of enhancements in that 
 directions) but then realized there are a number of problems that 
 structs/interfaces cannot solve. I believe that a better path to 
 pursue in checking interface conformance is via reflection.
That's a possible solution (and an easy way out to get rid of language design problems), but I'm sure it's not the best solution. (It also should be a little bit extended to give the user better error messages.) I hope cleaner solutions will be considered later, possibly after D2 is done. Which are the problems you mentioned?
I knew this was going to be asked. One issue I remember was that it's difficult to talk about "a struct must define a type with certain properties". You will say "but you can express that type as another interface" but that solves the problem by increasing the size of the feature. It gets even more complicated when you want to say "defines a type with these or these other properties, depending on the properties of this type parameter." I think that all can be solved through something that is very similar to C++ concepts, and I would want to avoid going that route.
In other words, a general solution inside the language would be too complex (because it had to support all sorts of type interfaces), so you'll just leave it to the user, who will implement the same thing with is() and friends?
I don't think that's a fair characterization. With reflection you can implement such capability in a library. Sometimes a motivated and well-intended feature could grow into a monster of generalization. This is what happened to C++ concepts and what could have happened to typedef if we hadn't clipped it. Andrei
Nov 16 2009
prev sibling parent reply Leandro Lucarella <llucax gmail.com> writes:
Andrei Alexandrescu, el 16 de noviembre a las 12:19 me escribiste:
 grauzone wrote:
Leandro Lucarella wrote:
Why not? ;)
The actual question is if Andrei/Walter are interested at all in this. Because they didn't show any interest so far. D will probably be doomed to compile time bug-typing I mean duck-typing forever.
There's an embarrassment of riches when it comes to finding stuff to work on for D2. I think we need to resign ourselves to the idea that we need to focus only on one subset of the stream of good ideas that are being proposed.
I completely agree and understand. I wish I had the time to make patches myself for the things I propose. But it's very different if something is not implemented because it sucks or just because the lack of men power :) If some good feature has Walter's blessing, maybe other person can implement it. Nobody wants to implement something if it doesn't have Walter's blessing, because we all know he's very tough to convince, and nobody wants to work on something it won't get accepted.
 FWIW I was keen on structs interacting in interesting ways with
 interfaces (and submitted a number of enhancements in that
 directions) but then realized there are a number of problems that
 structs/interfaces cannot solve. I believe that a better path to
 pursue in checking interface conformance is via reflection.
But that's very laborious, to be honest, I saw some points in std.range that would be hard to cover with static interfaces, but for those things you have all the flexibility you're already using. But static interfaces could cover a wide range of applications, making static duck-typing very easy to use. -- Leandro Lucarella (AKA luca) http://llucax.com.ar/ ---------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------- Breathe, breathe in the air. Don't be afraid to care. Leave but don't leave me. Look around and choose your own ground.
Nov 16 2009
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Leandro Lucarella wrote:
 Andrei Alexandrescu, el 16 de noviembre a las 12:19 me escribiste:
 grauzone wrote:
 Leandro Lucarella wrote:
 Why not? ;)
The actual question is if Andrei/Walter are interested at all in this. Because they didn't show any interest so far. D will probably be doomed to compile time bug-typing I mean duck-typing forever.
There's an embarrassment of riches when it comes to finding stuff to work on for D2. I think we need to resign ourselves to the idea that we need to focus only on one subset of the stream of good ideas that are being proposed.
I completely agree and understand. I wish I had the time to make patches myself for the things I propose. But it's very different if something is not implemented because it sucks or just because the lack of men power :) If some good feature has Walter's blessing, maybe other person can implement it. Nobody wants to implement something if it doesn't have Walter's blessing, because we all know he's very tough to convince, and nobody wants to work on something it won't get accepted.
What I can tell is that Walter's keenness on adding/changing something has a 10-fold improvement if a patch is present. For example Walter was sort of ambivalent about opDollar and opDollar!(dim)() until Don simply added it.
 FWIW I was keen on structs interacting in interesting ways with
 interfaces (and submitted a number of enhancements in that
 directions) but then realized there are a number of problems that
 structs/interfaces cannot solve. I believe that a better path to
 pursue in checking interface conformance is via reflection.
But that's very laborious, to be honest, I saw some points in std.range that would be hard to cover with static interfaces, but for those things you have all the flexibility you're already using. But static interfaces could cover a wide range of applications, making static duck-typing very easy to use.
There's a saying that most all girls may look good in the dark. Paraphrasing that, I'd say all language features look nice and easy when (a) detail is not discussed, (b) all the rest of the language is assumed to be mastered. There are places in D where we can't do what we want to do. Those are top priority. Those that do easier what we can do anyway (struct mixins, static interfaces, even the better switch which I actually personally like) are below top priority. The barrier is fuzzy because either I, Walter, or Don may suddenly catch fancy to a specific thing to work on, but I wish we were more, not less disciplined about that. Andrei
Nov 16 2009
prev sibling next sibling parent Bill Baxter <wbaxter gmail.com> writes:
On Mon, Nov 16, 2009 at 9:25 AM, Leandro Lucarella <llucax gmail.com> wrote:
 Why not? ;)

 I know you might want to hit me for bringing just another feature request
 inspired in Google's Go, but please do read it without preconceptions,
 because I think the features I'm suggesting are mostly already in D, but
 in a less convenient way.
I, for one, think these are great things to talk about. Even if it's decided that it's a bad idea for D to get such a feature, then as many people as possible should know that reason so we can talk intelligently about why D does things the way it does. Because if we're asking ourselves why D doesn't have X, you can bet there are others who will wonder the same when they see D. --bb
Nov 16 2009
prev sibling next sibling parent reply Bill Baxter <wbaxter gmail.com> writes:
On Mon, Nov 16, 2009 at 9:25 AM, Leandro Lucarella <llucax gmail.com> wrote=
:

This topic is actually very close to a discussion last week about
retrieving error messages from failures of __traits(compiles, xxx).

My question is: is it really that much of an improvement?

I've rearranged your code to see the equivalent snippets side-by-side:

 static interface InputRange(T) {
 =A0 =A0 =A0 =A0bool empty();
 =A0 =A0 =A0 =A0T front();
 =A0 =A0 =A0 =A0void popFront();
 }
 template isInputRange(R)
 {
    enum bool isInputRange =3D is(typeof(
    {
        R r;
        if (r.empty) {}
        r.popFront;
        auto h =3D r.front;
    }()));
 }
There's actually not that much difference here. Of course we would like several general improvements that have been discussed before: 1) some kind of template 'this' so we don't have to repeat the template nam= e. 2) something better than is(typeof()) (or __traits(compiles, ...)) to check if code is ok. [hoping for meta.compiles(...)] In some ways the current code is better, because it actually checks if a construct works or not, rather than requiring a specific function signature. Whether the code will work is really the minimal restriction you can place on the interface. A specific may be too tight. For instance, above you don't really care if empty returns bool or not. Just if you can test it in an "if".
 struct Stride(T): InputRange(T) {
 =A0 =A0 =A0 =A0// implementation
 }
 struct Stride(T) if (isInputRange!(T)) {
 =A0 =A0 =A0 =A0// implementation
 }
There's really not a significant difference in those two.
 size_t walkLength(InputRange range, size_t upTo =3D size_t.max)
 {
        // implementation
 }
 size_t walkLength(Range)(Range range, size_t upTo =3D size_t.max)
 if (isInputRange!(Range))
 {
        // implementation
 }
Here is the only one where I see a significant improvement. Basically that all comes from not having to repeat yourself. So maybe we'd be better off attacking that repetitiveness directly. But in terms of functionality it seems we cover pretty much everything static interface gives you. The only exception may be that static interfaces could give you better error messages about why your type doesn't satisfy the requirements. That's where better ways to get the error messages from __traits(compiles,...) errors come in. But it's harder to make that useful in D without giving up overloading. In D if T doesn't satisfy isInputRange!(T), it might satisfy some other overload of the template. --bb
Nov 16 2009
next sibling parent reply "Lars T. Kyllingstad" <public kyllingen.NOSPAMnet> writes:
Bill Baxter wrote:
 On Mon, Nov 16, 2009 at 9:25 AM, Leandro Lucarella <llucax gmail.com> wrote:
 
 This topic is actually very close to a discussion last week about
 retrieving error messages from failures of __traits(compiles, xxx).
 
 My question is: is it really that much of an improvement?
 
 I've rearranged your code to see the equivalent snippets side-by-side:
 
 static interface InputRange(T) {
        bool empty();
        T front();
        void popFront();
 }
 template isInputRange(R)
 {
    enum bool isInputRange = is(typeof(
    {
        R r;
        if (r.empty) {}
        r.popFront;
        auto h = r.front;
    }()));
 }
There's actually not that much difference here. Of course we would like several general improvements that have been discussed before: 1) some kind of template 'this' so we don't have to repeat the template name. 2) something better than is(typeof()) (or __traits(compiles, ...)) to check if code is ok. [hoping for meta.compiles(...)] In some ways the current code is better, because it actually checks if a construct works or not, rather than requiring a specific function signature. Whether the code will work is really the minimal restriction you can place on the interface. A specific may be too tight. For instance, above you don't really care if empty returns bool or not. Just if you can test it in an "if".
Another, related way in which the current code is better is that it doesn't care whether empty, front, and popFront are functions or variables. As we know, the standard trick for infinite ranges is to declare empty as an enum, not a function. BTW, I find it interesting that I see meta.* being mentioned in discussions and even used in code snippets all over the NG these days. It seems to be a popular proposal. I hope it gets implemented too. :) -Lars
Nov 17 2009
parent reply Leandro Lucarella <llucax gmail.com> writes:
Lars T. Kyllingstad, el 17 de noviembre a las 09:54 me escribiste:
In some ways the current code is better, because it actually checks if
a construct works or not, rather than requiring a specific function
signature.  Whether the code will work is really the minimal
restriction you can place on the interface.  A specific may be too
tight.  For instance, above you don't really care if empty returns
bool or not.  Just if you can test it in an "if".
Another, related way in which the current code is better is that it doesn't care whether empty, front, and popFront are functions or variables. As we know, the standard trick for infinite ranges is to declare empty as an enum, not a function.
Is not that hard to write: bool empty() { return false; } instead of enum bool empty = false;
 BTW, I find it interesting that I see meta.* being mentioned in
 discussions and even used in code snippets all over the NG these
 days. It seems to be a popular proposal. I hope it gets implemented
 too. :)
We all do ;) -- Leandro Lucarella (AKA luca) http://llucax.com.ar/ ---------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------- FALTAN 325 DIAS PARA LA PRIMAVERA -- Crónica TV
Nov 17 2009
next sibling parent reply "Lars T. Kyllingstad" <public kyllingen.NOSPAMnet> writes:
Leandro Lucarella wrote:
 Lars T. Kyllingstad, el 17 de noviembre a las 09:54 me escribiste:
 In some ways the current code is better, because it actually checks if
 a construct works or not, rather than requiring a specific function
 signature.  Whether the code will work is really the minimal
 restriction you can place on the interface.  A specific may be too
 tight.  For instance, above you don't really care if empty returns
 bool or not.  Just if you can test it in an "if".
Another, related way in which the current code is better is that it doesn't care whether empty, front, and popFront are functions or variables. As we know, the standard trick for infinite ranges is to declare empty as an enum, not a function.
Is not that hard to write: bool empty() { return false; } instead of enum bool empty = false;
That's why I mentioned infinite ranges. An infinite range is *defined* as a range where empty is implemented as enum bool empty = false; isInfinite(Range) tests whether this is the case, which it wouldn't be able to do if empty() was a function. -Lars
Nov 17 2009
next sibling parent Leandro Lucarella <llucax gmail.com> writes:
Lars T. Kyllingstad, el 17 de noviembre a las 15:12 me escribiste:
 Leandro Lucarella wrote:
Lars T. Kyllingstad, el 17 de noviembre a las 09:54 me escribiste:
In some ways the current code is better, because it actually checks if
a construct works or not, rather than requiring a specific function
signature.  Whether the code will work is really the minimal
restriction you can place on the interface.  A specific may be too
tight.  For instance, above you don't really care if empty returns
bool or not.  Just if you can test it in an "if".
Another, related way in which the current code is better is that it doesn't care whether empty, front, and popFront are functions or variables. As we know, the standard trick for infinite ranges is to declare empty as an enum, not a function.
Is not that hard to write: bool empty() { return false; } instead of enum bool empty = false;
That's why I mentioned infinite ranges. An infinite range is *defined* as a range where empty is implemented as enum bool empty = false; isInfinite(Range) tests whether this is the case, which it wouldn't be able to do if empty() was a function.
OK, there are way to fix that too, but I see your point now. -- Leandro Lucarella (AKA luca) http://llucax.com.ar/ ---------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------- "The Guinness Book of Records" holds the record for being the most stolen book in public libraries
Nov 17 2009
prev sibling parent Michel Fortin <michel.fortin michelf.com> writes:
On 2009-11-17 09:12:04 -0500, "Lars T. Kyllingstad" 
<public kyllingen.NOSPAMnet> said:

 That's why I mentioned infinite ranges. An infinite range is *defined* 
 as a range where empty is implemented as
 
    enum bool empty = false;
 
 isInfinite(Range) tests whether this is the case, which it wouldn't be 
 able to do if empty() was a function.
Which makes me think: what about a range that can but may not necessarily be infinite depending on runtime parameters? For instance, you could have a range returning a rational number as decimal digits: some will be infinite and others will be finite depending on the rational number (like 1/3 vs. 1/4). You can't express that with an enum. I guess you could fix that by overloading isInfinite for your specific range type though. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Nov 17 2009
prev sibling parent dsimcha <dsimcha yahoo.com> writes:
== Quote from Leandro Lucarella (llucax gmail.com)'s article
 Lars T. Kyllingstad, el 17 de noviembre a las 09:54 me escribiste:
In some ways the current code is better, because it actually checks if
a construct works or not, rather than requiring a specific function
signature.  Whether the code will work is really the minimal
restriction you can place on the interface.  A specific may be too
tight.  For instance, above you don't really care if empty returns
bool or not.  Just if you can test it in an "if".
Another, related way in which the current code is better is that it doesn't care whether empty, front, and popFront are functions or variables. As we know, the standard trick for infinite ranges is to declare empty as an enum, not a function.
Is not that hard to write: bool empty() { return false; } instead of enum bool empty = false;
Yes, but there's a reason to use enum: This convention enables compile time introspection as to whether the ranges are infinite. It's also more efficient when inlining is disabled.
Nov 17 2009
prev sibling parent reply Leandro Lucarella <llucax gmail.com> writes:
Bill Baxter, el 16 de noviembre a las 15:42 me escribiste:
 On Mon, Nov 16, 2009 at 9:25 AM, Leandro Lucarella <llucax gmail.com> wrote:
 
 This topic is actually very close to a discussion last week about
 retrieving error messages from failures of __traits(compiles, xxx).
 
 My question is: is it really that much of an improvement?
 
 I've rearranged your code to see the equivalent snippets side-by-side:
 
 static interface InputRange(T) {
        bool empty();
        T front();
        void popFront();
 }
 template isInputRange(R)
 {
    enum bool isInputRange = is(typeof(
    {
        R r;
        if (r.empty) {}
        r.popFront;
        auto h = r.front;
    }()));
 }
There's actually not that much difference here. Of course we would like several general improvements that have been discussed before: 1) some kind of template 'this' so we don't have to repeat the template name. 2) something better than is(typeof()) (or __traits(compiles, ...)) to check if code is ok. [hoping for meta.compiles(...)] In some ways the current code is better, because it actually checks if a construct works or not, rather than requiring a specific function signature. Whether the code will work is really the minimal restriction you can place on the interface. A specific may be too tight. For instance, above you don't really care if empty returns bool or not. Just if you can test it in an "if".
I think one could argue if being more restrictive is actually good or bad. Being more restrictive cold lead to less errors. Maybe if a struct empty() method returns, say, a pointer, I don't want to think it is *really* a range. Maybe that method is doing a lot of work and removing stuff, and then returning a pointer to some removed stuff. Anyway, I'm not saying that that couldn't happen even if empty() return bool (that's a risk when duck-typing, always :), I'm just saying I'm not so sure that being more restrictive is really a *bad thing*. And about what's nice or ugly, I agree the current way of doing stuff is not too bad, there are a few details that are not *that* nice. But with duck-typing I think the same that with tuples: those little details makes you feel that D doesn't support the concept so well, and make people resistant to use them. When something feels natural in a language, you use it all the time, for me, the current way to do tuples and duck-typing looks very artificial and harder than it should.
 But in terms of functionality it seems we cover pretty much everything
 static interface gives you.
I know that, I started this proposal just saying that =) Again, this proposal is about what I say in the previous paragraphs. -- Leandro Lucarella (AKA luca) http://llucax.com.ar/ ---------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------- Be nice to nerds Chances are you'll end up working for one
Nov 17 2009
parent Bill Baxter <wbaxter gmail.com> writes:
On Tue, Nov 17, 2009 at 5:51 AM, Leandro Lucarella <llucax gmail.com> wrote=
:
 Bill Baxter, el 16 de noviembre a las 15:42 me escribiste:
 On Mon, Nov 16, 2009 at 9:25 AM, Leandro Lucarella <llucax gmail.com> wr=
ote:
 This topic is actually very close to a discussion last week about
 retrieving error messages from failures of __traits(compiles, xxx).

 My question is: is it really that much of an improvement?

 I've rearranged your code to see the equivalent snippets side-by-side:

 static interface InputRange(T) {
 =A0 =A0 =A0 =A0bool empty();
 =A0 =A0 =A0 =A0T front();
 =A0 =A0 =A0 =A0void popFront();
 }
 template isInputRange(R)
 {
 =A0 =A0enum bool isInputRange =3D is(typeof(
 =A0 =A0{
 =A0 =A0 =A0 =A0R r;
 =A0 =A0 =A0 =A0if (r.empty) {}
 =A0 =A0 =A0 =A0r.popFront;
 =A0 =A0 =A0 =A0auto h =3D r.front;
 =A0 =A0}()));
 }
There's actually not that much difference here. =A0Of course we would like several general improvements that have been discussed before: 1) some kind of template 'this' so we don't have to repeat the template =
name.
 2) something better than is(typeof()) (or __traits(compiles, ...)) to
 check if code is ok. [hoping for meta.compiles(...)]

 In some ways the current code is better, because it actually checks if
 a construct works or not, rather than requiring a specific function
 signature. =A0Whether the code will work is really the minimal
 restriction you can place on the interface. =A0A specific may be too
 tight. =A0For instance, above you don't really care if empty returns
 bool or not. =A0Just if you can test it in an "if".
I think one could argue if being more restrictive is actually good or bad=
.
 Being more restrictive cold lead to less errors. Maybe if a struct empty(=
)
 method returns, say, a pointer, I don't want to think it is *really*
 a range. Maybe that method is doing a lot of work and removing stuff, and
 then returning a pointer to some removed stuff. Anyway, I'm not saying
 that that couldn't happen even if empty() return bool (that's a risk when
 duck-typing, always :), I'm just saying I'm not so sure that being more
 restrictive is really a *bad thing*.
If that's what you want, the D way gives you the option of checking is(typeof(R.empty)=3D=3Dbool) But yeh, that is klunkier than just writing out the function signature you're expecting.
 And about what's nice or ugly, I agree the current way of doing stuff is
 not too bad, there are a few details that are not *that* nice. But with
 duck-typing I think the same that with tuples: those little details makes
 you feel that D doesn't support the concept so well, and make people
 resistant to use them. When something feels natural in a language, you us=
e
 it all the time, for me, the current way to do tuples and duck-typing
 looks very artificial and harder than it should.
Agreed. But as for the duck typing aspect, I think there isn't necessarily an either-or choice here. For instance, in D, we could allow a static interface to have a static assert block: static interface InputRange(T) { static assert (meta.compiles({ if (this.empty) {}; // this is a shortcut for T.init })); T front(); void popFront(); } Or something like that. "Invariant {}" might be better there.
 But in terms of functionality it seems we cover pretty much everything
 static interface gives you.
I know that, I started this proposal just saying that =3D)
Ah. Sorry, I missed that comment. --bb
Nov 17 2009
prev sibling next sibling parent reply Jesse Phillips <jessekphillips+D gamil.com> writes:
Leandro Lucarella Wrote:

 
 The problem is "regular" interfaces provide dynamic dispatch. Andrei could
 implement all the range stuff using interfaces, but that would mean:
 1) You have to inherit from the interface (i.e., you can't use arrays)
 2) All calls to ranges functions are virtual (inefficient; this is
    particularly relevant since they are called inside loops => lot of
    times)
 
 A static interface don't have those problems, and I don't see a way to mix
 static and dynamic interfaces without introducing a "new" type of
 interfaces (static interface).
I realize that a class that inherits an interface would have these issues, but if you aren't required to inherit the interface and the use of interface in a function parameter constitutes a compile-time check instead of inheritance check... But this does bring up the question in your suggestion the following code couldn't compile, but based on the signature it looks like it should: size_t walkLength(InputRange range, size_t upTo = size_t.max) { InputRange temp = range; // implementation } I only know enough about the inner workings of compiling Classes to be able to follow explanation why my ideas won't work. This could give a very clear compiler error, but just doesn't appear wrong outside of that.
Nov 16 2009
parent Leandro Lucarella <llucax gmail.com> writes:
Jesse Phillips, el 16 de noviembre a las 19:14 me escribiste:
 Leandro Lucarella Wrote:
 
 
 The problem is "regular" interfaces provide dynamic dispatch. Andrei could
 implement all the range stuff using interfaces, but that would mean:
 1) You have to inherit from the interface (i.e., you can't use arrays)
 2) All calls to ranges functions are virtual (inefficient; this is
    particularly relevant since they are called inside loops => lot of
    times)
 
 A static interface don't have those problems, and I don't see a way to mix
 static and dynamic interfaces without introducing a "new" type of
 interfaces (static interface).
I realize that a class that inherits an interface would have these issues, but if you aren't required to inherit the interface and the use of interface in a function parameter constitutes a compile-time check instead of inheritance check... But this does bring up the question in your suggestion the following code couldn't compile, but based on the signature it looks like it should: size_t walkLength(InputRange range, size_t upTo = size_t.max) { InputRange temp = range; // implementation }
That can be translated to something like: size_t walkLength(Range)(Range range, size_t upTo = size_t.max) if (isInputRange!(Range)) { Range temp = range; // implementation } -- Leandro Lucarella (AKA luca) http://llucax.com.ar/ ---------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------- DETUVIERON BANDA DE PIRATAS DEL ASFALTO SON TODOS URUGUAYOS Y ROBARON MILES DE LITROS DE CERVEZA -- Crónica TV
Nov 17 2009
prev sibling next sibling parent =?UTF-8?B?IuOBruOBl+OBhOOBiyAobm9zaGlpa2EpIg==?= writes:
In order to achieve good completion support by editors (like
IntelliSense) with duck-typed variables, concept-checking code should
contain more type information than that of the code now used in Phobos.

Example:

   static interface InputRange(T) {
     const bool empty();
     T front();
     void popFront();
   }

or in the current D syntax,

   template isInputRange(T, R) {
     enum isInputRange =
       is(const(R).init.empty() == bool) &&
       is(R.init.front() == T) &&
       is(R.init.popFront() == void);
   }

How do you think about this?
Nov 18 2009
prev sibling parent reply "Simen kjaeraas" <simen.kjaras gmail.com> writes:
Leandro Lucarella <llucax gmail.com> wrote:

 What do you think?
I was inspired: template staticInterface( T, iface ) { static assert( is( iface == interface ) ); static assert( is( T == struct ) ); alias staticInterfaceImpl!( T, MemberTuple!( iface ) ) staticInterface; } template staticInterfaceImpl( T, U... ) { static if ( U.length ) { enum staticInterfaceImpl = isIn!( U[ 0 ], U[ 1 ], MemberTuple!( T ) ) && staticInterfaceImpl!( T, U[ 2..$ ] ); } else { enum staticInterfaceImpl = true; } } template MemberTuple( T ) { alias allMembersToTypeTuple!( T, __traits( allMembers, T ) ) MemberTuple; } template allMembersToTypeTuple( T, alias U, V... ) { static if( U.length ) { alias allMembersToTypeTuple!( T, U[1..$], U[ 0 ], typeof( mixin( T.stringof ~ "." ~ U[ 0 ] ) ), V ) allMembersToTypeTuple; } else { alias V allMembersToTypeTuple; } } template isIn( string name, T, U... ) { static if ( U.length ) { enum isIn = ( name == U[ 0 ] && is( T == U[ 1 ] ) ) || isIn!( name, T, U[ 2..$ ] ); } else { enum isIn = false; } } Usage: interface I; struct S; static if ( staticInterface!( S, I ) ) {} There may be problems with this, and it might not match everything as nicely as it should, but it's a proof-of-concept. I'm open to suggestions on how to extend/fix it. -- Simen
Nov 19 2009
parent Leandro Lucarella <llucax gmail.com> writes:
Simen kjaeraas, el 19 de noviembre a las 12:47 me escribiste:
 Leandro Lucarella <llucax gmail.com> wrote:
 
What do you think?
I was inspired:
[...]
 
 Usage:
 
 interface I;
 struct S;
 static if ( staticInterface!( S, I ) ) {}
[...] Nice! :) -- Leandro Lucarella (AKA luca) http://llucax.com.ar/ ---------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------- - Los romanos no tenían paz, loco... Necesitaban un poco de chala...
Nov 19 2009