June 20, 2009
Yigal Chripun wrote:

> Lutger wrote:
>> Not sure what that would do, but C++ concepts are not exactly compile time interfaces. This is very important: in C++0X, a type T which satisfies the concept Comparable<T> does not implement the concept explicitly, whereas languages with explicit constraints on generics do require T to be inherited from IComparable. The consequence is a bit of bloat and more rigid demands on what is supposed to relax the inflexible regime of the static type system. This bloat and rigidity is also a cognitive burden in it's own right, for example when it requires workarounds when the system is not expressive enough.
> 
> regarding the consequences - i agree that this is a bit more rigid. I don't see the bloat though. can you provide an example? can you also explain what kinds of workarounds are you talking about that would be required?

Ok, I sort of assumed you talked about explicit instantiation like in C#. For any type that implements a clone operation for example, you have to derive it from ICloneable if you are to use it as a generic parameter. In addition, in your template, you have to explicitly bring all operations under in an interface. (This is the bloat part). Now say I have a type from another library that supports cloning, has the same interface as ICloneable, but doesn't derive from it. You are forced to create a wrapper for it that derives from ICloneable. (the workaround). Also, there is the complication of what to do with arithmetic types.


>> 
>> Concepts provide two benefits in this context: documentation and extra
>> type checking for templates. But they do retain structural typing. In D,
>> we already have this covered with template constraints.* If you look at
>> std.range, this is exactly what you see: all the interfaces (and even
>> semantics) are nicely named and documented explicitly. So would we have
>> had compile time interfaces, they would add next to nothing about the
>> documentation or understanding of ranges.
>> 
> 
> there are several problems with the template constraints currently used. 1. the constraints are specified on the client code which means you need to either duplicate those constraints everywhere or call some template like isForwardRange manually to check that you got the correct type.

Yes that is the way to go I believe. Phobos already defines a lot of these concepts so that makes it easier.

> 2. The syntax for this is completely alien and unreadable, at least for me.

I agree, but this is a syntax detail. It has no bearing on the type system.


> documentations and type-checking are indeed the two main benefits I'd
> like to get.
> the current way it is done with is() expression is unreadable. this
> needs to be specified IMO with the same (or almost the same) syntax as
> interfaces. I don't get why D needs two completely different syntaxes
> for the same thing (specifying an interface). this will give us a more
> readable documentation aspect.
> the type-checking aspect of this is that the checks will be done on the
> template definition instead of the instantiation in the client code
> which will also prevent cases when bugs in a library template only
> manifest when the client programmer compiles *his* code. this happened
> to tango in the past.

I agree. This is point where concepts in C++ may prove more powerful.

>>> templates are hard for users to understand and one of the main reasons for this is that templates are essentially a completely different language with different syntax and semantics which to me looks like mis-design.
>> 
>> I don't think it is hard to understand because of structural typing. Generics are inherently somewhat difficult in a static typing language, because of it's abstract nature. You don't have this problem in dynamic languages. (or you can't escape it, depending on your POV)
>> 
>> I don't agree that templates are a completely different language though. When used purely for parametric polymorphism, it does integrate nicely in the normal type system. When you do use it for meta-programming, which is relatively rare, then the above also applies: this is an inherently difficult way of programming. Just look at something like lisp where you can metaprogram in the same language. Does that make it easy to understand? Or CTFE and string mixins in D, same language, but it's also difficult. Adding more constraints can never solve the fact that humans don't easily grok programs which generate programs.
>> 
> I was talking mostly about meta-programming and not parametric
> polymorphism.
> I agree that it is harder to grok programs that generate programs. this
> is why it is so important IMO to make this as readable as possible.
> to answer your question, lisp does make this _easier_ to understand
> compared to templates. D CTFE functions are much more readable than D
> templates.
> while I agree that this is never trivial, it should not be made near
> impossible like it is in C++. an experienced user should be able to read
> the source of libs like Boost and STL and understand without much
> trouble what it does without being a C++ guru.
> 
>> * I don't think the extra type checking is done, but perhaps it could be.
>> 
> 
> the distinction you make between generics with explicit constraints that
> require explicit inheritance and concepts is more of an implementation
> detail IMO.
> the first uses run-time inheritance for this type checking.
> what I'd prefer is the second implementation where the type-check is
> done at compile-time by means of structural typing but unlike C++ where
> it's optional I want it to be required so that the type-checking is
> performed on the definition and not on the instantiations.
> does that make sense?

