digitalmars.D - Ranges
- Steve Teale (19/19) Jun 18 2009 I shall start this again from scratch. Please excuse me for being thick ...
- Ary Borenszweig (8/34) Jun 18 2009 It's called "duck typing".
- dsimcha (41/60) Jun 18 2009 it. I have tried before in more direct questions to Andrei, but in the e...
- Derek Parnell (76/77) Jun 18 2009 In my case, I understand templates but that in itself doesn't help me
- Kristian Kilpi (12/18) Jun 19 2009 I agree. Well, even if the names of the Range methods are a bit 'odd' fo...
- Yigal Chripun (23/46) Jun 19 2009 while I agree with the general point about the naming convention, I
- Max Samukha (5/23) Jun 19 2009 clear() is used quite often. You could use the likes of erase() or
- Lionello Lunesu (7/12) Jun 18 2009 Actually, C# doesn't care for the interfaces. They are just there to hel...
- Ary Borenszweig (4/17) Jun 19 2009 Wow. :-)
- Lionello Lunesu (6/26) Jun 20 2009 Hehe, you're welcome!
- BLS (4/11) Jun 19 2009 What I don't get is how this (definition) may work on tree based
- Yigal Chripun (16/35) Jun 19 2009 as I understand this, A range is alway some linear (iteration) order of
- BLS (2/42) Jun 19 2009 Thanks, Yes that makes sense.
- bearophile (15/23) Jun 19 2009 This is right, and in some situations it may even be possible to provide...
- Yigal Chripun (21/56) Jun 19 2009 I thought about using an enum as well, but am unsure what's simpler in
- bearophile (4/5) Jun 19 2009 I don't understand what do you mean. Can you show me how you would like ...
- Yigal Chripun (11/19) Jun 19 2009 auto c = new Container(Type)();
- Robert Fraser (2/4) Jun 19 2009 So this is just a different bikeshed for opApply?
- dsimcha (4/7) Jun 19 2009 But the beauty of a lot of this stuff is that the syntax of iteration st...
- Yigal Chripun (5/13) Jun 19 2009 I'm not sure I follow this.
- dsimcha (9/22) Jun 19 2009 Yes, but a large portion of the time, iterating over all elements is all...
- Yigal Chripun (9/31) Jun 20 2009 OK, that does makes sense. i thought that ranged are for those
- dsimcha (12/43) Jun 20 2009 My vote would be for opApply. Of course there is no perfect answer and ...
- Tim Matthews (18/26) Jun 19 2009 I think steve understands templates and he was actually re-quoting the
- Robert Fraser (9/22) Jun 18 2009 Yeah, that one is a bit tricky, and what makes it worse is that it seems...
- grauzone (9/16) Jun 18 2009 Oh, finally someone who shares my concerns! I fear the alternatives
- robert fraser (4/9) Jun 18 2009 True -- both these features (string mixins and is-expressions) are rife ...
- bearophile (62/68) Jun 18 2009 If you read the book by Andrei you will probably find enough explanation...
- Derek Parnell (35/56) Jun 18 2009 LOL ... glad to have helped a tiny bit.
- Robert Fraser (11/23) Jun 18 2009 Eh, it has the word "compiles" in it... You're right, though, it's not
- Derek Parnell (6/8) Jun 18 2009 You're right. I misread the documentation on that one.
- Yigal Chripun (5/110) Jun 19 2009 we all know that D's compile-time features are a complete mess.
- Steve Teale (2/29) Jun 19 2009 So does this mean that interfaces are just a tragic mistake. I'd always ...
- Lutger (5/12) Jun 19 2009 Could you explain why that makes interfaces a mistake? Interfaces (as in...
- Yigal Chripun (3/33) Jun 19 2009 IMHO, duck-typing in D is a tragic mistake... This should have been
- Lutger (3/6) Jun 19 2009 Care to provide arguments?
- superdan (2/9) Jun 19 2009 ignorance 'n' arrogance should do.
- Lutger (2/13) Jun 19 2009 Those I hold I high esteem, you convinced me.
- Yigal Chripun (20/28) Jun 19 2009 duck typing makes more sense in dynamic languages like Ruby which is
- bearophile (5/8) Jun 19 2009 What are interfaces from the point of view of the compiler?
- dsimcha (19/24) Jun 19 2009 Abstract classes with only pure virtual functions. In other words, basi...
- bearophile (4/6) Jun 20 2009 Oh, right, sorry, my question really was "What are compile-time interfac...
- Yigal Chripun (15/42) Jun 20 2009 duck-typing has its benefits, that's for sure. it all boils down to is
- Lutger (8/40) Jun 20 2009 It's called structural typing in static languages which is almost the sa...
- Yigal Chripun (3/11) Jun 20 2009 This is why i don't like it in static languages. I like my type system
- dsimcha (2/17) Jun 20 2009 Simplicity and DRY--you only need to specify what the compiler doesn't a...
- Yigal Chripun (4/22) Jun 20 2009 simplicity - have one syntax to remember instead of two.
- Robert Fraser (3/4) Jun 19 2009 He gave a D talk on Wednesday night. I get the feeling the next release
- superdan (5/34) Jun 19 2009 yer dunno what yer talking about do ya. d checks duck typed shit at comp...
- Lutger (32/63) Jun 20 2009 Not sure what that would do, but C++ concepts are not exactly compile ti...
- Yigal Chripun (42/82) Jun 20 2009 regarding the consequences - i agree that this is a bit more rigid. I
- Lutger (20/107) Jun 20 2009 Ok, I sort of assumed you talked about explicit instantiation like in C#...
- Yigal Chripun (29/40) Jun 20 2009 the arithmetic types issue is not a problem in D since the relevant
- dsimcha (9/42) Jun 19 2009 thought that what you said was a pretty good description of what an inte...
- Tim Matthews (5/46) Jun 19 2009 It sometimes makes up for a lack of an actual type system but it is not
- dsimcha (3/49) Jun 19 2009 No you don't, constraints are just to improve overloading capabilities a...
- Steve Teale (2/23) Jun 19 2009 Kind of like the oomigooli bird. Flies round in ever decreasing circles ...
I shall start this again from scratch. Please excuse me for being thick about it. I have tried before in more direct questions to Andrei, but in the end all I got was a snotty email telling me not to be a nuisance and RTFD. Unfortunately, the relevant documentation seems to have disappeared. In range.d, in the context of isInputRange, it says: "Returns $(D true) if $(D R) is an input range. An input range must define the primitives $(D empty), $(D popFront), and $(D front). The following code should compile for any input range ..." template isInputRange(R) { enum bool isInputRange = is(typeof( { R r; // can define a range object if (r.empty) {} // can test for empty r.popFront; // can invoke next auto h = r.front; // can get the front of the range }())); } I can not possibly be the only D enthusiast who finds this completely incomprehensible. What is a range? Is it a template interface, or is it just a trick of template syntax that supports the old assertion that "nobody really understands templates". If ranges are to be a feature of the D language, then they should probably be supported at language level rather than by some trick that has been discovered by experimenting with how far you can push templates. Also, it would be very useful to have some indication of what you might use them for. I occasionally had to resort to STL iterators because I wanted to use 'map'. I agree that the syntax sucked, but nobody is telling me how ranges help. I realize that some people with an IQ of 580 will find my questions naive and misguided - not to mention impertinent, but it seems to me that one of the responsibilities of being a leader is to explain to less gifted followers what the fuck is going on. Or maybe I've got it wrong - if you're that bright (sorry Walter - not you) then perhaps it's just a big ego trip.
Jun 18 2009
Steve Teale wrote:I shall start this again from scratch. Please excuse me for being thick about it. I have tried before in more direct questions to Andrei, but in the end all I got was a snotty email telling me not to be a nuisance and RTFD. Unfortunately, the relevant documentation seems to have disappeared. In range.d, in the context of isInputRange, it says: "Returns $(D true) if $(D R) is an input range. An input range must define the primitives $(D empty), $(D popFront), and $(D front). The following code should compile for any input range ..." template isInputRange(R) { enum bool isInputRange = is(typeof( { R r; // can define a range object if (r.empty) {} // can test for empty r.popFront; // can invoke next auto h = r.front; // can get the front of the range }())); } I can not possibly be the only D enthusiast who finds this completely incomprehensible. What is a range? Is it a template interface, or is it just a trick of template syntax that supports the old assertion that "nobody really understands templates". If ranges are to be a feature of the D language, then they should probably be supported at language level rather than by some trick that has been discovered by experimenting with how far you can push templates. Also, it would be very useful to have some indication of what you might use them for. I occasionally had to resort to STL iterators because I wanted to use 'map'. I agree that the syntax sucked, but nobody is telling me how ranges help. I realize that some people with an IQ of 580 will find my questions naive and misguided - not to mention impertinent, but it seems to me that one of the responsibilities of being a leader is to explain to less gifted followers what the fuck is going on. Or maybe I've got it wrong - if you're that bright (sorry Walter - not you) then perhaps it's just a big ego trip.It's called "duck typing". http://en.wikipedia.org/wiki/Duck_typing "an object's current set of methods and properties determines the valid semantics, rather than its inheritance from a particular class or implementation of a specific interface" So you don't say something is a range by looking if it implements some Range interface, but rather if it has some methods in it.
Jun 18 2009
== Quote from Steve Teale (steve.teale britseyeview.com)'s articleI shall start this again from scratch. Please excuse me for being thick aboutit. I have tried before in more direct questions to Andrei, but in the end all I got was a snotty email telling me not to be a nuisance and RTFD. Unfortunately, the relevant documentation seems to have disappeared.In range.d, in the context of isInputRange, it says: "Returns $(D true) if $(D R) is an input range. An input range must define the primitives $(D empty), $(D popFront), and $(D front). The following code should compile for any input range ..." template isInputRange(R) { enum bool isInputRange = is(typeof( { R r; // can define a range object if (r.empty) {} // can test for empty r.popFront; // can invoke next auto h = r.front; // can get the front of the range }())); } I can not possibly be the only D enthusiast who finds this completelyincomprehensible. What is a range? Is it a template interface, or is it just a trick of template syntax that supports the old assertion that "nobody really understands templates".If ranges are to be a feature of the D language, then they should probably besupported at language level rather than by some trick that has been discovered by experimenting with how far you can push templates.Also, it would be very useful to have some indication of what you might use themfor. I occasionally had to resort to STL iterators because I wanted to use 'map'. I agree that the syntax sucked, but nobody is telling me how ranges help.I realize that some people with an IQ of 580 will find my questions naive andmisguided - not to mention impertinent, but it seems to me that one of the responsibilities of being a leader is to explain to less gifted followers what the fuck is going on. Or maybe I've got it wrong - if you're that bright (sorry Walter - not you) then perhaps it's just a big ego trip. Ranges are just pretty much an implicit compile-time interface. As Ary put it, compile time duck typing is a pretty accurate description. Basically, a range doesn't have to *be* a specific *type*, it just has to support certain specific *operations*, namely front, popFront, and empty. As long as it *has* these operations, and they compile and return what they're supposed to (ElementType!(T) for front(), void for popFront() and bool for empty), it doesn't matter what type it *is*. I guess the best way to think of it is that ranges are simply a convention used in Phobos about how to define iteration over user-defined types. If you stick to this convention, then all the range templates people write implicitly know what to do with your type even if they know nothing about the specifics of it. Ranges are really just a form of iterators that's given sane syntax (unlike C++) and relies on this compile-time duck typing instead of virtual functions and they are the same except that ranges can be used where both efficiency and readability nothing more than a way of encapsulating (at compile time, but not necessarily at runtime) iteration over user-defined types so that generic code can be written to work on these types. I suspect that your lack of understanding of ranges stems from lack of understanding of templates, since you mention that "noone understands templates" and once you get templates, ranges are ridiculously simple. If that's the case, then your best bet is probably to learn a little more about templates (which are so fundamental to what makes D special IMHO that I would say that, for all practical purposes, if you don't understand templates you don't understand D) and then try to understand ranges again.
Jun 18 2009
Steve Teale (steve.teale britseyeview.com) wrote:In my case, I understand templates but that in itself doesn't help me understand Andrei's concept of ranges. Just using English as a guide doesn't really help either because a "range" is not an "iterator" in English. So I'd describe a range more along the lines of ... A Range is a data construct that has a set of zero or more discrete elements and can be used to iterate over its elements. There are a few varieties of ranges: InputRange, OutputRange, ForwardRange, BidirectionalRange and RandomAccessRange. InputRange ---------- With this range, iteration can only occur in one direction, from first to last element. As a minimum requirement it must implement methods that ... 1) Provide a function that returns if there are any elements (remaining) to iterator over. That function must be called 'empty()'. 2) Provide a function that returns the current first element in the set. Calling this when 'empty()' returns true will throw an exception. That function must be called 'front()'. 3) Provide a function that moves an internal 'cursor' to the element following the current first element in the set, such that it becomes the new current first element. Calling this when 'empty()' returns true will throw an exception. That function must be called 'popFront()'. (I assume that the initial call to front() before calling popFront() will return the absolute first element in the set, but this doesn't seem to be documented.) OutputRange ----------- This is an InputRange with the extra capability of being able to add elements to the range. In addition to the InputRange methods, it must also provide a method that adds a new element to the range, such that it becomes the current element. That method must be called 'put(E)' where 'E' is the new element. ForwardRange ------------ This is an InputRange with the extra capability of being able checkpoint the current first 'cursor' position simply by copying the range. When you copy an plain InputRange the copied range starts again from the absolute first element, but a copied ForwardRange starts at whatever was the current first element in the source range. (I'm not sure why Birectional ranges are not allowed to be checkpointed) BidirectionalRange ------------------ This is a ForwardRange that also allows iteration in the opposite direction. In addition to the ForwardRange methods it must also ... 1) Provide a function that returns the current last element in the set. Calling this when 'empty()' returns true will throw an exception. This must be called 'back()'. 2) Provide a function that moves an internal 'cursor' to the element that precedes the current last element in the set, such that it becomes the new current last element. Calling this when 'empty()' returns true will throw an exception. That function must be called 'popBack()'. (I assume that the initial call to back() before calling popBack() will return the absolute last element in the set, but this doesn't seem to be documented.) RandomAccessRange ----------------- This is either a BidirectionalRange or any Range in which 'empty()' always returns false (an infinite range). It must provide a method that will return the Nth element of the set. That function must be called 'opIndex(N)' where 'N' is a non-negative integer that represents a zero-based index into the set. Thus opIndex(0) returns the first element, opIndex(1) returns the 2nd element, etc ... (I assume that for finite Ranges if 'N' is not a valid index that the Range will throw an exception, but this doesn't seem to be documented.) Now I admit that these are not method names I would have choosen, as I would have preferred names more like ... isEmpty(), getFront(), moveForwards(), getBack(), moveBackwards(), getElement(N), addElement(E), but the bikeshed gods have more wisdom than me ... and not that I'm complaining of course. -- Derek Parnell Melbourne, Australia skype: derek.j.parnellWhat is a range?
Jun 18 2009
On Fri, 19 Jun 2009 03:13:11 +0300, Derek Parnell <derek psych.ward> wrote:Steve Teale (steve.teale britseyeview.com) wrote: Now I admit that these are not method names I would have choosen, as I would have preferred names more like ... isEmpty(), getFront(), moveForwards(), getBack(), moveBackwards(), getElement(N), addElement(E), but the bikeshed gods have more wisdom than me ... and not that I'm complaining of course.I agree. Well, even if the names of the Range methods are a bit 'odd' for me, I guess they are ok... except for empty(). If I have a container that I wan't to use as a Range, I can't use empty() to empty the container (i.e. remove all the elements from it). :( And yes, *I* am complaining here. ;) I'm accustomed to use isXyz() for checking something and xyz() for doing something (e.g. isEmpty() + empty()). What function name should I use for emptying the container then? removeAllElements(), makeEmpty(), or maybe even emptyThisContainer()...? :P Hmm, should the Range methods use some special naming convention? E.g. rangeFront(), rangePopFront()...?
Jun 19 2009
Kristian Kilpi wrote:On Fri, 19 Jun 2009 03:13:11 +0300, Derek Parnell <derek psych.ward> wrote:while I agree with the general point about the naming convention, I don't see how is this a problem here since a range should be a distinct type from the container. that means that you have for instance: class Tree { void empty(); ... TreeRange preOrder(); TreeRange inOrder(); TreeRange postOrder(); } struct TreeRange { bool empty(); ... } tree.empty(); // will empty the tree auto range = tree.inOrder(); ... while (!range.empty()) { // will check if the range is empty // do stuff } this might be confusing but not impossible.Steve Teale (steve.teale britseyeview.com) wrote: Now I admit that these are not method names I would have choosen, as I would have preferred names more like ... isEmpty(), getFront(), moveForwards(), getBack(), moveBackwards(), getElement(N), addElement(E), but the bikeshed gods have more wisdom than me ... and not that I'm complaining of course.I agree. Well, even if the names of the Range methods are a bit 'odd' for me, I guess they are ok... except for empty(). If I have a container that I wan't to use as a Range, I can't use empty() to empty the container (i.e. remove all the elements from it). :( And yes, *I* am complaining here. ;) I'm accustomed to use isXyz() for checking something and xyz() for doing something (e.g. isEmpty() + empty()). What function name should I use for emptying the container then? removeAllElements(), makeEmpty(), or maybe even emptyThisContainer()...? :P Hmm, should the Range methods use some special naming convention? E.g. rangeFront(), rangePopFront()...?
Jun 19 2009
On Fri, 19 Jun 2009 11:49:06 +0300, "Kristian Kilpi" <kjkilpi gmail.com> wrote:On Fri, 19 Jun 2009 03:13:11 +0300, Derek Parnell <derek psych.ward> wrote:clear() is used quite often. You could use the likes of erase() or purge(). But, as Yigal already said, ranges and containers they crawl over are usually distinct types, so this shouldn't be a big problem.Steve Teale (steve.teale britseyeview.com) wrote: Now I admit that these are not method names I would have choosen, as I would have preferred names more like ... isEmpty(), getFront(), moveForwards(), getBack(), moveBackwards(), getElement(N), addElement(E), but the bikeshed gods have more wisdom than me ... and not that I'm complaining of course.I agree. Well, even if the names of the Range methods are a bit 'odd' for me, I guess they are ok... except for empty(). If I have a container that I wan't to use as a Range, I can't use empty() to empty the container (i.e. remove all the elements from it). :( And yes, *I* am complaining here. ;) I'm accustomed to use isXyz() for checking something and xyz() for doing something (e.g. isEmpty() + empty()). What function name should I use for emptying the container then? removeAllElements(), makeEmpty(), or maybe even emptyThisContainer()...? :PHmm, should the Range methods use some special naming convention? E.g. rangeFront(), rangePopFront()...?
Jun 19 2009
"dsimcha" <dsimcha yahoo.com> wrote in message news:h1e6qp$umo$1 digitalmars.com...Ranges are really just a form of iterators that's given sane syntax (unlike C++) and relies on this compile-time duck typing instead of virtual functions and"T GetEnumerator()", where type T in turn implements "bool MoveNext()" and "S Current { get; }". L.
Jun 18 2009
Lionello Lunesu wrote:"dsimcha" <dsimcha yahoo.com> wrote in message news:h1e6qp$umo$1 digitalmars.com...Wow. :-) You always learn something new... Thanks, Lionello!Ranges are really just a form of iterators that's given sane syntax (unlike C++) and relies on this compile-time duck typing instead of virtual functions andtype with "T GetEnumerator()", where type T in turn implements "bool MoveNext()" and "S Current { get; }".
Jun 19 2009
Ary Borenszweig wrote:Lionello Lunesu wrote:Hehe, you're welcome! L. By the way, using 'duck typing' (instead of implementing won't emit virtual calls and try-finally-Dispose. :)"dsimcha" <dsimcha yahoo.com> wrote in message news:h1e6qp$umo$1 digitalmars.com...Wow. :-) You always learn something new... Thanks, Lionello!Ranges are really just a form of iterators that's given sane syntax (unlike C++) and relies on this compile-time duck typing instead of virtual functions andtype with "T GetEnumerator()", where type T in turn implements "bool MoveNext()" and "S Current { get; }".
Jun 20 2009
dsimcha wrote:Ranges are just pretty much an implicit compile-time interface. As Ary put it, compile time duck typing is a pretty accurate description. Basically, a range doesn't have to *be* a specific *type*, it just has to support certain specific *operations*, namely front, popFront, and empty. As long as it *has* these operations, and they compile and return what they're supposed to (ElementType!(T) for front(), void for popFront() and bool for empty), it doesn't matter what type it *is*.What I don't get is how this (definition) may work on tree based structures. To me it seems that ranges work fine on "linear" data types (whatever) lists, but well, as said I don't get it. :(
Jun 19 2009
BLS wrote:dsimcha wrote:as I understand this, A range is alway some linear (iteration) order of the elements. so a tree structure can provide: tree.preOrder() tree.inOrder() tree.postOrder() which return three different ranges representing these orderings of the tree elements. a sub-tree will be of type tree itself and has nothing to do with ranges. you can of course combine the two, e.g.: AVLtree.left.right.left.postOrder; for linear structures. e.g. an array these two operations are the same. int[100] arr; auto slice = arr[40, 50]; slice is both a sub-array and a range of that sub-array.Ranges are just pretty much an implicit compile-time interface. As Ary put it, compile time duck typing is a pretty accurate description. Basically, a range doesn't have to *be* a specific *type*, it just has to support certain specific *operations*, namely front, popFront, and empty. As long as it *has* these operations, and they compile and return what they're supposed to (ElementType!(T) for front(), void for popFront() and bool for empty), it doesn't matter what type it *is*.What I don't get is how this (definition) may work on tree based structures. To me it seems that ranges work fine on "linear" data types (whatever) lists, but well, as said I don't get it. :(
Jun 19 2009
Yigal Chripun wrote:BLS wrote:Thanks, Yes that makes sense.dsimcha wrote:as I understand this, A range is alway some linear (iteration) order of the elements. so a tree structure can provide: tree.preOrder() tree.inOrder() tree.postOrder() which return three different ranges representing these orderings of the tree elements. a sub-tree will be of type tree itself and has nothing to do with ranges. you can of course combine the two, e.g.: AVLtree.left.right.left.postOrder; for linear structures. e.g. an array these two operations are the same. int[100] arr; auto slice = arr[40, 50]; slice is both a sub-array and a range of that sub-array.Ranges are just pretty much an implicit compile-time interface. As Ary put it, compile time duck typing is a pretty accurate description. Basically, a range doesn't have to *be* a specific *type*, it just has to support certain specific *operations*, namely front, popFront, and empty. As long as it *has* these operations, and they compile and return what they're supposed to (ElementType!(T) for front(), void for popFront() and bool for empty), it doesn't matter what type it *is*.What I don't get is how this (definition) may work on tree based structures. To me it seems that ranges work fine on "linear" data types (whatever) lists, but well, as said I don't get it. :(
Jun 19 2009
Yigal Chripun:so a tree structure can provide: tree.preOrder() tree.inOrder() tree.postOrder() which return three different ranges representing these orderings of the tree elements.This is right, and in some situations it may even be possible to provide a generic scan: tree.scan(ScanType.PREORDER) tree.scan(ScanType.LIMITEDDEPHT) etc. But from my first experiments with the range protocol I have seen that the sometimes leads to simpler to write iteration code. For example defining a opApply that scans a tree by pre-order is very easy, you just put the yield (or the equivalent machinery of opApply) where you want to process a leaf of the tree. But when you use the range protocol you have to split that code in parts and you must manage the state yourself manually. This can sometimes be tricky and maybe even bug-prone. ------------------------------- Kristian Kilpi:Hmm, should the Range methods use some special naming convention? E.g. rangeFront(), rangePopFront()...?<Time ago I have suggested something like opFront, opEmpty, etc, like the normal D operators. ------------------------------- Yigal Chripun:while I agree with the general point about the naming convention, I don't see how is this a problem here since a range should be a distinct type from the container.<Is this always true? In a simple data structure I may want to conflate the iteration protocol with the data structure itself, for example for an array type. Is this a bad/wrong design? Bye, bearophile
Jun 19 2009
bearophile wrote:Yigal Chripun:I thought about using an enum as well, but am unsure what's simpler in this case. I'm not in love with D's enum construct.so a tree structure can provide: tree.preOrder() tree.inOrder() tree.postOrder() which return three different ranges representing these orderings of the tree elements.This is right, and in some situations it may even be possible to provide a generic scan: tree.scan(ScanType.PREORDER) tree.scan(ScanType.LIMITEDDEPHT) etc.But from my first experiments with the range protocol I have seen that the sometimes leads to simpler to write iteration code. For example defining a opApply that scans a tree by pre-order is very easy, you just put the yield (or the equivalent machinery of opApply) where you want to process a leaf of the tree. But when you use the range protocol you have to split that code in parts and you must manage the state yourself manually. This can sometimes be tricky and maybe even bug-prone.what you talk about above is the tradeoff between client iteration (C++ iterators) vs. container iteration (functional each method). being a Python programmer you prefer the second but both have pros and cons. you can always combine both (coroutines/fibers/etc..) but that has a performance cost. all the above have their benefits and you should use what's appropriate for the task at hand, instead of religiously committing yourself to just one. the benefit of client side iteration like with ranges is that client code gets fine grained control over the iteration process. personally, I think opApply should be removed. it provides "push" style iteration which should be provided as a "each" method of the container. the "pull" style of ranges should be used with client side looping.------------------------------- Kristian Kilpi:I mentioned arrays in my original post as an exception but I feel that in general it shouldn't be conflated in order to get a cleaner design. where else would it make sense to you to conflate the two except arrays and maybe linked-lists? I think this separation of concerns is a good thing.Hmm, should the Range methods use some special naming convention? E.g. rangeFront(), rangePopFront()...?<Time ago I have suggested something like opFront, opEmpty, etc, like the normal D operators. ------------------------------- Yigal Chripun:while I agree with the general point about the naming convention, I don't see how is this a problem here since a range should be a distinct type from the container.<Is this always true? In a simple data structure I may want to conflate the iteration protocol with the data structure itself, for example for an array type. Is this a bad/wrong design?Bye, bearophile
Jun 19 2009
Yigal Chripun:personally, I think opApply should be removed. it provides "push" style iteration which should be provided as a "each" method of the container. the "pull" style of ranges should be used with client side looping.<I don't understand what do you mean. Can you show me how you would like to replace the purpose of opApply, maybe with an example? Bye, bearophile
Jun 19 2009
bearophile wrote:Yigal Chripun:auto c = new Container(Type)(); .. 1. Container implements iteration, "yields" items in sequence c.each( (Type obj) { ... } ); 2. client code implements iteration, pulls container for items auto r = c.getRange(); // name isn't important, just the semantics while (!r.empty) { // do stuff with current item if (some_external_condition) break; }personally, I think opApply should be removed. it provides "push" style iteration which should be provided as a "each" method of the container. the "pull" style of ranges should be used with client side looping.<I don't understand what do you mean. Can you show me how you would like to replace the purpose of opApply, maybe with an example? Bye, bearophile
Jun 19 2009
Yigal Chripun wrote:1. Container implements iteration, "yields" items in sequence c.each( (Type obj) { ... } );So this is just a different bikeshed for opApply?
Jun 19 2009
== Quote from Yigal Chripun (yigal100 gmail.com)'s articlepersonally, I think opApply should be removed. it provides "push" style iteration which should be provided as a "each" method of the container. the "pull" style of ranges should be used with client side looping.But the beauty of a lot of this stuff is that the syntax of iteration stays the same no matter how it works under the hood (builtins, ranges, opApply). This is important for both generic programming and programmer convenience.
Jun 19 2009
dsimcha wrote:== Quote from Yigal Chripun (yigal100 gmail.com)'s articleI'm not sure I follow this. if you just want to do something with all elements than you're right but if you want to do something more complex where you need to use the range interface yourself than you can't use the foreach loop.personally, I think opApply should be removed. it provides "push" style iteration which should be provided as a "each" method of the container. the "pull" style of ranges should be used with client side looping.But the beauty of a lot of this stuff is that the syntax of iteration stays the same no matter how it works under the hood (builtins, ranges, opApply). This is important for both generic programming and programmer convenience.
Jun 19 2009
== Quote from Yigal Chripun (yigal100 gmail.com)'s articledsimcha wrote:Yes, but a large portion of the time, iterating over all elements is all you need. For example, if I want to write a generic function to find the mean and standard deviation of some object, I just need to be able to loop over it once. I don't care if it uses a range, builtin arrays, builtin associative arrays, opApply, or pixie dust and magic. The way this is done should be dead simple and consistent regardless of how it works under the hood. Of course if you need to do something more complicated you may need to care about the details, but it's very often the case that you don't.== Quote from Yigal Chripun (yigal100 gmail.com)'s articleI'm not sure I follow this. if you just want to do something with all elements than you're right but if you want to do something more complex where you need to use the range interface yourself than you can't use the foreach loop.personally, I think opApply should be removed. it provides "push" style iteration which should be provided as a "each" method of the container. the "pull" style of ranges should be used with client side looping.But the beauty of a lot of this stuff is that the syntax of iteration stays the same no matter how it works under the hood (builtins, ranges, opApply). This is important for both generic programming and programmer convenience.
Jun 19 2009
dsimcha wrote:== Quote from Yigal Chripun (yigal100 gmail.com)'s articleOK, that does makes sense. i thought that ranged are for those complicated situations but at a second thought there is no reason to have this limit. the one consist way to iterate over the elements regardless of what's under the hood is the foreach loop so my question than is what is/should be the priorities between the different iteration modes? if I have a container that provides both a range and opApply, which would be used by the compiler in the foreach loop?dsimcha wrote:Yes, but a large portion of the time, iterating over all elements is all you need. For example, if I want to write a generic function to find the mean and standard deviation of some object, I just need to be able to loop over it once. I don't care if it uses a range, builtin arrays, builtin associative arrays, opApply, or pixie dust and magic. The way this is done should be dead simple and consistent regardless of how it works under the hood. Of course if you need to do something more complicated you may need to care about the details, but it's very often the case that you don't.== Quote from Yigal Chripun (yigal100 gmail.com)'s articleI'm not sure I follow this. if you just want to do something with all elements than you're right but if you want to do something more complex where you need to use the range interface yourself than you can't use the foreach loop.personally, I think opApply should be removed. it provides "push" style iteration which should be provided as a "each" method of the container. the "pull" style of ranges should be used with client side looping.But the beauty of a lot of this stuff is that the syntax of iteration stays the same no matter how it works under the hood (builtins, ranges, opApply). This is important for both generic programming and programmer convenience.
Jun 20 2009
== Quote from Yigal Chripun (yigal100 gmail.com)'s articledsimcha wrote:My vote would be for opApply. Of course there is no perfect answer and the compiler will have to guess which one you mean, but I think opApply is about always the right guess. Ranges are the more flexible interface from the client's perspective, so the fact that you took the time and effort to write an opApply must mean that you want to use it when the client doesn't need that flexibility. As a concrete example, let's say you are iterating over a binary tree. This can be done with ranges, but only with an explicit stack. With opApply, you have control over the call stack and can just use recursion. Therefore, one might want to design two methods of iterating over a tree: An opApply method that uses recursion and a range method that uses an explicit stack but is more flexible for the client.== Quote from Yigal Chripun (yigal100 gmail.com)'s articleOK, that does makes sense. i thought that ranged are for those complicated situations but at a second thought there is no reason to have this limit. the one consist way to iterate over the elements regardless of what's under the hood is the foreach loop so my question than is what is/should be the priorities between the different iteration modes? if I have a container that provides both a range and opApply, which would be used by the compiler in the foreach loop?dsimcha wrote:Yes, but a large portion of the time, iterating over all elements is all you need. For example, if I want to write a generic function to find the mean and standard deviation of some object, I just need to be able to loop over it once. I don't care if it uses a range, builtin arrays, builtin associative arrays, opApply, or pixie dust and magic. The way this is done should be dead simple and consistent regardless of how it works under the hood. Of course if you need to do something more complicated you may need to care about the details, but it's very often the case that you don't.== Quote from Yigal Chripun (yigal100 gmail.com)'s articleI'm not sure I follow this. if you just want to do something with all elements than you're right but if you want to do something more complex where you need to use the range interface yourself than you can't use the foreach loop.personally, I think opApply should be removed. it provides "push" style iteration which should be provided as a "each" method of the container. the "pull" style of ranges should be used with client side looping.But the beauty of a lot of this stuff is that the syntax of iteration stays the same no matter how it works under the hood (builtins, ranges, opApply). This is important for both generic programming and programmer convenience.
Jun 20 2009
dsimcha wrote:I suspect that your lack of understanding of ranges stems from lack of understanding of templates, since you mention that "noone understands templates" and once you get templates, ranges are ridiculously simple. If that's the case, then your best bet is probably to learn a little more about templates (which are so fundamental to what makes D special IMHO that I would say that, for all practical purposes, if you don't understand templates you don't understand D) and then try to understand ranges again.I think steve understands templates and he was actually re-quoting the quotes from the digitalmars d docs: http://digitalmars.com/d/2.0/template.html I think that I can safely say that nobody understands template mechanics. -- Richard Deyman http://digitalmars.com/d/2.0/templates-revisited.html "What I am going to tell you about is what we teach our programming students in the third or fourth year of graduate school... It is my task to convince you not to turn away because you don't understand it. You see my programming students don't understand it... That is because I don't understand it. Nobody does." -- Richard Deeman Also I hope this quote summarizes some of the viewpoints of ranges implemented through templates: "If a nuke had a single big red button as a detonator, then you have a lot of power and that is very easy to use. Doesn't necessarily make it the right weapon for the job though."
Jun 19 2009
Steve Teale wrote:template isInputRange(R) { enum bool isInputRange = is(typeof( { R r; // can define a range object if (r.empty) {} // can test for empty r.popFront; // can invoke next auto h = r.front; // can get the front of the range }())); } I can not possibly be the only D enthusiast who finds this completely incomprehensible.Yeah, that one is a bit tricky, and what makes it worse is that it seems officially sanctioned by Walter/Andrei as the "right way" to check if a type supports some operations. Basically, if you have: is(typeof({ }())); this means "if I made a function containing , would that function compile?". It's a hack which stems from the way the is expression works.What is a range?As others have mentioned, it's just a struct (or other type) that happens to support certain operations.
Jun 18 2009
Robert Fraser wrote:Yeah, that one is a bit tricky, and what makes it worse is that it seems officially sanctioned by Walter/Andrei as the "right way" to check if a type supports some operations. Basically, if you have:Oh, finally someone who shares my concerns! I fear the alternatives would require to much thought and implementation/testing work, so that our gurus prefer the current approach, despite that the semantic of the code depends on silent compilation failures. (Just like SFINAE, maybe even worse.)is(typeof({ }())); this means "if I made a function containing , would that function compile?". It's a hack which stems from the way the is expression works.Your example doesn't compile right now. But if you use a string mixin, the code doesn't even have to be syntactically/lexically valid: is(typeof({ mixin(" "); }))
Jun 18 2009
grauzone Wrote:Your example doesn't compile right now.The " " was meant as an example to be replaced with any code. Yeah, you probably knew that.But if you use a string mixin, the code doesn't even have to be syntactically/lexically valid: is(typeof({ mixin(" "); }))True -- both these features (string mixins and is-expressions) are rife with pitfalls. But they're both very useful features (if you get rid of string mixins, 25% of my code will stop compiling...). Silent compilation is dangerous indeed, but also very powerful. I was just suggesting we need a better syntax, but I realized we have one: __traits(compiles). Why Andrei isn't using this is the real mystery.
Jun 18 2009
Steve Teale:I realize that some people with an IQ of 580 will find my questions naive and misguided - not to mention impertinent, but it seems to me that one of the responsibilities of being a leader is to explain to less gifted followers what the fuck is going on.<If you read the book by Andrei you will probably find enough explanations. But we can also write a Wiki page (see at end of this post). --------------------- Robert Fraser:I was just suggesting we need a better syntax, but I realized we have one: __traits(compiles).<__traits() isn't a good looking syntax, and its semantic looks almost like a random conflation/accretion of too many different things (and some of them can be done better in other ways). For example in my dlibs I have templates for the following purposes (and most of them are present in Tango too): isAssociativeArray isFloating isIntegral isStaticArray isUnsigned So instead of writing: __traits(isStaticArray, x) I write in D1: IsStaticArray!(x) In D2 you can write: IsStaticArray!x That looks better than the traits. (The syntax of is() too looks like an accretion of mixed things).Why Andrei isn't using this is the real mystery.<Maybe he agrees with me that __traits is not nice looking :-) Those underscores make it look like a temporary functionality. is(typeof()) has purposes similar to __traits(compiles, ). Having two syntaxes to do the same thing may be bad. Let's try using traits as you suggest. This it he original written by Steve Teale (copied from elsewhere): template isInputRange(R) { enum bool isInputRange = is(typeof( { R r; // can define a range object if (r.empty) {} // can test for empty r.popFront; // can invoke next auto h = r.front; // can get the front of the range }())); } This may be the traits version (there is no () after the {} after the "compiles"): template isInputRange(R) { enum bool isInputRange = __traits(compiles, { R r; // can define a range object if (r.empty) {} // can test for empty r.popFront; // can invoke next auto h = r.front; // can get the front of the range }); } I don't know if this is correct, but if it's correct, is it better looking? It looks almost the same to me. Both traits() and is() need a more clean and logic redesign, to move elsewhere some of their purposes, and to avoid duplication in their purposes. Andrei has shown to be able to improve the API of the regex module, so maybe he can find a better design for is() and traits(). If types become first-class at compile time, of type "type", then you can remove some purposes from is() too, you can do: type t1 = int; type t2 = float; static if (t1 != t2) {... Instead of: alias int t1; alias float t2; static if (!is(t1 == t2)) {... -------------------------- Derek Parnell: Thank you Derek Parnell for your nice summary about ranges: with to your post my understanding of this topic has gone from 10% to 15% :-) There are things I don't understand from what you have written:OutputRange - This is an InputRange with the extra capability of being able to add elements to the range. In addition to the InputRange methods, it must also provide a method that adds a new element to the range, such that it becomes the current element. That method must be called 'put(E)' where 'E' is the new element.<I guess a single linked list can be seen as an OutputRange then. You can add an item where you are and scan it forward (unfortunately linked listes today are dead, they are never an efficient solution on modern CPUs) In what othr situations you may use/need an OutputRange? In a file, as in a stack, you can only add in a very specific point (the end, in files, or replace the current item).ForwardRange - This is an InputRange with the extra capability of being able checkpoint the current first 'cursor' position simply by copying the range. When you copy an plain InputRange the copied range starts again from the absolute first element, but a copied ForwardRange starts at whatever was the current first element in the source range.<I don't understand and I don't know what checkpointing may mean there. I suggest to explain those things better, and then add 3 or more examples (very different from each other, complete, real-world and ready-to-be-copied-pasted-and-run, like you can find in every page of Borland Delphi documentation) for each kind of range. And then to put the page on the D Wiki :-)Now I admit that these are not method names I would have choosen, as I would have preferred names more like<Andrei has shown that inventing very good names for those methods isn't easy... And putting lot of uppercase letters in the middle of those names isn't nice, nor handy, and it's visually noisy. Bye, bearophile
Jun 18 2009
On Thu, 18 Jun 2009 21:00:08 -0400, bearophile wrote:Thank you Derek Parnell for your nice summary about ranges: with to your post my understanding of this topic has gone from 10% to 15% :-)LOL ... glad to have helped a tiny bit.There are things I don't understand from what you have written:I haven't got a clue. I'm only trying to put into simpler words what I read in the official documentation.OutputRange ...In what othr situations you may use/need an OutputRange?It's just a way to save your place in an iteration so that presumably you can come back to that spot later on.ForwardRange ...I don't understand and I don't know what checkpointing may mean there.I suggest to explain those things better, and then add 3 or more examples (very different from each other, complete, real-world and ready-to-be-copied-pasted-and-run, like you can find in every page of Borland Delphi documentation) for each kind of range. And then to put the page on the D Wiki :-)That would be nice. Hmmm... I'll see if I can do something ...Yes, he certainly has.Now I admit that these are not method names I would have choosen ...Andrei has shown that inventing very good names for those methods isn't easy...And putting lot of uppercase letters in the middle of those names isn't nice, nor handy, and it's visually noisy.Eye-of-the-beholder situation. Whether one uses "getelement", "get_element", "GetElement", "Get_Element", "getElement", "GETELEMENT", "element.get", ... is beside the point. What I was trying to show was that the current names do not intuitively tell me what is the purpose of the methods. Does 'empty()' return a Boolean that tells me if the set is empty or not, or does it return an empty set, or does it cause the set to become empty, ??? A method name that consists of a single word that can be interpreted as an adjective or a verb or a noun, etc, is ambiguous, IMO. That is why in imperative languages I prefer to see method names that reduce the potential for ambiguous interpretations by using the form <verb>[<adjective>]<noun> is_empty get_front add_element get_background_color etc ... Of course, if an unambiguous name exists it should be used, and there are also abbreviations that can be employed. But anyhow, I digress as this is just a personal style issue and not worth discussing at this point. -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
Jun 18 2009
bearophile wrote:template isInputRange(R) { enum bool isInputRange = __traits(compiles, { R r; // can define a range object if (r.empty) {} // can test for empty r.popFront; // can invoke next auto h = r.front; // can get the front of the range }); } I don't know if this is correct, but if it's correct, is it better looking? It looks almost the same to me.Eh, it has the word "compiles" in it... You're right, though, it's not great.I guess a single linked list can be seen as an OutputRange then. You can add an item where you are and scan it forward (unfortunately linked listes today are dead, they are never an efficient solution on modern CPUs)LinkedList!(T) is basically useless. But how many times have you used a structure with a "next" and/or "previous" pointer? How about separate chaining in hash tables? "parent" pointers (forms a linked list up to the root for trees, also applies to GUI widgets, French fries, directory hierarchies, etc.)? Linked lists are *everywhere*, they're just generally implicit in some structure and not very long.In what othr situations you may use/need an OutputRange? In a file, as in a stack, you can only add in a very specific point (the end, in files, or replace the current item).I think an OutputRange doesn't have to be an InputRange. It just needs put().
Jun 18 2009
On Thu, 18 Jun 2009 19:07:06 -0700, Robert Fraser wrote:I think an OutputRange doesn't have to be an InputRange. It just needs put().You're right. I misread the documentation on that one. -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
Jun 18 2009
bearophile wrote:Steve Teale:we all know that D's compile-time features are a complete mess. also, duck-typing IMHO has no place in a statically typed language, that's just inconsistent for the language as a whole and confusing for the users.I realize that some people with an IQ of 580 will find my questions naive and misguided - not to mention impertinent, but it seems to me that one of the responsibilities of being a leader is to explain to less gifted followers what the fuck is going on.<If you read the book by Andrei you will probably find enough explanations. But we can also write a Wiki page (see at end of this post). --------------------- Robert Fraser:I was just suggesting we need a better syntax, but I realized we have one: __traits(compiles).<__traits() isn't a good looking syntax, and its semantic looks almost like a random conflation/accretion of too many different things (and some of them can be done better in other ways). For example in my dlibs I have templates for the following purposes (and most of them are present in Tango too): isAssociativeArray isFloating isIntegral isStaticArray isUnsigned So instead of writing: __traits(isStaticArray, x) I write in D1: IsStaticArray!(x) In D2 you can write: IsStaticArray!x That looks better than the traits. (The syntax of is() too looks like an accretion of mixed things).Why Andrei isn't using this is the real mystery.<Maybe he agrees with me that __traits is not nice looking :-) Those underscores make it look like a temporary functionality. is(typeof()) has purposes similar to __traits(compiles, ). Having two syntaxes to do the same thing may be bad. Let's try using traits as you suggest. This it he original written by Steve Teale (copied from elsewhere): template isInputRange(R) { enum bool isInputRange = is(typeof( { R r; // can define a range object if (r.empty) {} // can test for empty r.popFront; // can invoke next auto h = r.front; // can get the front of the range }())); } This may be the traits version (there is no () after the {} after the "compiles"): template isInputRange(R) { enum bool isInputRange = __traits(compiles, { R r; // can define a range object if (r.empty) {} // can test for empty r.popFront; // can invoke next auto h = r.front; // can get the front of the range }); } I don't know if this is correct, but if it's correct, is it better looking? It looks almost the same to me. Both traits() and is() need a more clean and logic redesign, to move elsewhere some of their purposes, and to avoid duplication in their purposes. Andrei has shown to be able to improve the API of the regex module, so maybe he can find a better design for is() and traits(). If types become first-class at compile time, of type "type", then you can remove some purposes from is() too, you can do: type t1 = int; type t2 = float; static if (t1 != t2) {... Instead of: alias int t1; alias float t2; static if (!is(t1 == t2)) {... -------------------------- Derek Parnell: Thank you Derek Parnell for your nice summary about ranges: with to your post my understanding of this topic has gone from 10% to 15% :-) There are things I don't understand from what you have written:OutputRange - This is an InputRange with the extra capability of being able to add elements to the range. In addition to the InputRange methods, it must also provide a method that adds a new element to the range, such that it becomes the current element. That method must be called 'put(E)' where 'E' is the new element.<I guess a single linked list can be seen as an OutputRange then. You can add an item where you are and scan it forward (unfortunately linked listes today are dead, they are never an efficient solution on modern CPUs) In what othr situations you may use/need an OutputRange? In a file, as in a stack, you can only add in a very specific point (the end, in files, or replace the current item).ForwardRange - This is an InputRange with the extra capability of being able checkpoint the current first 'cursor' position simply by copying the range. When you copy an plain InputRange the copied range starts again from the absolute first element, but a copied ForwardRange starts at whatever was the current first element in the source range.<I don't understand and I don't know what checkpointing may mean there. I suggest to explain those things better, and then add 3 or more examples (very different from each other, complete, real-world and ready-to-be-copied-pasted-and-run, like you can find in every page of Borland Delphi documentation) for each kind of range. And then to put the page on the D Wiki :-)Now I admit that these are not method names I would have choosen, as I would have preferred names more like<Andrei has shown that inventing very good names for those methods isn't easy... And putting lot of uppercase letters in the middle of those names isn't nice, nor handy, and it's visually noisy. Bye, bearophile
Jun 19 2009
Robert Fraser Wrote:Steve Teale wrote:So does this mean that interfaces are just a tragic mistake. I'd always thought that what you said was a pretty good description of what an interface is!template isInputRange(R) { enum bool isInputRange = is(typeof( { R r; // can define a range object if (r.empty) {} // can test for empty r.popFront; // can invoke next auto h = r.front; // can get the front of the range }())); } I can not possibly be the only D enthusiast who finds this completely incomprehensible.Yeah, that one is a bit tricky, and what makes it worse is that it seems officially sanctioned by Walter/Andrei as the "right way" to check if a type supports some operations. Basically, if you have: is(typeof({ }())); this means "if I made a function containing , would that function compile?". It's a hack which stems from the way the is expression works.What is a range?As others have mentioned, it's just a struct (or other type) that happens to support certain operations.
Jun 19 2009
Steve Teale wrote: ...Robert Fraser Wrote:Could you explain why that makes interfaces a mistake? Interfaces (as in classes implementing an interface) do provide dynamic polymorphism which these compile time constraints (or 'concepts' in STL terms) don't.As others have mentioned, it's just a struct (or other type) that happens to support certain operations.So does this mean that interfaces are just a tragic mistake. I'd always thought that what you said was a pretty good description of what an interface is!
Jun 19 2009
Steve Teale wrote:Robert Fraser Wrote:IMHO, duck-typing in D is a tragic mistake... This should have been implemented with compile time interfaces.Steve Teale wrote:So does this mean that interfaces are just a tragic mistake. I'd always thought that what you said was a pretty good description of what an interface is!template isInputRange(R) { enum bool isInputRange = is(typeof( { R r; // can define a range object if (r.empty) {} // can test for empty r.popFront; // can invoke next auto h = r.front; // can get the front of the range }())); } I can not possibly be the only D enthusiast who finds this completely incomprehensible.Yeah, that one is a bit tricky, and what makes it worse is that it seems officially sanctioned by Walter/Andrei as the "right way" to check if a type supports some operations. Basically, if you have: is(typeof({ }())); this means "if I made a function containing , would that function compile?". It's a hack which stems from the way the is expression works.What is a range?As others have mentioned, it's just a struct (or other type) that happens to support certain operations.
Jun 19 2009
Yigal Chripun wrote: ...IMHO, duck-typing in D is a tragic mistake... This should have been implemented with compile time interfaces.Care to provide arguments?
Jun 19 2009
Lutger Wrote:Yigal Chripun wrote: ...ignorance 'n' arrogance should do.IMHO, duck-typing in D is a tragic mistake... This should have been implemented with compile time interfaces.Care to provide arguments?
Jun 19 2009
superdan wrote:Lutger Wrote:Those I hold I high esteem, you convinced me.Yigal Chripun wrote: ...ignorance 'n' arrogance should do.IMHO, duck-typing in D is a tragic mistake... This should have been implemented with compile time interfaces.Care to provide arguments?
Jun 19 2009
Lutger wrote:Yigal Chripun wrote: ...duck typing makes more sense in dynamic languages like Ruby which is famous for it. in static languages I as a user prefer to trade flexibility due to duck-typing for compile time checks. yes, at compile time, duck typing and (compile-time) interfaces are basically the same thing, but since the rest of the language uses formal interfaces, it is more consistent (and easier to understand) to use the same approach at compile-time as well. point in case, look how much unnecessary confusion Ranges cause which would be eliminated had D allowed for compile-time interfaces. i.e. Interface I { .. } struct S : I { ... } this is basically the same as C++ concepts only without redundant and confusing syntax. 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.IMHO, duck-typing in D is a tragic mistake... This should have been implemented with compile time interfaces.Care to provide arguments?
Jun 19 2009
Yigal Chripun:point in case, look how much unnecessary confusion Ranges cause which would be eliminated had D allowed for compile-time interfaces.What are interfaces from the point of view of the compiler? Bye, bearophile (P.S.: Is Walter around still? He's pretty silent lately. Talking when he's not around looks quite academic).
Jun 19 2009
== Quote from bearophile (bearophileHUGS lycos.com)'s articleYigal Chripun:Abstract classes with only pure virtual functions. In other words, basically under the hood, an interface is just the layout of a vtable. This actually leads to a comment I want to make in the wider debate: I personally find explicit interfaces really, really annoying and I think that duck typing is by far the most intuitive type system there is. I used to program primarily in duck typed languages and resort to every kludge imaginable for speed. What attracted me to D was that the templates and type inference are so powerful that I almost feel like it's still a duck typed language, but much faster and with more error checking. I guess that's why I like ranges so much. 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.point in case, look how much unnecessary confusion Ranges cause which would be eliminated had D allowed for compile-time interfaces.What are interfaces from the point of view of the compiler?
Jun 19 2009
dsimcha:Abstract classes with only pure virtual functions. In other words, basically under the hood, an interface is just the layout of a vtable.Oh, right, sorry, my question really was "What are compile-time interfaces from the point of view of the compiler?" Bye, bearophile
Jun 20 2009
dsimcha wrote:== Quote from bearophile (bearophileHUGS lycos.com)'s articleThat's run-time interfaces. compile-time interfaces are like C++ concepts.Yigal Chripun:Abstract classes with only pure virtual functions. In other words, basically under the hood, an interface is just the layout of a vtable.point in case, look how much unnecessary confusion Ranges cause which would be eliminated had D allowed for compile-time interfaces.What are interfaces from the point of view of the compiler?This actually leads to a comment I want to make in the wider debate: I personally find explicit interfaces really, really annoying and I think that duck typing is by far the most intuitive type system there is. I used to program primarily in duck typed languages and resort to every kludge imaginable for speed. What attracted me to D was that the templates and type inference are so powerful that I almost feel like it's still a duck typed language, but much faster and with more error checking. I guess that's why I like ranges so much.duck-typing has its benefits, that's for sure. it all boils down to is style issues I guess - do you prefer implicit or explicit interfaces. either are fine by me, even though it seems to me that duck-typing is more of a dynamically typed language feature but maybe my feeling here is wrong. either way, the language needs to be consistent about it in order to not confuse users unnecessarily.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.
Jun 20 2009
Yigal Chripun wrote:dsimcha wrote:It's called structural typing in static languages which is almost the same but not quite. In duck typing, you can pass an object which does not implement the 'required' interface and this is not checked until a missing method is actually called. Another way of looking at it is that the interface is determined by the path of execution, which is even more flexible than structural typing.== Quote from bearophile (bearophileHUGS lycos.com)'s articleThat's run-time interfaces. compile-time interfaces are like C++ concepts.Yigal Chripun:Abstract classes with only pure virtual functions. In other words, basically under the hood, an interface is just the layout of a vtable.point in case, look how much unnecessary confusion Ranges cause which would be eliminated had D allowed for compile-time interfaces.What are interfaces from the point of view of the compiler?This actually leads to a comment I want to make in the wider debate: I personally find explicit interfaces really, really annoying and I think that duck typing is by far the most intuitive type system there is. I used to program primarily in duck typed languages and resort to every kludge imaginable for speed. What attracted me to D was that the templates and type inference are so powerful that I almost feel like it's still a duck typed language, but much faster and with more error checking. I guess that's why I like ranges so much.duck-typing has its benefits, that's for sure. it all boils down to is style issues I guess - do you prefer implicit or explicit interfaces. either are fine by me, even though it seems to me that duck-typing is more of a dynamically typed language feature but maybe my feeling here is wrong. either way, the language needs to be consistent about it in order to not confuse users unnecessarily.
Jun 20 2009
Lutger wrote:It's called structural typing in static languages which is almost the same but not quite. In duck typing, you can pass an object which does not implement the 'required' interface and this is not checked until a missing method is actually called. Another way of looking at it is that the interface is determined by the path of execution, which is even more flexible than structural typing.This is why i don't like it in static languages. I like my type system and want the compiler to check my code.
Jun 20 2009
== Quote from Yigal Chripun (yigal100 gmail.com)'s articleSimplicity and DRY--you only need to specify what the compiler doesn't already know.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.
Jun 20 2009
dsimcha wrote:== Quote from Yigal Chripun (yigal100 gmail.com)'s articlesimplicity - 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.Simplicity and DRY--you only need to specify what the compiler doesn't already know.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.
Jun 20 2009
bearophile wrote:(P.S.: Is Walter around still? He's pretty silent lately. Talking when he's not around looks quite academic).He gave a D talk on Wednesday night. I get the feeling the next release is going to be something big.
Jun 19 2009
Yigal Chripun Wrote:Lutger wrote:yer didnt say why & this adds nutt'n'.Yigal Chripun wrote: ...duck typing makes more sense in dynamic languages like Ruby which is famous for it.IMHO, duck-typing in D is a tragic mistake... This should have been implemented with compile time interfaces.Care to provide arguments?in static languages I as a user prefer to trade flexibility due to duck-typing for compile time checks.yer dunno what yer talking about do ya. d checks duck typed shit at compile time.yes, at compile time, duck typing and (compile-time) interfaces are basically the same thing, but since the rest of the language uses formal interfaces, it is more consistent (and easier to understand) to use the same approach at compile-time as well. point in case, look how much unnecessary confusion Ranges cause which would be eliminated had D allowed for compile-time interfaces. i.e. Interface I { .. } struct S : I { ... } this is basically the same as C++ concepts only without redundant and confusing syntax.& how do ya figure tat I defines a type elementtype?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.2 me looks like yer in way over yer head.
Jun 19 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. 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.Yigal Chripun wrote: ...duck typing makes more sense in dynamic languages like Ruby which is famous for it. in static languages I as a user prefer to trade flexibility due to duck-typing for compile time checks. yes, at compile time, duck typing and (compile-time) interfaces are basically the same thing, but since the rest of the language uses formal interfaces, it is more consistent (and easier to understand) to use the same approach at compile-time as well. point in case, look how much unnecessary confusion Ranges cause which would be eliminated had D allowed for compile-time interfaces. i.e. Interface I { .. } struct S : I { ... } this is basically the same as C++ concepts only without redundant and confusing syntax.IMHO, duck-typing in D is a tragic mistake... This should have been implemented with compile time interfaces.Care to provide arguments?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 don't think the extra type checking is done, but perhaps it could be.
Jun 20 2009
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?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. 2. The syntax for this is completely alien and unreadable, at least for me. 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 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.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 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?
Jun 20 2009
Yigal Chripun wrote:Lutger wrote: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.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?Yes that is the way to go I believe. Phobos already defines a lot of these concepts so that makes it easier.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.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.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.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.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 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?
Jun 20 2009
Lutger wrote: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.
Jun 20 2009
== Quote from Yigal Chripun (yigal100 gmail.com)'s articleSteve Teale wrote:incomprehensible.Robert Fraser Wrote:Steve Teale wrote:template isInputRange(R) { enum bool isInputRange = is(typeof( { R r; // can define a range object if (r.empty) {} // can test for empty r.popFront; // can invoke next auto h = r.front; // can get the front of the range }())); } I can not possibly be the only D enthusiast who finds this completelythought that what you said was a pretty good description of what an interface is!Yeah, that one is a bit tricky, and what makes it worse is that it seems officially sanctioned by Walter/Andrei as the "right way" to check if a type supports some operations. Basically, if you have: is(typeof({ }())); this means "if I made a function containing , would that function compile?". It's a hack which stems from the way the is expression works.So does this mean that interfaces are just a tragic mistake. I'd alwaysWhat is a range?As others have mentioned, it's just a struct (or other type) that happens to support certain operations.Why? Duck typing is incredibly flexible and simple, but the downside is that, in its traditional implementation it's inefficient and only checkable at runtime. The whole beauty of D's template system is that it allows something similar to duck typing that is checked at compile time and has usually negligible (I won't say zero since object file bloat can be practically significant in a few corner cases) overhead.IMHO, duck-typing in D is a tragic mistake... This should have been implemented with compile time interfaces.
Jun 19 2009
dsimcha wrote:== Quote from Yigal Chripun (yigal100 gmail.com)'s articleIt sometimes makes up for a lack of an actual type system but it is not a true duck type system built into the language anyway as you have to go through the manual process of asking whether it is of a certain type through templates.Steve Teale wrote:incomprehensible.Robert Fraser Wrote:Steve Teale wrote:template isInputRange(R) { enum bool isInputRange = is(typeof( { R r; // can define a range object if (r.empty) {} // can test for empty r.popFront; // can invoke next auto h = r.front; // can get the front of the range }())); } I can not possibly be the only D enthusiast who finds this completelythought that what you said was a pretty good description of what an interface is!Yeah, that one is a bit tricky, and what makes it worse is that it seems officially sanctioned by Walter/Andrei as the "right way" to check if a type supports some operations. Basically, if you have: is(typeof({ }())); this means "if I made a function containing , would that function compile?". It's a hack which stems from the way the is expression works.So does this mean that interfaces are just a tragic mistake. I'd alwaysWhat is a range?As others have mentioned, it's just a struct (or other type) that happens to support certain operations.IMHO, duck-typing in D is a tragic mistake... This should have been implemented with compile time interfaces.Why? Duck typing is incredibly flexible and simple, but the downside is that, in its traditional implementation it's inefficient and only checkable at runtime. The whole beauty of D's template system is that it allows something similar to duck typing that is checked at compile time and has usually negligible (I won't say zero since object file bloat can be practically significant in a few corner cases) overhead.
Jun 19 2009
== Quote from Tim Matthews (tim.matthews7 gmail.com)'s articledsimcha wrote:No you don't, constraints are just to improve overloading capabilities and provide better error handling if you use a template wrong.== Quote from Yigal Chripun (yigal100 gmail.com)'s articleIt sometimes makes up for a lack of an actual type system but it is not a true duck type system built into the language anyway as you have to go through the manual process of asking whether it is of a certain type through templates.Steve Teale wrote:incomprehensible.Robert Fraser Wrote:Steve Teale wrote:template isInputRange(R) { enum bool isInputRange = is(typeof( { R r; // can define a range object if (r.empty) {} // can test for empty r.popFront; // can invoke next auto h = r.front; // can get the front of the range }())); } I can not possibly be the only D enthusiast who finds this completelythought that what you said was a pretty good description of what an interface is!Yeah, that one is a bit tricky, and what makes it worse is that it seems officially sanctioned by Walter/Andrei as the "right way" to check if a type supports some operations. Basically, if you have: is(typeof({ }())); this means "if I made a function containing , would that function compile?". It's a hack which stems from the way the is expression works.So does this mean that interfaces are just a tragic mistake. I'd alwaysWhat is a range?As others have mentioned, it's just a struct (or other type) that happens to support certain operations.IMHO, duck-typing in D is a tragic mistake... This should have been implemented with compile time interfaces.Why? Duck typing is incredibly flexible and simple, but the downside is that, in its traditional implementation it's inefficient and only checkable at runtime. The whole beauty of D's template system is that it allows something similar to duck typing that is checked at compile time and has usually negligible (I won't say zero since object file bloat can be practically significant in a few corner cases) overhead.
Jun 19 2009
grauzone Wrote:Robert Fraser wrote:Kind of like the oomigooli bird. Flies round in ever decreasing circles and eventually disappears up its own arsehole.Yeah, that one is a bit tricky, and what makes it worse is that it seems officially sanctioned by Walter/Andrei as the "right way" to check if a type supports some operations. Basically, if you have:Oh, finally someone who shares my concerns! I fear the alternatives would require to much thought and implementation/testing work, so that our gurus prefer the current approach, despite that the semantic of the code depends on silent compilation failures. (Just like SFINAE, maybe even worse.)is(typeof({ }())); this means "if I made a function containing , would that function compile?". It's a hack which stems from the way the is expression works.Your example doesn't compile right now. But if you use a string mixin, the code doesn't even have to be syntactically/lexically valid: is(typeof({ mixin(" "); }))
Jun 19 2009