I don't understand how you can have structural typing and at the same time require explicit constraints. Maybe I'm missing something here? This was my entire point: losing structural typing because of explicit generic constraints is a bad thing.

June 20, 2009
== Quote from Yigal Chripun (yigal100@gmail.com)'s article
> > Also, while the fact that you need interfaces to specify a vtable layout is an implementation detail, I would argue that, in close to the metal languages, it does more harm than good to try too hard to prevent implementation details from leaking into the language abstractions.  Otherwise, what would be the point of it being a close to the metal language?  The fact that, for templates, one does not need to specify vtable layouts and for OO you do justifies the asymmetry between templates and OO.  Interfaces for templates would just add boilerplate and make explicit something that is already implicitly knowable and checked at compile time anyhow.
> here I disagree. it sometimes makes sense to let implementation details leak into your abstractions when you gain something by it, like performance (e.g. "Worse is better" principle) but I don't see how this applies here. what is there to gain by doing this compromise in this case? there is no added performance since it's all compile-time, there is no additional flexibly like with run-time duck-typing.

Simplicity and DRY--you only need to specify what the compiler doesn't already know.
June 20, 2009
Lutger wrote:
> 
> Ok, I sort of assumed you talked about explicit instantiation like in C#. For any type that implements a clone operation for example, you have to derive it from ICloneable if you are to use it as a generic parameter. In addition, in your template, you have to explicitly bring all operations under in an interface. (This is the bloat part). Now say I have a type from another library that supports cloning, has the same interface as ICloneable, but doesn't derive from it. You are forced to create a wrapper for it that derives from ICloneable. (the workaround). Also, there is the complication of what to do with arithmetic types.
> 

the arithmetic types issue is not a problem in D since the relevant operators are non-static in D.
I don't understand what's the bloat here. here's an example:

interface I {  // you mean this interface is bloat ?
 void func() ;
}

class C(T : I) {
...
}

regarding the workaround issue, we agreed already that verifying a type against a concept is done structurally.
possible solutions are that identical concepts with different names are implicitly castable to each other. the compiler will treat it similar to aliases.

suppose that you got a type implementing the MyClonable interface which is structurally identical to the standard ICloneable interface.
void foo(T : ICloneable) (T t) {...}
foo() should work with your type because for the compiler both interfaces represent the same concept.

this restricts "duck-typing" to the concept level only.
another option is that instead of this being done by the compiler, the programmer could specify this relation by:

alias MyConcept Iconcept;
// compile type-checks here for conformance and registers this identity

This idea probably can be further refined. I think My general direction here is to have C++ concepts but with a *much* better syntax and more tightly integrated into the type system. In C++ it feels like an optional addon added as an after thought.
June 20, 2009
dsimcha wrote:
> == Quote from Yigal Chripun (yigal100@gmail.com)'s article
>>> Also, while the fact that you need interfaces to specify a vtable layout is an
>>> implementation detail, I would argue that, in close to the metal languages, it
>>> does more harm than good to try too hard to prevent implementation details from
>>> leaking into the language abstractions.  Otherwise, what would be the point of it
>>> being a close to the metal language?  The fact that, for templates, one does not
>>> need to specify vtable layouts and for OO you do justifies the asymmetry between
>>> templates and OO.  Interfaces for templates would just add boilerplate and make
>>> explicit something that is already implicitly knowable and checked at compile time
>>> anyhow.
>> here I disagree. it sometimes makes sense to let implementation details
>> leak into your abstractions when you gain something by it, like
>> performance (e.g. "Worse is better" principle) but I don't see how this
>> applies here. what is there to gain by doing this compromise in this case?
>> there is no added performance since it's all compile-time, there is no
>> additional flexibly like with run-time duck-typing.
> 
> Simplicity and DRY--you only need to specify what the compiler doesn't already know.

simplicity - have one syntax to remember instead of two.
DRY - concepts are already present and used, see the isXXXRange templates. I didn't add anything beyond that.
1 2 3
Next ›   Last »