## digitalmars.D - Rant after trying Rust a bit

• simendsjo (123/123) Jul 22 2015 Long rant ahead - a bit dipsy..
• Jack Stouffer (23/83) Jul 22 2015 Dub is endorsed by the leadership and is included in the same
• simendsjo (39/78) Jul 22 2015 Yes, I agree. I haven't done much good for the D community,
• Adam D. Ruppe (5/12) Jul 22 2015 That's what the ternary expression is for:
• simendsjo (3/16) Jul 22 2015 :) The example was written to save space. I recon you understand
• Adam D. Ruppe (7/9) Jul 23 2015 Yeah, but the if/else is one of the most useful examples of it,
• ixid (3/12) Jul 23 2015 If we had a clean sheet wouldn't it be better to have if return a
• Jonathan M Davis (8/22) Jul 23 2015 Maybe, but the ternary operator is a lot less verbose, and from
• Jacob Carlborg (20/24) Jul 23 2015 Scala implements it without those requirements. I looks exactly like in
• Ziad Hatahet via Digitalmars-d (23/28) Jul 23 2015 The ternary operator becomes much harder to read the moment you have mor...
• Adam D. Ruppe (16/22) Jul 23 2015 I think it is actually kinda pretty:
• dennis luehring (2/3) Jul 24 2015 and allowes const correctness without functions
• Andrei Alexandrescu (3/5) Jul 25 2015 Yet it requires braces around the arms. Rust taketh away, Rust requireth...
• Timon Gehr (2/7) Jul 26 2015 It's arguably a better trade-off.
• Enamex (4/13) Jul 26 2015 Yeah. Instead of sometimes requiring just parentheses and
• Andrei Alexandrescu (2/15) Jul 23 2015 Possibly, but then you'd need to have while return a value. -- Andrei
• Walter Bright (17/19) Jul 23 2015 Then we'd start seeing code like:
• Tofu Ninja (24/45) Jul 23 2015 I think I agree on the if else issue, seems arbitrary as we
• Tofu Ninja (13/67) Jul 23 2015 Actually now that I think about it, I think I would expect auto a
• Jacob Carlborg (4/25) Jul 24 2015 How does UFCS apply here? There isn't even a dot in the code.
• Tofu Ninja (3/44) Jul 24 2015 Is omitting the () not part of ufcs? Or does it have some other
• Jonathan M Davis (14/16) Jul 24 2015 No. That's simply omitting the parens from a function call that
• Tofu Ninja (20/36) Jul 24 2015 Ok I kinda assumed they were both included in the concept of
• Enamex (17/30) Aug 05 2015 Not crazy about your last point, TBH. Personally I really dislike
• ixid (19/31) Jul 24 2015 As opposed to:
• Walter Bright (18/52) Jul 24 2015 Nope. As opposed to:
• ixid (6/23) Jul 24 2015 My point was that you can effectively do the ugly thing already
• "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (4/8) Jul 24 2015 Agreed. It makes more sense for switch/match, but D's switch
• Timon Gehr (5/10) Jul 24 2015 This is obviously not true:
• jmh530 (13/20) Jul 22 2015 I prefer this example from one of the various Rust tutorials
• John Colvin (16/38) Jul 22 2015 Admittedly nowhere near as clean, but if you can bear to see the
• jmh530 (4/7) Jul 22 2015 Honestly, I don't mind doing things the D way (and I probably
• Andre Kostur (4/46) Jul 23 2015 Shouldn't that be its own function anyway? If you needed it in one
• Andrei Alexandrescu (12/34) Jul 23 2015 I used to be quite jazzed about the everything-is-an-expression mantra,
• shannon mackey (7/23) Jul 23 2015 "> On Wednesday, 22 July 2015 at 19:41:16 UTC, Jack Stouffer
• shannon mackey (3/24) Jul 23 2015 Sorry, I didn't read to the end of the thread, and this has been
• bachmeier (10/17) Jul 23 2015 I'd challenge you to write something other than 100% praise for
• Jacob Carlborg (5/7) Jul 22 2015 It's not available in the Downloads, Compiler & Tools or Resources
• Joakim (4/10) Jul 23 2015 That's because it has its own top-level link in the sidebar, More
• Jacob Carlborg (5/8) Jul 23 2015 Ah, right, I completely forgot about that. But one could expect the
• Alex Parrill (20/34) Jul 22 2015 I'm not at all familiar with Rust, so forgive me if I'm
• simendsjo (18/37) Jul 22 2015 Exactly. D is a lot more flexible, but looking just at
• John (7/15) Jul 28 2015 I wish all the D related posts go under the sub-reddit
• John (5/11) Jul 29 2015 To make my point more clear, the other language groups post their
• Jacob Carlborg (14/22) Jul 22 2015 I would be a lot more cleaner to be able to do this:
• Dicebot (14/14) Jul 22 2015 Traits system is awesome and pure win. Pattern matching is not
• simendsjo (22/38) Jul 22 2015 The fact that you can use pattern matching many places makes it
• Jonathan M Davis (12/23) Jul 22 2015 What I understood that he meant was that it was interesting to
• Dicebot (15/32) Jul 22 2015 Compared to string mixins they require much much more effort to
• Brad Anderson (10/18) Jul 23 2015 I've read that someone managed to implement compile time regex
• Paolo Invernizzi (4/7) Jul 23 2015 +1, from a business D user!
• Laeeth Isharc (10/12) Jul 23 2015 would you care to write more on this as a blog post, making it
• Dicebot (37/50) Jul 23 2015 AFAIR Don had quite a good summary at DConf 2013
• jmh530 (56/78) Jul 22 2015 Rust only has structs. I'm not as familiar with them so it's not
• jmh530 (18/21) Jul 23 2015 I think I had a few misconception about D interfaces. The
• Adam D. Ruppe (48/55) Jul 23 2015 It isn't really expanded upon, but the : Interfaces at the top
• jmh530 (47/64) Jul 23 2015 I see that now. It's there, but it's practically a throw-a-way
• H. S. Teoh via Digitalmars-d (12/40) Jul 23 2015 [...]
• Andrei Alexandrescu (3/6) Jul 23 2015 Focused, specialized, and clear pull requests are the way to go here.
• jmh530 (6/9) Jul 23 2015 Again, I just found a reference to this behavior in the template
• Adam D. Ruppe (54/72) Jul 23 2015 Yeah, I agree. Here's what I've been trying to write:
• jmh530 (8/27) Jul 23 2015 I actually just had your D Cookbook delivered a few days ago.
• Adam D. Ruppe (33/40) Jul 23 2015 That gets better! I didn't realize how the editing process worked
• =?UTF-8?Q?Tobias=20M=C3=BCller?= (8/10) Jul 23 2015 That's actually very outdated information. Two of the four pointer types
• Ziad Hatahet via Digitalmars-d (6/9) Jul 23 2015 Rust has ditched the ~ and @ syntax for pointers for a long time now. Fo...
• Bienlein (9/16) Jul 26 2015 What some people call "interface inheritance" in Rust is not
• rsw0x (4/5) Jul 22 2015 I think rust makes the ugliness of D's "push everything into
• Andrei Alexandrescu (2/7) Jul 23 2015 Care for a list? Thanks! -- Andrei
• QAston (38/48) Jul 23 2015 Ugliness is in the eyes of the beholder, for me for example is
• simendsjo (3/4) Jul 23 2015 Someone else wrote about the "ugliness", not me.
• ponce (23/56) Jul 22 2015 I think this tells something foremost about the size of the
• Jacob Carlborg (5/11) Jul 23 2015 I had a look at the example for "visit" and looked at the
• Nick B (6/12) Jul 23 2015 But does it reflect the size of the community? Look at these
• Jacob Carlborg (36/71) Jul 22 2015 I completely agree and don't really like the approach D has implemented
• Walter Bright (7/16) Jul 23 2015 Consider that template constraints can be arbitrarily complex, and can e...
• Andrei Alexandrescu (8/28) Jul 23 2015 Agreed. And that's just scratching the surface.
• Dicebot (28/55) Jul 23 2015 It is definitely a big issue for designing more advanced generic
• Walter Bright (4/15) Jul 23 2015 D interface types also produce the simplistic dispatch table, and if you...
• Dicebot (24/50) Jul 23 2015 I am not sure how it applies. My point was about the fact that
• Walter Bright (15/36) Jul 23 2015 D interfaces (defined with the 'interface' keyword) are simple dispatch ...
• Dicebot (38/56) Jul 23 2015 Correct indeed, though I don't feel it is much of a difference
• Walter Bright (30/59) Jul 23 2015 That is correct. Some care must be taken that the mock types used in the...
• Dicebot (37/110) Jul 25 2015 Sorry for somewhat delayed answer - not sure if anyone has
• Andrei Alexandrescu (8/13) Jul 25 2015 Test coverage shouldn't totter up and down as application code is
• Dicebot (18/36) Jul 25 2015 Does word "refactoring" or "adding new features" ring a bell? In
• Walter Bright (2/6) Jul 25 2015 A good case. https://issues.dlang.org/show_bug.cgi?id=14825
• Andrei Alexandrescu (4/9) Jul 25 2015 Is this new? I agree we should allow it, but I don't think it was added
• deadalnix (7/19) Jul 25 2015 This is not in the language and should not be added lightly.
• Timon Gehr (3/13) Jul 26 2015 In any case, the painful thing about the RangeObject interfaces is that
• Andrei Alexandrescu (4/52) Jul 25 2015 Yah, building stuff in does have its advantages.
• Dicebot (11/21) Jul 25 2015 I feel it is not as much about "built-in vs library", but
• Enamex (4/12) Jul 25 2015 Sorry, I don't quite get this. How is the most loved feature of
• Dicebot (5/20) Jul 26 2015 I have just checked the dictionary and it simply a matter of
• Walter Bright (2/8) Jul 23 2015 But if a unit test fails at instantiating it, it fails at compile time.
• H. S. Teoh via Digitalmars-d (6/17) Jul 23 2015 That assumes the template author is diligent (foolhardy?) enough to
• Walter Bright (5/7) Jul 23 2015 No, only each branch of the template code must be instantiated, not ever...
• Vlad Levenfeld (16/25) Jul 23 2015 I dunno about good practices but I have some use cases.
• H. S. Teoh via Digitalmars-d (62/72) Jul 23 2015 OK, I jumped into the middle of this discussion so probably I'm speaking
• Dicebot (4/6) Jul 23 2015 This is exactly one major advantage of Rust traits I have been
• Walter Bright (22/27) Jul 23 2015 Consider the following:
• =?UTF-8?Q?Tobias=20M=C3=BCller?= (11/49) Jul 23 2015 You may aus well ask "How do interfaces in OO programming deal with this...
• H. S. Teoh via Digitalmars-d (52/104) Jul 23 2015 Well, this is where the whole idea of Concepts comes from. Rather than
• Walter Bright (3/11) Jul 23 2015 That's true but it changes nothing about what I wrote. Just replace "has...
• Andrei Alexandrescu (5/17) Jul 25 2015 As I argued in "Generic Programming Must Go", this does work on
• Walter Bright (5/6) Jul 23 2015 It's a good question. And the answer is, the top level function does not...
• Jacob Carlborg (6/11) Jul 24 2015 If you have an interface and then doing a dynamic cast then you're doing...
• Walter Bright (4/12) Jul 24 2015 Dynamic cast is no different from QueryInterface(), which is how it's do...
• Jacob Carlborg (4/8) Jul 25 2015 I'm not familiar with QueryInterface():
• Jonathan M Davis (42/51) Jul 25 2015 It's part of COM. Basically, it's equivalent to doing something
• Paulo Pinto (7/61) Jul 25 2015 It makes sense when one thinks about pure interfaces and
• deadalnix (10/19) Jul 24 2015 It is not required and probably shouldn't, or at least shouldn't
• =?UTF-8?Q?Tobias=20M=C3=BCller?= (5/13) Jul 25 2015 IMO dynamic casting or QueryInterface() is a sign of bad design.
• vitus (8/42) Jul 23 2015 Fun begins:
• Artur Skawina via Digitalmars-d (43/63) Jul 24 2015 The fact that some other concept/trait implementations got it wrong
• Walter Bright (12/40) Jul 24 2015 That really isn't any different from a user calling it with a 'hasPrefix...
• H. S. Teoh via Digitalmars-d (60/62) Jul 24 2015 [...]
• Walter Bright (4/6) Jul 24 2015 Another way is to list everything you accept in the constraint, and then...
• Jonathan M Davis (36/43) Jul 24 2015 I've considered off and on arguing that a function like find
• Walter Bright (13/15) Jul 25 2015 Consider the following pattern, which I see often in Phobos:
• Jonathan M Davis (97/114) Jul 25 2015 Yeah, though, I believe that Andrei has argued against that every
• Walter Bright (6/12) Jul 25 2015 I also sometimes see:
• Andrei Alexandrescu (2/19) Jul 25 2015 There are problems with that but I do agree with the sentiment. -- Andre...
• Enamex (4/24) Jul 25 2015 The example(s) is confusing me. foo!(first)(second); isn't
• Nicholas Wilson (9/38) Jul 25 2015 No. Yes. first is a compile time parameter and can be anything.
• Manu via Digitalmars-d (7/23) Jul 25 2015 This! I've felt this way with phobos in particular for ages.
• Andrei Alexandrescu (2/7) Jul 25 2015 I think we're in good shape there. -- Andrei
• Artur Skawina via Digitalmars-d (35/63) Jul 24 2015 The difference is that right now the developer has to write a unit-test
• Jonathan M Davis (7/13) Jul 24 2015 But you see. This is exactly wrong attitude. Why on earth should
• Tofu Ninja (2/5) Jul 24 2015 Because that is 99% of D users...
• Jonathan M Davis (12/17) Jul 24 2015 If so, they have no excuse. D has made it ridiculously easy to
• Tofu Ninja (4/24) Jul 24 2015 I ment 99% don't 100% unit tests, but even close to 100% is still
• Justin Whear (13/34) Jul 24 2015 Commercial (though in-house) D library and tools writer here. We run
• Walter Bright (3/12) Jul 24 2015 Right on, Jonathan!
• Jonathan M Davis (27/34) Jul 25 2015 Yeah. I wonder what would happen with some of the folks that I've
• Walter Bright (22/31) Jul 25 2015 It's still unusual to have 100% coverage in Phobos, and this is not beca...
• Jonathan M Davis (60/81) Jul 25 2015 Yeah. I thought there had been a change to make it so that the
• Walter Bright (14/26) Jul 25 2015 There's another thing I discovered. If functions are broken up into smal...
• Jonathan M Davis (11/21) Aug 04 2015 LOL. I finally got some bugs sorted out on the project that I'm
• Jacob Carlborg (5/9) Jul 28 2015 Most modern languages are capable to implement something similar or
• Artur Skawina via Digitalmars-d (5/9) Jul 24 2015 How exactly does making it harder to write tests translate into
• jmh530 (62/75) Jul 24 2015 I'm a little confused here. I seem to be of the belief that D's
• Walter Bright (6/9) Jul 24 2015 As I replied earlier,
• Timon Gehr (4/17) Jul 24 2015 This kind of testing for conformance can easily be allowed for
• Jacob Carlborg (6/18) Jul 24 2015 I don't see the difference compared to a regular parameter. If you don't...
• Walter Bright (9/12) Jul 24 2015 So constraints then will be an all-or-nothing proposition? I believe tha...
• Tofu Ninja (11/26) Jul 24 2015 But thats exactly how normal interfaces work...
• deadalnix (2/31) Jul 24 2015 C: do a runtime check or downcast.
• Walter Bright (4/13) Jul 24 2015 No it isn't. Google QueryInterface(). Nobody lists all the interfaces at...
• Tofu Ninja (6/10) Jul 24 2015 The only time you don't use the right interface for your needs is
• Jonathan M Davis (6/16) Jul 24 2015 I confess that I've always thought that QueryInterface was a
• Walter Bright (7/8) Jul 24 2015 Specifying every interface that a type must support at the top of the hi...
• Tofu Ninja (7/12) Jul 24 2015 But that is what every one does.... you are really making me
• Walter Bright (3/5) Jul 24 2015 The one type that encompasses everything defeats the whole point of type...
• Jonathan M Davis (44/54) Jul 24 2015 Well, in most code, I would think that you should be getting the
• "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (4/12) Jul 25 2015 The point of having a type system is to catch as many mistakes at
• Walter Bright (9/11) Jul 25 2015 Again, the D constraint system *is* a compile time system, and if the te...
• Tofu Ninja (4/7) Jul 25 2015 But only if the template gets instantiated with a bad type. Unit
• Andrei Alexandrescu (2/8) Jul 25 2015 I disagree. -- Andrei
• deadalnix (5/12) Jul 25 2015 This unitest argument is becoming ridiculous. Unless some strong
• Tofu Ninja (2/7) Jul 25 2015 +1000
• Andrei Alexandrescu (3/16) Jul 26 2015 To me that's self understood. Run time is fundamentally different from
• Walter Bright (3/20) Jul 26 2015 Unit tests are also not exclusively about runtime. Using a unit test to
• deadalnix (19/21) Jul 26 2015 Yes, it test the instantiation to some extent. It tests that the
• Walter Bright (20/38) Jul 26 2015 If the template constraint is 'isInputRange', and you pass it an 'InputR...
• deadalnix (36/79) Jul 27 2015 That is not enough, it has been covered. It is clear that this is
• Tofu Ninja (7/8) Jul 27 2015 I really don't get how the mess of unittests, mock data types,
• Andrei Alexandrescu (25/35) Jul 27 2015 Clearly adding more language features to D would have certain payoff.
• Jonathan M Davis (5/7) Jul 27 2015 LOL. That's why I was originally planning to not say anything in
• jmh530 (3/10) Jul 27 2015 Your comments were very clear and much appreciated, but I see the
• Jonathan M Davis (38/50) Jul 27 2015 Well, I ended up commenting, because there were some very import
• Chris (20/74) Jul 28 2015 Very wise. More often than not it is useless and irrelevant to
• "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (3/5) Jul 29 2015 I believe most Algol-like languages outside the C-family have
• Chris (4/9) Jul 30 2015 So can you tell me what pitfalls there are? Sure people must have
• Timon Gehr (4/12) Jul 30 2015 They are the intersection of nasty bugs involving ?: and nasty bugs
• Chris (8/25) Jul 30 2015 My point was that any (new) feature introduces its own problems.
• Andrei Alexandrescu (2/23) Jul 26 2015 YES! For templates unittests have a dual role. -- Andrei
• Russel Winder via Digitalmars-d (16/20) Jul 25 2015 [[=E2=80=A6]
• Brendan Zabarauskas (8/22) Jul 25 2015 I think the point is that trait based constraints force
• "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (14/28) Jul 25 2015 Well, I am not sure if the flexibility scales up when clever
• =?UTF-8?Q?Tobias=20M=C3=BCller?= (4/6) Jul 25 2015 I don't think NNTP has any problems with that. My newsreader displays it
• deadalnix (6/18) Jul 25 2015 Obvious, everything is at compile time here. Still, there is 2
• Andrei Alexandrescu (5/25) Jul 26 2015 Probably that's the root of all disagreement. So we have template
• deadalnix (9/13) Jul 26 2015 It is closer, but it doesn't matter for the argument being made.
• "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (6/9) Jul 27 2015 It (or more likely, his user agent) does deal with them well.
• Andrei Alexandrescu (4/10) Jul 25 2015 And that simile would be superficial.
• Walter Bright (22/25) Jul 25 2015 I am not explaining this properly. Trying again,
• deadalnix (8/26) Jul 25 2015 Well yes and now. When you design is new and shinny, sure, that
• Tofu Ninja (2/3) Jul 24 2015 Then make yourself more clear...
• deadalnix (21/36) Jul 24 2015 In many language you have an instaceof keyword or something
• Walter Bright (2/27) Jul 24 2015 I don't see how this addresses my point at all. This is very frustrating...
• Tofu Ninja (23/68) Jul 24 2015 The same "problems" that you are claiming will happen with with
• Andrei Alexandrescu (13/17) Jul 25 2015 Every time this (or really any apology of C++ concepts) comes up, the
• deadalnix (5/24) Jul 25 2015 So far, you've just rehashed bogous claim made for dynamic typing
• deadalnix (11/56) Jul 25 2015 I think it does. Your point is essentially an argument by
• Walter Bright (10/12) Jul 25 2015 You say that interfaces do the same thing. So please show how it's done ...
• deadalnix (2/15) Jul 26 2015 I'm not sure what is the problem here.
• Walter Bright (2/19) Jul 26 2015 I give up.
• Jonathan M Davis (82/101) Jul 26 2015 The problem here is that if you're dealing with traits or
• Walter Bright (2/3) Jul 26 2015 Thank you, Jonathan!
• deadalnix (40/143) Jul 27 2015 So, if I translate to regular D, here is what I get :
• Walter Bright (2/3) Jul 27 2015 I asked how you'd solve the problem with interfaces.
• Andrei Alexandrescu (4/7) Jul 25 2015 No, that's not the case at all. There is a distinction: in dynamic
• Brendan Zabarauskas (8/19) Jul 25 2015 Runtime errors are a usability problem for users and
• deadalnix (6/17) Jul 25 2015 In case 1, it is argued that unitest check runtime, so we are
• Andrei Alexandrescu (3/15) Jul 26 2015 I don't see it as the same argument. I do agree that applied to runtime
• Bruno Queiroga (27/55) Jul 24 2015 Could not the compiler just issue a warning of implicit use of
• Walter Bright (4/6) Jul 24 2015 C warnings are not part of Standard C. They're extensions only, and vary...
• Jonathan M Davis (114/149) Jul 24 2015 I don't know about this. The problem is that if you don't list
• Walter Bright (7/15) Jul 25 2015 Phew, finally, someone understands what I'm talking about! I'm really ba...
• Jonathan M Davis (38/63) Jul 25 2015 Well, the caller then has to do deal with the fact that the
• Tofu Ninja (87/95) Jul 25 2015 Actually I don't think the problem you state is actually a
• deadalnix (5/30) Jul 25 2015 This only make sense under the premise that both technique are
• Walter Bright (18/49) Jul 23 2015 I argue that every code line of the template must at least have been
• H. S. Teoh via Digitalmars-d (41/107) Jul 23 2015 Agreed.
• Walter Bright (12/14) Jul 23 2015 The worst case of not having this feature is a compile time error. Not a...
• =?UTF-8?Q?Tobias=20M=C3=BCller?= (3/7) Jul 23 2015 Haskell has type classes since ~1990.
• Walter Bright (8/14) Jul 23 2015 Haskell is sometimes described as a bondage-and-discipline language. Goo...
• Timon Gehr (3/9) Jul 24 2015 You got this wrong. In D, compile-time errors possibly influence runtime...
• Andrei Alexandrescu (3/6) Jul 25 2015 Then you need to understand that concepts are not helping you with that....
• Jonathan M Davis (189/194) Jul 25 2015 I see two issues here, both of which relate to maintenance. The
• Tofu Ninja (43/233) Jul 25 2015 I think one of the key points, is that it would be opt-in.
• Jonathan M Davis (27/28) Jul 25 2015 Opt-in doesn't really fix the problem. It just allows you to
• jmh530 (28/35) Jul 25 2015 I think from your post I finally understand what Walter was
• Jonathan M Davis (68/105) Jul 25 2015 I suggested the same in response to Walter earlier. It is one way
• jmh530 (22/42) Jul 25 2015 I appreciate the thorough response. I think I agree with your
• Jonathan M Davis (81/102) Jul 25 2015 That's certainly an interesting idea, though if we're going that
• jmh530 (30/45) Jul 26 2015 Yeah, I can see how something like that's going to get
• Jonathan M Davis (51/55) Jul 26 2015 Well, that's pretty much exactly why Walter is trying to say that
• Sebastiaan Koppe (5/44) Jul 25 2015 Regardless of this debate, it would be great if template
• Andrei Alexandrescu (21/50) Jul 25 2015 -cov does help, although indeed in a suboptimal way because you need to
• Andrei Alexandrescu (6/20) Jul 25 2015 Well at least all paths must be compiled. You wouldn't ship templates
• deadalnix (6/12) Jul 25 2015 That is an instance of happy case testing. You test that what you
• Walter Bright (2/11) Jul 25 2015 Um, testing all paths is not happy case testing.
• deadalnix (9/30) Jul 26 2015 You test all execution path, not all "instantiation path".
• Andrei Alexandrescu (9/38) Jul 26 2015 No, you are very wrong here. I am sorry! Instantiation testing is making...
• deadalnix (9/62) Jul 26 2015 It is not an analogy. The dynamic typing is not a problem that is
• =?UTF-8?Q?Tobias=20M=C3=BCller?= (8/15) Jul 23 2015 You define a new trait and implement it differently for A and B.
• Walter Bright (7/9) Jul 23 2015 I knew someone would bring that up :-)
• Jacob Carlborg (7/11) Jul 24 2015 Perhaps it might be good idea to allow to set a predefined version
• Walter Bright (18/21) Jul 24 2015 I don't want to encourage "if it compiles, ship it!" I've strongly disag...
• jmh530 (5/8) Jul 24 2015 I wouldn't be surprised if you're right, contra one, and you've
• Andrei Alexandrescu (7/31) Jul 25 2015 It would be a mistake to put concepts and traits together. Traits have
• Walter Bright (2/3) Jul 25 2015 Then I'm misunderstanding one or the other.
• =?UTF-8?Q?Tobias=20M=C3=BCller?= (7/10) Jul 25 2015 I'm not convinced at all that checked exceptions (as implemented in Java...
• Walter Bright (5/9) Jul 25 2015 Unfortunately, Bruce Eckel's seminal article on it
• Guillaume Chatelet (3/17) Jul 25 2015 This ?
• Brandon Ragland (18/18) Jul 25 2015 I'm not quite sure I understand why this thread is so hot...
• Walter Bright (2/16) Jul 25 2015 No, that's Anders.
• deadalnix (3/17) Jul 25 2015 Yes, checked exception is bankrupt at this point. It was not
• Alix Pexton (2/7) Jul 26 2015 https://web.archive.org/web/20150515072240/http://www.mindview.net/Etc/D...
• =?UTF-8?Q?Tobias=20M=C3=BCller?= (7/18) Jul 26 2015 This is article not convincing at all. His argument is basically "Most
• deadalnix (11/33) Jul 26 2015 No it is that checked Exception encourage this behavior.
• Bruno Medeiros (6/32) Oct 15 2015 Then define the logger interface as throwing a generic Exception class.
• Walter Bright (2/10) Jul 27 2015 That's it. Thanks for finding the link.
• =?UTF-8?Q?Tobias=20M=C3=BCller?= (9/21) Jul 25 2015 How is this related to testability? Using boolean conditions does not
• Jacob Carlborg (5/9) Jul 23 2015 I know that is possible, but in most cases it's only a list of function
• Chris (10/11) Jul 23 2015 [snip]
• Walter Bright (4/11) Jul 23 2015 It is very true that many features look good on paper, and only time and...
• Justin Whear (2/3) Jul 23 2015 Trigger warning needed!
• Chris (12/30) Jul 24 2015 What happens next is that users demand that things be changed and
• Timon Gehr (2/19) Jul 24 2015 That also fails the first clause.
• Jacob Carlborg (5/6) Jul 24 2015 I would say that Ruby is pretty far up the list of successful languages,...
• Walter Bright (2/6) Jul 24 2015 I know you're a great fan of Ruby, so I'll bite my tongue :-)
• Timon Gehr (2/12) Jul 24 2015 FWIW, most of the features in his list are very old.
• "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (46/92) Jul 23 2015 Thank you for sharing your impressions! I think posts like this
• Andrei Alexandrescu (27/40) Jul 23 2015 Thanks for the link, good quick read to get the overview of Rust's
• deadalnix (13/26) Jul 23 2015 On that note, I've mentioned scala's trait, which are kind of
• Jacob Carlborg (5/13) Jul 23 2015 In Scala there's no problem. No semicolons are required a no braces.
• Walter Bright (2/2) Jul 26 2015 C++ concepts for those interested:
• Bruno Medeiros (20/23) Jul 30 2015 Tooling doesn't just matters. Tooling trumps everything else.
• Alex Parrill (16/17) Jul 30 2015 I don't agree. IMO reducing the need for tools would be a better
• deadalnix (8/26) Jul 30 2015 It is not matter of agreeing or not. It is matter of fact.
• Bruno Medeiros (10/17) Jul 30 2015 That doesn't go against what I said. Not having the need for a tool can
• Enamex (20/36) Jul 30 2015 Mostly my experience, so far. If I have to choose the 'most
• Enamex (5/13) Jul 30 2015 Ouch. Actually forgot second most important point (right before
• Jonathan M Davis (18/20) Jul 31 2015 There are no warnings because it hasn't actually been deprecated
• Tofu Ninja (4/25) Jul 31 2015 I would much rather delete to stay and rig it up so new and
• Enamex (16/23) Aug 03 2015 GC and memory management in general are inadequately documented.
• rsw0x (12/13) Aug 04 2015 One thing I didn't see mentioned at all is Rust's plugin system.
• "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (3/7) Aug 04 2015 Yes, it looks very cool. It lowers the threshold for
• jmh530 (5/7) Aug 05 2015 Don't worry, I don't recall anybody talking about it.
• Enamex (5/13) Aug 05 2015 Oh, it _is_ talked about a lot. Just normally called 'syntax
"simendsjo" <simendsjo gmail.com> writes:
Long rant ahead - a bit dipsy..

TL;DR: Rust has momentum, manpower and tooling. Tooling matters.
Safe defaults.  Ergonomics like expressions and deconstructing
rocks.

I'm reluctantly confessing that I've had a small affair with Rust
recently. While I think D is a really good language, there are
several things I don't agree upon. Some are caused by historical
reasons, some are caused by the lack of peer review. Lets look at
a couple of things I think looks exiting about Rust after a bit
experimentation.

Manpower
--------
Silly stating this as why I have been playing with Rust, but it's
probably one of the main reasons. Rust has momentum and a lot of
manpower and well as some big compinies behind it. It should be
of secondary importance, but this is something that really shows
in the quality of Rust.

I've been using D on and off since 2007, and the lack of manpower
shows in every aspect of the language, design and ecosystem. Rust
has a pretty nice ecosystem and tools given its very young age.

Cargo
-----
Rust has a default package manager much like Dub. The main
difference is that Cargo has been endorsed by the Rust team and
is an official product. This means it works very well with the
compiler and feels like an integrated part of the language. Dub,
on the other hand, is a product from the outside, and
unfortunately, it feels this way too. Having Dub become the
endorsed package manager for D sounds like a very good idea for
me. In order to do this, some breaking changes might be necessary
(I haven't used much D or Dub recently though..). If so, a good
time to introduce breaking changes would be before integrating it
into the "core" D ecosystem.

The effects of Cargo is very visible by looking on the amount of
libraries on https://crates.io as opposed to
http://code.dlang.org.

While code.dlang.org has 530 packages, crates.io has 2610
packages, and this is even if Rust is very new. Dubs repository
website is a lot better than Rusts though :)

Traits
------
I think the ability to express an interface without buying into
inheritance is the right move. The alternative in D is specifying
the behavior as a template and verifying the contract in a
unittest for the type.

Algebraic data types
--------------------
Haven't looked into Algebraic!, so I won't start bashing D here
:) But given the lack of pattern matching, I doubt it will be as
pretty as Rust.

Macros
------
I haven't written more than extremely simple macros in Rust, but
having macros that is possible for tools to understand is a win.
Templates and string mixins is often used for this purpose, but
trying to build tools when string mixins exists is probably
extremely hard. If D had hygenic macros, I expect several
features could be expressed with this instead of string mixins,
making tooling easier to implement.

Safe by default
---------------
D is often said being safe by default, but D still has default
nullable references and mutable by default. I don't see it being
possible to change at this stage, but expressing when I want to
be unsafe rather than the opposite is very nice. I end up typing
a lot more in D than Rust because of this.

Pattern matching
----------------
Ooooh... I don't know what to say.. D should definitely look into
implementing some pattern matching! final switch is good for
making sure all values are handled, but deconstructing is just so
ergonomic.

Expressions
-----------
This probably also falls in the "too late" category, but
statements-as-expressions is really nice. auto a = if ... <-
why not?

Borrowing
---------
This is probably the big thing that makes Rust really different.
Everything is a resource, and resources have an owner and a
lifetime. As a part of this, you can either have multiple aliases
with read-only references, or a single reference with a writeable
reference. I won't say I have a lot of experience with this, but
it seems like it's not an extremely unergonomic trade-off. I
cannot even remotely imagine the amount of possible compiler
optimizations possible with this feature.

----

Why have I been looking at Rust?

I haven't been using D in production since 2008, which was an
utterly disaster. I did some work on the native mysql client, but
that was mostly making the code more D than C. Some small
experimentations thereafter.

The constant breakage in the language and standard library
haven't been an real issue for me as I haven't used it in
production - the problem when I used it was partly the phobos vs
tango with incompatible runtimes together with an extremely buggy
compiler. On the breaking part, the real issue is the "We're not
going to break any code!" stance, while each release still breaks
every codebase. The effect is that a lot of really long-term
necessary breaking changes is never accepted - the only breaking
changes is the unintended breaking changes! I'm in the "break-it"
camp. If you don't mind living with historical baggage and want
an expressive language, you can use C++. Why should D try to stay
with the baggage it has only recently acquired because of the
lack of manpower? I've recently been to job interviews, and none
of them ever heard of D...

After following Rust for some time (instead of following D!) and
now spending some time playing with it, I have to say that Rust
has several things going for it: manpower, momentum, tooling.
There are also really nice things like "everything" being an
expression, pattern matching and the compiler as a library.

But again... After playing a bit with Rust, I feel it lacks a lot
in expressive power. D has templates, template mixins, alias
this, string mixins, opDispatch etc. In my little time with Rust,
I've seen several pages of generic constrains that is expressible
in a couple of lines with D. I've seen copy/pasted code that just
isn't necessary when you code in D.

Anyways - my little ramblings after trying the Rust programming
language while I haven't used D in a long, long while (But I'm
still here now, as I'm not sure Rust is able to express
everything that is possible with D). Looking forward to following
D again :)

Jul 22 2015
"Jack Stouffer" <jack jackstouffer.com> writes:
On Wednesday, 22 July 2015 at 18:47:33 UTC, simendsjo wrote:
I've been using D on and off since 2007, and the lack of
manpower shows in every aspect of the language, design and
ecosystem. Rust has a pretty nice ecosystem and tools given its
very young age.

Only one way to fix this. Volunteer.

Rust has a default package manager much like Dub. The main
difference is that Cargo has been endorsed by the Rust team and
is an official product. This means it works very well with the
compiler and feels like an integrated part of the language.
Dub, on the other hand, is a product from the outside, and
unfortunately, it feels this way too. Having Dub become the
endorsed package manager for D sounds like a very good idea for
me.

Dub is endorsed by the leadership and is included in the same
Github organization as the complier and the standard library

While code.dlang.org has 530 packages, crates.io has 2610
packages, and this is even if Rust is very new. Dubs repository
website is a lot better than Rusts though :)

I attribute this to hype. Also, while dub may feel a little bare
at times, I always think of the worse alternative in node, so
many packages that are crap that they push out anything of value.

Traits
------
I think the ability to express an interface without buying into
inheritance is the right move. The alternative in D is
specifying the behavior as a template and verifying the
contract in a unittest for the type.

I don't know enough about rust to comment.

Macros
------
I haven't written more than extremely simple macros in Rust,
but having macros that is possible for tools to understand is a
win.  Templates and string mixins is often used for this
purpose, but trying to build tools when string mixins exists is
probably extremely hard. If D had hygenic macros, I expect
several features could be expressed with this instead of string
mixins, making tooling easier to implement.

Maybe tooling would become easier to write, but in my personal
experience, macros are much harder for programmers to understand
than mixins, and much easier to abuse.

Safe by default
---------------
D is often said being safe by default, but D still has default
nullable references and mutable by default. I don't see it
being possible to change at this stage, but expressing when I
want to be unsafe rather than the opposite is very nice. I end
up typing a lot more in D than Rust because of this.

This would break so much code it's not even funny. I agree that
immutable by default is the best paradigm, but as far as breaking
changes go, you can only have so many before people abandoned a
language.

Expressions
-----------
This probably also falls in the "too late" category, but
statements-as-expressions is really nice. auto a = if ... <-
why not?

Don't quite know what you mean here.

Borrowing
---------
This is probably the big thing that makes Rust really
different.  Everything is a resource, and resources have an
owner and a lifetime. As a part of this, you can either have
multiple aliases with read-only references, or a single
reference with a writeable reference. I won't say I have a lot
of experience with this, but it seems like it's not an
extremely unergonomic trade-off. I cannot even remotely imagine
the amount of possible compiler optimizations possible with
this feature.

I think that someone was working on this, but I think it got
sidelined (as it should) to fixing RefCounted and other things in
the std lib.

The constant breakage in the language and standard library
haven't been an real issue for me as I haven't used it in
production - the problem when I used it was partly the phobos
vs tango with incompatible runtimes together with an extremely
buggy compiler.

The tango vs Phobos issue has been mostly settled after the
transition from d1 to d2, and the complier is a lot better now
than it was.

On the breaking part, the real issue is the "We're not going to
break any code!" stance,

Who, in the leadership or a contributor, has ever said this.

Jul 22 2015
"simendsjo" <simendsjo gmail.com> writes:
On Wednesday, 22 July 2015 at 19:41:16 UTC, Jack Stouffer wrote:
On Wednesday, 22 July 2015 at 18:47:33 UTC, simendsjo wrote:
I've been using D on and off since 2007, and the lack of
manpower shows in every aspect of the language, design and
ecosystem. Rust has a pretty nice ecosystem and tools given
its very young age.

Only one way to fix this. Volunteer.

Yes, I agree. I haven't done much good for the D community,
although I believe some of the code I've written for mysql native
is in production.

Let me add a point for Rust somewhat related :)

Community
---------
The community is nice, helpful and doesn't condecent people.

On Wednesday, 22 July 2015 at 19:41:16 UTC, Jack Stouffer wrote:
On Wednesday, 22 July 2015 at 18:47:33 UTC, simendsjo wrote:
Expressions
-----------
This probably also falls in the "too late" category, but
statements-as-expressions is really nice. auto a = if ... <-
why not?

Don't quite know what you mean here.

When "everything" is an expressions, you can write things like
auto a = if(e) c else d;

In D you have to write
type a = invalid_value;
if(e) a = c;
else  a = d;
assert(a != invalid_value);

On Wednesday, 22 July 2015 at 19:41:16 UTC, Jack Stouffer wrote:
On Wednesday, 22 July 2015 at 18:47:33 UTC, simendsjo wrote:
(... snip [regarding phobos/tango runtime incompatibility] ...)

The tango vs Phobos issue has been mostly settled after the
transition from d1 to d2, and the complier is a lot better now
than it was.

There are probably nothing in newer times that can be credited as
much pain as the incompatible runtimes of phobos and tango, but
I've still encountered a lot of compiler and phobos bugs. Yes,
it's just every 500 or so lines now, but I've only encountered
a couple of bugs in the C# compiler and a couple more in the .Net
library in several years - again credited to a lot more manpower
available.

On Wednesday, 22 July 2015 at 19:41:16 UTC, Jack Stouffer wrote:
On Wednesday, 22 July 2015 at 18:47:33 UTC, simendsjo wrote:
On the breaking part, the real issue is the "We're not going
to break any code!" stance,

Who, in the leadership or a contributor, has ever said this.

I'm pretty sure Walter Bright has said this on a lot of occations.
He seemed to believe a failure of adoption of D was due to the
constant breaking changes, which might be true.

On Wednesday, 22 July 2015 at 19:41:16 UTC, Jack Stouffer wrote:
On Wednesday, 22 July 2015 at 18:47:33 UTC, simendsjo wrote:
Rust has a default package manager much like Dub. The main
difference is that Cargo has been endorsed by the Rust team
and is an official product. (... snip ...)

Dub is endorsed by the leadership and is included in the same
Github organization as the complier and the standard library

I didn't know that. Very nice. I hope it's in a clean state when
it gets pulled in.

On Wednesday, 22 July 2015 at 19:41:16 UTC, Jack Stouffer wrote:
On Wednesday, 22 July 2015 at 18:47:33 UTC, simendsjo wrote:
While code.dlang.org has 530 packages, crates.io has 2610
packages, (... snip ...)

I attribute this to hype. (... snip ...)

Yes, but the hype is probably driving even more developers to the
language, and I believe a large user-base is of paramount
importance for a language to become good.

On Wednesday, 22 July 2015 at 19:41:16 UTC, Jack Stouffer wrote:
On Wednesday, 22 July 2015 at 18:47:33 UTC, simendsjo wrote:
Macros
------
(... snip ...)

Maybe tooling would become easier to write, but in my personal
experience, macros are much harder for programmers to
understand than mixins, and much easier to abuse.

I disagree. String mixins are much easier to abuse than hygenic
macros. String mixins allows anything, and while it offers
infinite possibilities, it also encourage abuse.

Jul 22 2015
"Adam D. Ruppe" <destructionator gmail.com> writes:
On Wednesday, 22 July 2015 at 20:43:04 UTC, simendsjo wrote:
When "everything" is an expressions, you can write things like
auto a = if(e) c else d;

In D you have to write
type a = invalid_value;
if(e) a = c;
else  a = d;
assert(a != invalid_value);

That's what the ternary expression is for:

auto a = e ? c : d;

Though the ternary is unnecessary with statements as expressions,
common cases like this are handled.

Jul 22 2015
"simendsjo" <simendsjo gmail.com> writes:
On Wednesday, 22 July 2015 at 20:59:11 UTC, Adam D. Ruppe wrote:
On Wednesday, 22 July 2015 at 20:43:04 UTC, simendsjo wrote:
When "everything" is an expressions, you can write things like
auto a = if(e) c else d;

In D you have to write
type a = invalid_value;
if(e) a = c;
else  a = d;
assert(a != invalid_value);

That's what the ternary expression is for:

auto a = e ? c : d;

Though the ternary is unnecessary with statements as
expressions, common cases like this are handled.

:) The example was written to save space. I recon you understand
what I mean.

Jul 22 2015
"Adam D. Ruppe" <destructionator gmail.com> writes:
On Wednesday, 22 July 2015 at 21:04:57 UTC, simendsjo wrote:
:) The example was written to save space. I recon you
understand what I mean.

Yeah, but the if/else is one of the most useful examples of it,
and is covered by ?:, so the whole thing becomes less compelling
then.

The other places where I've used it in languages that support it
are little blocks crammed into a line and sometimes exception
grabbing... but still, the value isn't that great.

Jul 23 2015
On Thursday, 23 July 2015 at 13:33:43 UTC, Adam D. Ruppe wrote:
On Wednesday, 22 July 2015 at 21:04:57 UTC, simendsjo wrote:
:) The example was written to save space. I recon you
understand what I mean.

Yeah, but the if/else is one of the most useful examples of it,
and is covered by ?:, so the whole thing becomes less
compelling then.

The other places where I've used it in languages that support
it are little blocks crammed into a line and sometimes
exception grabbing... but still, the value isn't that great.

If we had a clean sheet wouldn't it be better to have if return a
value and ditch ternary?

Jul 23 2015
"Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Thursday, 23 July 2015 at 14:49:55 UTC, ixid wrote:
On Thursday, 23 July 2015 at 13:33:43 UTC, Adam D. Ruppe wrote:
On Wednesday, 22 July 2015 at 21:04:57 UTC, simendsjo wrote:
:) The example was written to save space. I recon you
understand what I mean.

Yeah, but the if/else is one of the most useful examples of
it, and is covered by ?:, so the whole thing becomes less
compelling then.

The other places where I've used it in languages that support
it are little blocks crammed into a line and sometimes
exception grabbing... but still, the value isn't that great.

If we had a clean sheet wouldn't it be better to have if return
a value and ditch ternary?

Maybe, but the ternary operator is a lot less verbose, and from
some other comments in this thread, it sounds like the way they
implemented it in Rust forces you to use braces for single line
statements, which would be a _huge_ downside IMHO. I'm inclined
to think that it would need a use case that's a lot more
compelling than if-else chains to be worth it.

- Jonathan M Davis

Jul 23 2015
Jacob Carlborg <doob me.com> writes:
On 2015-07-23 17:41, Jonathan M Davis wrote:

Maybe, but the ternary operator is a lot less verbose, and from some
other comments in this thread, it sounds like the way they implemented
it in Rust forces you to use braces for single line statements, which
would be a _huge_ downside IMHO.

Scala implements it without those requirements. I looks exactly like in
D, just that it also returns a value.

Also, I think it's getting a lot more interesting when you combine it
with automatically return in a method and optional braces for methods
with a single expression:

def returnType = if (node.isConstructor)
None
else
Some(Type.translate(binding.getReturnType))

Or if you're using pattern matching:

def fromModifier(value: Modifier) = value match {
case Modifier.ABSTRACT => ABSTRACT
case Modifier.STATIC => STATIC
case Modifier.FINAL => FINAL
case _ => NONE
}

BTW, Ruby supports both the ternary operator and statements are expressions.

--
/Jacob Carlborg

Jul 23 2015
Ziad Hatahet via Digitalmars-d <digitalmars-d puremagic.com> writes:
On Thu, Jul 23, 2015 at 8:41 AM, Jonathan M Davis via Digitalmars-d <
digitalmars-d puremagic.com> wrote:

Maybe, but the ternary operator is a lot less verbose

The ternary operator becomes much harder to read the moment you have more
than the simple if/else case. As it was mentioned elsewhere on this thread,
you can do the following in Scala:

val x = if (condition_1) 1
else if (condition_2) 2
else if (condition_3) 3
else 4

Having expressions be "built-in" extends beyond the simple if/else case,
which can be emulated with the ternary operator as you said. You can assign
the result of match expressions for instance, or the result of scoped
blocks, .e.g.

val x = {
val ys = foo()
ys.map(...).filter(...).exists(...)
}

, and from some other comments in this thread, it sounds like the way they
implemented it in Rust forces you to use braces for single line statements,
which would be a _huge_ downside IMHO.

On the other hand, Rust does not require parenthesis around if conditions:

let x = if some_condition { 1 } else { 2 }

I'm inclined to think that it would need a use case that's a lot more
compelling than if-else chains to be worth it.

I provided examples above.

--

Jul 23 2015
"Adam D. Ruppe" <destructionator gmail.com> writes:
On Thursday, 23 July 2015 at 20:48:34 UTC, Ziad Hatahet wrote:
The ternary operator becomes much harder to read the moment you
have more than the simple if/else case.

I think it is actually kinda pretty:

auto x =   (condition_1) ? 1
: (condition_2) ? 2
: (condition_3) ? 3
: 4;

val x = {
val ys = foo()
ys.map(...).filter(...).exists(...)
}

auto x = {
auto ys = foo();
return ys.map(...).filter(...).exists(...);
}();

Before you get too worried about the (), I'd point out that this
is a very common pattern in Javascript (for like everything...)
and while everybody hates JS, most every uses it too; this patten
is good enough for usefulness.

(or for that you could prolly just write foo().map()... directly

Jul 23 2015
Ziad Hatahet via Digitalmars-d <digitalmars-d puremagic.com> writes:
On Thu, Jul 23, 2015 at 2:00 PM, Adam D. Ruppe via Digitalmars-d <
digitalmars-d puremagic.com> wrote:

I think it is actually kinda pretty:

int median(int a, int b, int c) {
return (a<b) ? (b<c) ? b : (a<c) ? c : a : (a<c) ? a : (b<c) ? c : b;
}

vs.

def median(a: Int, b: Int, c: Int) =
if (a < b) {
if (b < c) b
else if (a < c) c
else a
}
else if (a < c) a
else if (b < c) c
else b

Before you get too worried about the (), I'd point out that this is a very
common pattern in Javascript (for like everything...) and while everybody
hates JS, most every uses it too; this patten is good enough for usefulness.

Is the compiler always able to always optimize out the function call by
inlining it, as would be the case with a scope?

Jul 23 2015
"Adam D. Ruppe" <destructionator gmail.com> writes:
On Thursday, 23 July 2015 at 21:27:17 UTC, Ziad Hatahet wrote:
I think it is actually kinda pretty:

int median(int a, int b, int c) {
return (a<b) ? (b<c) ? b : (a<c) ? c : a : (a<c) ? a :
(b<c) ? c : b;
}

vs.

def median(a: Int, b: Int, c: Int) =
if (a < b) {
if (b < c) b
else if (a < c) c
else a
}
else if (a < c) a
else if (b < c) c
else b

Not really a spaces-to-spaces comparison...

to be honest, I'd probably just write that as:

int median(int a, int b, int c) {
if (a < b) {
if (b < c) return b;
else if (a < c) return c;
else return a;
}
else if (a < c) return a;
else if (b < c) return c;
else return b;
}

You don't need it to be an expression since it is a function, you
can simply write return statements (which I kinda like since then
it is obvious that that value is a terminating condition and not
just the middle of some other calculation).

But if we were using a ternary, some newlines can help with it:

return
(a < b) ? (
(b < c) ? b
: (a < c) ? c
: a
)
: (a < c) ? a
: (b < c) ? c
: b;

Indeed, there I just took your if/else thing and swapped out the
else keyword for the : symbol, then replaced if(cond) with (cond)
?, then changed out the {} for (). It still works the same way.

Is the compiler always able to always optimize out the function
call by inlining it, as would be the case with
a scope?

It should be.

Jul 23 2015
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/23/15 5:26 PM, Ziad Hatahet via Digitalmars-d wrote:
On Thu, Jul 23, 2015 at 2:00 PM, Adam D. Ruppe via Digitalmars-d
<digitalmars-d puremagic.com <mailto:digitalmars-d puremagic.com>> wrote:

I think it is actually kinda pretty:

int median(int a, int b, int c) {
return (a<b) ? (b<c) ? b : (a<c) ? c : a : (a<c) ? a : (b<c) ? c : b;
}

vs.

def median(a: Int, b: Int, c: Int) =
if (a < b) {
if (b < c) b
else if (a < c) c
else a
}
else if (a < c) a
else if (b < c) c
else b

This is a wash. If we want to discuss good things in Rust we could get
inspiration from, we need relevant examples. -- Andrei

Jul 25 2015
Timon Gehr <timon.gehr gmx.ch> writes:
On 07/25/2015 02:19 PM, Andrei Alexandrescu wrote:
On 7/23/15 5:26 PM, Ziad Hatahet via Digitalmars-d wrote:
On Thu, Jul 23, 2015 at 2:00 PM, Adam D. Ruppe via Digitalmars-d
<digitalmars-d puremagic.com <mailto:digitalmars-d puremagic.com>> wrote:

I think it is actually kinda pretty:

int median(int a, int b, int c) {
return (a<b) ? (b<c) ? b : (a<c) ? c : a : (a<c) ? a : (b<c) ? c
: b;
}

vs.

def median(a: Int, b: Int, c: Int) =
if (a < b) {
if (b < c) b
else if (a < c) c
else a
}
else if (a < c) a
else if (b < c) c
else b

This is a wash. If we want to discuss good things in Rust

(The quoted bit is Scala code.)

we could get inspiration from, we need relevant examples. -- Andrei

What do you mean?

I think it is pretty obvious that 'if'/'else' is "better" syntax than
'?:'. It e.g. does not leave the separation of context and condition up
to operator precedence rules and is hence easier to parse by a human.
Not that I'd care much, but it is inconvenient to be asked not to use
the ternary operator in team projects just because it has a badly
engineered syntax.

Also, we have (int x){ return r; }, auto foo(int x){ return r; }, (int
x)=>r, but not auto foo(int x)=>r. It's an arbitrary restriction.

(BTW: To all the people who like to put the ternary operator condition
into parens in order to imitate if: A convention that makes more sense
here is to put the entire (chained) ternary expression in parentheses.)

Jul 26 2015
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/26/15 11:18 AM, Timon Gehr wrote:
I think it is pretty obvious that 'if'/'else' is "better" syntax than '?:'.

It may as well be the case but the whole deal is marginal. Yeah, we
could have slightly better tactical tools for expressing conditionals,
but really we're fine. -- Andrei

Jul 26 2015
dennis luehring <dl.soluz gmx.net> writes:
Am 23.07.2015 um 22:47 schrieb Ziad Hatahet via Digitalmars-d:
Having expressions be "built-in" extends beyond the simple if/else case

and allowes const correctness without functions

Jul 24 2015
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/23/15 4:47 PM, Ziad Hatahet via Digitalmars-d wrote:
On the other hand, Rust does not require parenthesis around if
conditions:

Yet it requires braces around the arms. Rust taketh away, Rust requireth
back :o). -- Andrei

Jul 25 2015
Timon Gehr <timon.gehr gmx.ch> writes:
On 07/25/2015 02:18 PM, Andrei Alexandrescu wrote:
On 7/23/15 4:47 PM, Ziad Hatahet via Digitalmars-d wrote:
On the other hand, Rust does not require parenthesis around if
conditions:

Yet it requires braces around the arms. Rust taketh away, Rust requireth
back :o). -- Andrei


Jul 26 2015
"Enamex" <enamex+d outlook.com> writes:
On Sunday, 26 July 2015 at 15:21:32 UTC, Timon Gehr wrote:
On 07/25/2015 02:18 PM, Andrei Alexandrescu wrote:
On 7/23/15 4:47 PM, Ziad Hatahet via Digitalmars-d wrote:
On the other hand, Rust does not require parenthesis around if
conditions:

Yet it requires braces around the arms. Rust taketh away, Rust
requireth
back :o). -- Andrei

Yeah. Instead of sometimes requiring just parentheses and
sometimes them and braces. It's also less error-inducing. I
rather like it though it's not exactly a functionality thing.

Jul 26 2015
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/23/15 10:49 AM, ixid wrote:
On Thursday, 23 July 2015 at 13:33:43 UTC, Adam D. Ruppe wrote:
On Wednesday, 22 July 2015 at 21:04:57 UTC, simendsjo wrote:
:) The example was written to save space. I recon you understand what
I mean.

Yeah, but the if/else is one of the most useful examples of it, and is
covered by ?:, so the whole thing becomes less compelling then.

The other places where I've used it in languages that support it are
little blocks crammed into a line and sometimes exception grabbing...
but still, the value isn't that great.

If we had a clean sheet wouldn't it be better to have if return a value
and ditch ternary?

Possibly, but then you'd need to have while return a value. -- Andrei

Jul 23 2015
Timon Gehr <timon.gehr gmx.ch> writes:
On 07/23/2015 06:28 PM, Andrei Alexandrescu wrote:
On 7/23/15 10:49 AM, ixid wrote:
On Thursday, 23 July 2015 at 13:33:43 UTC, Adam D. Ruppe wrote:
On Wednesday, 22 July 2015 at 21:04:57 UTC, simendsjo wrote:
:) The example was written to save space. I recon you understand what
I mean.

Yeah, but the if/else is one of the most useful examples of it, and is
covered by ?:, so the whole thing becomes less compelling then.

The other places where I've used it in languages that support it are
little blocks crammed into a line and sometimes exception grabbing...
but still, the value isn't that great.

If we had a clean sheet wouldn't it be better to have if return a value
and ditch ternary?

Possibly, but then you'd need to have while return a value. -- Andrei

https://en.wikipedia.org/wiki/Unit_type

Jul 23 2015
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/23/15 1:51 PM, Timon Gehr wrote:
On 07/23/2015 06:28 PM, Andrei Alexandrescu wrote:
On 7/23/15 10:49 AM, ixid wrote:
On Thursday, 23 July 2015 at 13:33:43 UTC, Adam D. Ruppe wrote:
On Wednesday, 22 July 2015 at 21:04:57 UTC, simendsjo wrote:
:) The example was written to save space. I recon you understand what
I mean.

Yeah, but the if/else is one of the most useful examples of it, and is
covered by ?:, so the whole thing becomes less compelling then.

The other places where I've used it in languages that support it are
little blocks crammed into a line and sometimes exception grabbing...
but still, the value isn't that great.

If we had a clean sheet wouldn't it be better to have if return a value
and ditch ternary?

Possibly, but then you'd need to have while return a value. -- Andrei

https://en.wikipedia.org/wiki/Unit_type

I said awkward, not impossible. -- Andrei

Jul 23 2015
Timon Gehr <timon.gehr gmx.ch> writes:
On 07/23/2015 09:18 PM, Andrei Alexandrescu wrote:
On 7/23/15 1:51 PM, Timon Gehr wrote:
On 07/23/2015 06:28 PM, Andrei Alexandrescu wrote:
On 7/23/15 10:49 AM, ixid wrote:
On Thursday, 23 July 2015 at 13:33:43 UTC, Adam D. Ruppe wrote:
On Wednesday, 22 July 2015 at 21:04:57 UTC, simendsjo wrote:
:) The example was written to save space. I recon you understand what
I mean.

Yeah, but the if/else is one of the most useful examples of it, and is
covered by ?:, so the whole thing becomes less compelling then.

The other places where I've used it in languages that support it are
little blocks crammed into a line and sometimes exception grabbing...
but still, the value isn't that great.

If we had a clean sheet wouldn't it be better to have if return a value
and ditch ternary?

Possibly, but then you'd need to have while return a value. -- Andrei

https://en.wikipedia.org/wiki/Unit_type

I said awkward, not impossible. -- Andrei

It's not awkward.

Jul 24 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 7/23/2015 7:49 AM, ixid wrote:
If we had a clean sheet wouldn't it be better to have if return a value and
ditch ternary?

Then we'd start seeing code like:

x = 45 + if (y == 10) { while (i--) z += call(i); z; } else { switch (x) {
case 6: foo(); y; } + tan(z);

I.e. the embedding of arbitrary statements within expressions. We already have
some of this with embedded anonymous lambda support, and I've discovered one
needs to be very careful in formatting it to not wind up with an awful

So I'd be really reluctant to continue down that path.

Now, if you want to disallow { } within the embedded if statement, then the
proposal becomes nothing more than:

? => if
: => else

which is a potayto potahto thing.

I agree that trivial syntax issues actually do matter, but having used ?: a
lot,
I have a hard time seeing embeddable if-else as a real improvement, in fact I
find it more than a little jarring to see.

Jul 23 2015
"Tofu Ninja" <emmons0 purdue.edu> writes:
On Thursday, 23 July 2015 at 20:09:34 UTC, Walter Bright wrote:
On 7/23/2015 7:49 AM, ixid wrote:
If we had a clean sheet wouldn't it be better to have if
return a value and
ditch ternary?

Then we'd start seeing code like:

x = 45 + if (y == 10) { while (i--) z += call(i); z; } else
{ switch (x) { case 6: foo(); y; } + tan(z);

I.e. the embedding of arbitrary statements within expressions.
We already have some of this with embedded anonymous lambda
support, and I've discovered one needs to be very careful in
formatting it to not wind up with an awful unreadable mess.

So I'd be really reluctant to continue down that path.

Now, if you want to disallow { } within the embedded if
statement, then the proposal becomes nothing more than:

? => if
: => else

which is a potayto potahto thing.

I agree that trivial syntax issues actually do matter, but
having used ?: a lot, I have a hard time seeing embeddable
if-else as a real improvement, in fact I find it more than a
little jarring to see.

I think I agree on the if else issue, seems arbitrary as we
already have ?:. Other statements as expressions have less
obvious meanings. The only part is that I wish you could have
blocks as expressions. The thing is with ufcs, it really should
be possible.

For example the following does not compile:
int a = {return 4;};

but the following does:
int a = {return 4;}();

I know it's a really small difference, but with UFCS, I would
expect you the be able to omit the () and have the function
literal called automatically. Though I can see that this would
have problems with auto and knowing if it should be a function
pointer or to call the function.

I guess what I would expect is "auto a = {return 4;};" to type a
to a function pointer, but if you explicitly type a to int then
the literal should be called.

Does UFCS even apply to function pointers? I guess it is a
problem, it does not seem to be obvious when to call and when to
copy the pointer. I don't really know what should happen. I think
I read a dip a little while ago that might have addressed this,
but I don't really remember. I dont know, now that I have written
this, it seems to have more problems than I originally thought.

Jul 23 2015
"Tofu Ninja" <emmons0 purdue.edu> writes:
On Friday, 24 July 2015 at 00:55:35 UTC, Tofu Ninja wrote:
On Thursday, 23 July 2015 at 20:09:34 UTC, Walter Bright wrote:
On 7/23/2015 7:49 AM, ixid wrote:
If we had a clean sheet wouldn't it be better to have if
return a value and
ditch ternary?

Then we'd start seeing code like:

x = 45 + if (y == 10) { while (i--) z += call(i); z; }
else { switch (x) { case 6: foo(); y; } + tan(z);

I.e. the embedding of arbitrary statements within expressions.
We already have some of this with embedded anonymous lambda
support, and I've discovered one needs to be very careful in
formatting it to not wind up with an awful unreadable mess.

So I'd be really reluctant to continue down that path.

Now, if you want to disallow { } within the embedded if
statement, then the proposal becomes nothing more than:

? => if
: => else

which is a potayto potahto thing.

I agree that trivial syntax issues actually do matter, but
having used ?: a lot, I have a hard time seeing embeddable
if-else as a real improvement, in fact I find it more than a
little jarring to see.

I think I agree on the if else issue, seems arbitrary as we
already have ?:. Other statements as expressions have less
obvious meanings. The only part is that I wish you could have
blocks as expressions. The thing is with ufcs, it really should
be possible.

For example the following does not compile:
int a = {return 4;};

but the following does:
int a = {return 4;}();

I know it's a really small difference, but with UFCS, I would
expect you the be able to omit the () and have the function
literal called automatically. Though I can see that this would
have problems with auto and knowing if it should be a function
pointer or to call the function.

I guess what I would expect is "auto a = {return 4;};" to type
a to a function pointer, but if you explicitly type a to int
then the literal should be called.

Does UFCS even apply to function pointers? I guess it is a
problem, it does not seem to be obvious when to call and when
to copy the pointer. I don't really know what should happen. I
think I read a dip a little while ago that might have addressed
this, but I don't really remember. I dont know, now that I have
written this, it seems to have more problems than I originally
thought.

Actually now that I think about it, I think I would expect auto a
= { return 4;}; to type a to an int and call the function
literal, and auto a = &{ return 4;}; to type a to a function
pointer. I think that makes sense. Then if a is a function
pointer auto b = a; would type b to a function pointer as well. I
suppose UFCS really does not make sense to function pointers, but
does make sense for function literals.

I expect {return 4;} to just be an anonymous function, not a
pointer to an anonymous function. That way you can write alias f
= {return 4;}; which would just be an alias to a function, which
makes sense. I haven't thought about how this would apply to
delegates.

Jul 23 2015
Jacob Carlborg <doob me.com> writes:
On 2015-07-24 02:55, Tofu Ninja wrote:

I think I agree on the if else issue, seems arbitrary as we already have
?:. Other statements as expressions have less obvious meanings. The only
part is that I wish you could have blocks as expressions. The thing is
with ufcs, it really should be possible.

For example the following does not compile:
int a = {return 4;};

but the following does:
int a = {return 4;}();

I know it's a really small difference, but with UFCS, I would expect you
the be able to omit the () and have the function literal called
automatically. Though I can see that this would have problems with auto
and knowing if it should be a function pointer or to call the function.

I guess what I would expect is "auto a = {return 4;};" to type a to a
function pointer, but if you explicitly type a to int then the literal
should be called.

Does UFCS even apply to function pointers? I guess it is a problem, it
does not seem to be obvious when to call and when to copy the pointer. I
don't really know what should happen. I think I read a dip a little
while ago that might have addressed this, but I don't really remember. I
dont know, now that I have written this, it seems to have more problems
than I originally thought.

How does UFCS apply here? There isn't even a dot in the code.

--
/Jacob Carlborg

Jul 24 2015
"Tofu Ninja" <emmons0 purdue.edu> writes:
On Friday, 24 July 2015 at 13:22:34 UTC, Jacob Carlborg wrote:
On 2015-07-24 02:55, Tofu Ninja wrote:

I think I agree on the if else issue, seems arbitrary as we
?:. Other statements as expressions have less obvious
meanings. The only
part is that I wish you could have blocks as expressions. The
thing is
with ufcs, it really should be possible.

For example the following does not compile:
int a = {return 4;};

but the following does:
int a = {return 4;}();

I know it's a really small difference, but with UFCS, I would
expect you
the be able to omit the () and have the function literal called
automatically. Though I can see that this would have problems
with auto
and knowing if it should be a function pointer or to call the
function.

I guess what I would expect is "auto a = {return 4;};" to type
a to a
function pointer, but if you explicitly type a to int then the
literal
should be called.

Does UFCS even apply to function pointers? I guess it is a
problem, it
does not seem to be obvious when to call and when to copy the
pointer. I
don't really know what should happen. I think I read a dip a
little
while ago that might have addressed this, but I don't really
remember. I
dont know, now that I have written this, it seems to have more
problems
than I originally thought.

How does UFCS apply here? There isn't even a dot in the code.

Is omitting the () not part of ufcs? Or does it have some other
name, I can never remember.

Jul 24 2015
"Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Friday, 24 July 2015 at 20:46:07 UTC, Tofu Ninja wrote:
Is omitting the () not part of ufcs? Or does it have some other
name, I can never remember.

No. That's simply omitting the parens from a function call that
has no arguments. If it has a name, it's just "optional parens."
Universal Function Call Syntax is the syntax that allows you to
call a free function as if it were a member function, which is
why stuff like

auto result = myRange.find(bar);

works. It _does_ mean that you can drop even more parens, because
the first function argument is now to the left of the dot, and
the parens are then empty if there was only one function
argument, but being able to drop the parens has nothing to do
with UFCS. We could still have UFCS and yet never be able to drop
parens.

- Jonathan M Davis

Jul 24 2015
"Tofu Ninja" <emmons0 purdue.edu> writes:
On Friday, 24 July 2015 at 21:29:56 UTC, Jonathan M Davis wrote:
On Friday, 24 July 2015 at 20:46:07 UTC, Tofu Ninja wrote:
Is omitting the () not part of ufcs? Or does it have some
other name, I can never remember.

No. That's simply omitting the parens from a function call that
has no arguments. If it has a name, it's just "optional
parens." Universal Function Call Syntax is the syntax that
allows you to call a free function as if it were a member
function, which is why stuff like

auto result = myRange.find(bar);

works. It _does_ mean that you can drop even more parens,
because the first function argument is now to the left of the
dot, and the parens are then empty if there was only one
function argument, but being able to drop the parens has
nothing to do with UFCS. We could still have UFCS and yet never
be able to drop parens.

- Jonathan M Davis

Ok I kinda assumed they were both included in the concept of
UFCS, just replace "UFCS" in what I said before with "optional
parens". Basicly I was just saying that I suppose optional parens
do not really make sense on function pointers. Aka if "a" is a
function pointer then auto b = a; should type "b" to a function
pointer as well, which is how it currently works, afik.

But the part that I don't think makes sense for

auto a = {return 4;};

to type "a" to a function pointer. I would expect {return 4;} to
be treated as a function(not a function pointer). With it being
treated as a function, I would expect it to be called with
optional parens and type "a" to an int. I would expect auto

a = &{return 4;};

to type "a" to a function pointer, which makes much more sense to
me. But that's not how function literals work right now. Treating
{return 4;} as a function(not a function pointer) makes a lot
more sense and allows

alias a = {return 4;};

to work as well, which is simply a function declaration.

Jul 24 2015
"Enamex" <enamex+d outlook.com> writes:
On Friday, 24 July 2015 at 21:44:42 UTC, Tofu Ninja wrote:
But the part that I don't think makes sense for

auto a = {return 4;};

to type "a" to a function pointer. I would expect {return 4;}
to be treated as a function(not a function pointer). With it
being treated as a function, I would expect it to be called
with optional parens and type "a" to an int. I would expect auto

a = &{return 4;};

to type "a" to a function pointer, which makes much more sense
to me. But that's not how function literals work right now.
Treating {return 4;} as a function(not a function pointer)
makes a lot more sense and allows

alias a = {return 4;};

to work as well, which is simply a function declaration.

function literals being _just_ { ... } and as a matter of
principle only write (){...} when I need one. {} to me can
only mean blocks that are part of the current scope, but they're
sometimes that and sometimes lambdas, depending on whether they
had any returns and are in a place to be assigned a name or
immediately called :/

A related thing is having _some way_ to quickly return a value
from inside an invoked function literal without return. Somme
stuff can't be done in a one-liner and need _two_(ish) lines and
have to write, say, { Type myval, myres; res_by_ref(myval,
myres); return myres; }() instead of { Type myval, myres;
res_by_ref(myval, myres); myres } (expression-oriented) or (){
...; => myres; }()(hypothetically). Point is, writing return
in the middle of a function and having it return _only_ from a
lambda breaks the flow, I believe, same as in C++.

Aug 05 2015
On Thursday, 23 July 2015 at 20:09:34 UTC, Walter Bright wrote:
On 7/23/2015 7:49 AM, ixid wrote:
If we had a clean sheet wouldn't it be better to have if
return a value and
ditch ternary?

Then we'd start seeing code like:

x = 45 + if (y == 10) { while (i--) z += call(i); z; } else
{ switch (x) { case 6: foo(); y; } + tan(z);

I.e. the embedding of arbitrary statements within expressions.
We already have some of this with embedded anonymous lambda
support, and I've discovered one needs to be very careful in
formatting it to not wind up with an awful unreadable mess.

So I'd be really reluctant to continue down that path.

As opposed to:

auto n = {
if (y == 10) {
return {
while (i--)
z += call(i);
return z;
}();
} else {
return {
switch (x) {
case 6: return foo();
default: return y;
}
}();
}
}() + tan(z);

You can already do that, it's even uglier.

Jul 24 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 7/24/2015 3:06 AM, ixid wrote:
On Thursday, 23 July 2015 at 20:09:34 UTC, Walter Bright wrote:
On 7/23/2015 7:49 AM, ixid wrote:
If we had a clean sheet wouldn't it be better to have if return a value and
ditch ternary?

Then we'd start seeing code like:

x = 45 + if (y == 10) { while (i--) z += call(i); z; } else { switch (x) {
case 6: foo(); y; } + tan(z);

I.e. the embedding of arbitrary statements within expressions. We already have
some of this with embedded anonymous lambda support, and I've discovered one
needs to be very careful in formatting it to not wind up with an awful

So I'd be really reluctant to continue down that path.

As opposed to:

auto n = {
if (y == 10) {
return {
while (i--)
z += call(i);
return z;
}();
} else {
return {
switch (x) {
case 6: return foo();
default: return y;
}
}();
}
}() + tan(z);

You can already do that, it's even uglier.

Nope. As opposed to:

int r;
if (y == 10) {
while (i--)
z += call(i);
r = z;
} else {
switch (x) {
case 6:
r = foo();
break;
default:
r = y;
break;
}
}

x = 45 + r + tan(z);

Jul 24 2015
On Friday, 24 July 2015 at 10:15:36 UTC, Walter Bright wrote:
Nope. As opposed to:

int r;
if (y == 10) {
while (i--)
z += call(i);
r = z;
} else {
switch (x) {
case 6:
r = foo();
break;
default:
r = y;
break;
}
}

x = 45 + r + tan(z);

My point was that you can effectively do the ugly thing already
in a worse way. I didn't say there aren't neater ways of getting
the same functionality in this particular case. Doesn't it
demonstrate that expressions returning values can make a given
piece of code tidier?

Jul 24 2015
"Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Thursday, 23 July 2015 at 20:09:34 UTC, Walter Bright wrote:
I agree that trivial syntax issues actually do matter, but
having used ?: a lot, I have a hard time seeing embeddable
if-else as a real improvement, in fact I find it more than a
little jarring to see.

Agreed. It makes more sense for switch/match, but D's switch
syntax is already quite heavy, an assignment in each branch

Jul 24 2015
Timon Gehr <timon.gehr gmx.ch> writes:
On 07/23/2015 10:09 PM, Walter Bright wrote:
Now, if you want to disallow { } within the embedded if statement, then
the proposal becomes nothing more than:

? => if
: => else

This is obviously not true:

a?b:c => a if b else c // ..?

which is a potayto potahto thing.

Some people have trouble with the precedence of ?: .

Fun fact: D and C++ have different precedence for ?: . :-)

Jul 24 2015
"jmh530" <john.michael.hall gmail.com> writes:
On Wednesday, 22 July 2015 at 20:43:04 UTC, simendsjo wrote:
When "everything" is an expressions, you can write things like
auto a = if(e) c else d;

In D you have to write
type a = invalid_value;
if(e) a = c;
else  a = d;
assert(a != invalid_value);

I prefer this example from one of the various Rust tutorials

let foo = if x == 5 {
"five"
}
else if x == 6 {
"six"
}
else {
"neither"
}

You're basically using a conditional expression as an rvalue. You
can do the same thing with a { } block.

Jul 22 2015
"John Colvin" <john.loughran.colvin gmail.com> writes:
On Wednesday, 22 July 2015 at 21:36:58 UTC, jmh530 wrote:
On Wednesday, 22 July 2015 at 20:43:04 UTC, simendsjo wrote:
When "everything" is an expressions, you can write things like
auto a = if(e) c else d;

In D you have to write
type a = invalid_value;
if(e) a = c;
else  a = d;
assert(a != invalid_value);

I prefer this example from one of the various Rust tutorials

let foo = if x == 5 {
"five"
}
else if x == 6 {
"six"
}
else {
"neither"
}

You're basically using a conditional expression as an rvalue.
You can do the same thing with a { } block.

Admittedly nowhere near as clean, but if you can bear to see the
"return"s, function literals can turn any bunch of code in to an
expression:

auto foo = { if(x == 5)
return "five";
else if(x == 6)
return "six";
else
return "neither";
}();

or of course there's the perhaps overly terse (brackets optional,
i like them to visually group the condition with the ? ):

auto foo = (x == 5)? "five"
: (x == 6)? "six"
: "neither";

Jul 22 2015
"jmh530" <john.michael.hall gmail.com> writes:
On Wednesday, 22 July 2015 at 22:35:54 UTC, John Colvin wrote:
Admittedly nowhere near as clean, but if you can bear to see
the "return"s, function literals can turn any bunch of code in
to an expression:

Honestly, I don't mind doing things the D way (and I probably
wouldn't have even done that your way). I just think it's
interesting when another programming language has a cool feature.

Jul 22 2015
Andre Kostur <andre kostur.net> writes:
On 2015-07-22 3:35 PM, John Colvin wrote:
On Wednesday, 22 July 2015 at 21:36:58 UTC, jmh530 wrote:
On Wednesday, 22 July 2015 at 20:43:04 UTC, simendsjo wrote:
When "everything" is an expressions, you can write things like
auto a = if(e) c else d;

In D you have to write
type a = invalid_value;
if(e) a = c;
else  a = d;
assert(a != invalid_value);

I prefer this example from one of the various Rust tutorials

let foo = if x == 5 {
"five"
}
else if x == 6 {
"six"
}
else {
"neither"
}

You're basically using a conditional expression as an rvalue. You can
do the same thing with a { } block.

Admittedly nowhere near as clean, but if you can bear to see the
"return"s, function literals can turn any bunch of code in to an
expression:

auto foo = { if(x == 5)
return "five";
else if(x == 6)
return "six";
else
return "neither";
}();

or of course there's the perhaps overly terse (brackets optional, i like
them to visually group the condition with the ? ):

auto foo = (x == 5)? "five"
: (x == 6)? "six"
: "neither";

Shouldn't that be its own function anyway?  If you needed it in one
place, you'll probably need it elsewhere.  And, in this case, it can
even be marked as pure.

Jul 23 2015
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/22/15 5:36 PM, jmh530 wrote:
On Wednesday, 22 July 2015 at 20:43:04 UTC, simendsjo wrote:
When "everything" is an expressions, you can write things like
auto a = if(e) c else d;

In D you have to write
type a = invalid_value;
if(e) a = c;
else  a = d;
assert(a != invalid_value);

I prefer this example from one of the various Rust tutorials

let foo = if x == 5 {
"five"
}
else if x == 6 {
"six"
}
else {
"neither"
}

You're basically using a conditional expression as an rvalue. You can do
the same thing with a { } block.

I used to be quite jazzed about the everything-is-an-expression mantra,
but it's not all great.

1. Inferring function return types when everything is an expression
(i.e. last expression there is the return type) may yield WAT results.

2. Defining a result type for loops is awkward.

At the end of the day everything-is-an-expression is natural for
functional languages, but doesn't seem it makes a large difference to an
imperative language.

To OP: thanks for your rant! Instead of getting defensive we'd do good
to derive action items from it.

Andrei

Jul 23 2015
"jmh530" <john.michael.hall gmail.com> writes:
On Thursday, 23 July 2015 at 13:30:49 UTC, Andrei Alexandrescu
wrote:
At the end of the day everything-is-an-expression is natural
for functional languages, but doesn't seem it makes a large
difference to an imperative language.

Good points.

Jul 23 2015
"Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Thursday, 23 July 2015 at 13:30:49 UTC, Andrei Alexandrescu
wrote:
I used to be quite jazzed about the everything-is-an-expression
mantra, but it's not all great.

1. Inferring function return types when everything is an
expression (i.e. last expression there is the return type) may
yield WAT results.

2. Defining a result type for loops is awkward.

At the end of the day everything-is-an-expression is natural
for functional languages, but doesn't seem it makes a large
difference to an imperative language.

It also works well for Ruby with its dynamic typing. Function
return types don't matter, of course, and for loops you just use
whatever the last executed expression happens to be.

Anyway, in D we have delegate literals, for the rare cases where
it's useful.

Jul 23 2015
Jacob Carlborg <doob me.com> writes:
On 2015-07-23 15:30, Andrei Alexandrescu wrote:

1. Inferring function return types when everything is an expression
(i.e. last expression there is the return type) may yield WAT results.

I have not had that problem with Scala. Either I want to return some
thing and let it be inferred, or I don't and declare it as Unit (void).

--
/Jacob Carlborg

Jul 23 2015
"Dicebot" <public dicebot.lv> writes:
On Thursday, 23 July 2015 at 19:20:20 UTC, Jacob Carlborg wrote:
On 2015-07-23 15:30, Andrei Alexandrescu wrote:

1. Inferring function return types when everything is an
expression
(i.e. last expression there is the return type) may yield WAT
results.

I have not had that problem with Scala. Either I want to return
some thing and let it be inferred, or I don't and declare it as
Unit (void).

I had a lot of frustration with that (mis)feature and Rust and
find it very unreadable. Because of that, so far I always used
explicit returns in Rust code even if it is not necessary - that
allows to quickly oversee all main exit points of the function.

That is mostly matter of programming culture and hard to
resonably justify in any way. Ironically, that would feel more
"at home" in D than in Rust because normally latter is much more
restrictive and explicit in the code style, such implicit
functional syntax sugar feels very alien in typically verbose and
detailed code.

Jul 23 2015
Jacob Carlborg <doob me.com> writes:
On 2015-07-23 21:26, Dicebot wrote:

I had a lot of frustration with that (mis)feature and Rust and find it
very unreadable. Because of that, so far I always used explicit returns
in Rust code even if it is not necessary - that allows to quickly
oversee all main exit points of the function.

Perhaps the two languages implement it differently.

That is mostly matter of programming culture and hard to resonably
justify in any way. Ironically, that would feel more "at home" in D than
in Rust because normally latter is much more restrictive and explicit in
the code style, such implicit functional syntax sugar feels very alien
in typically verbose and detailed code.

Yeah, I'm used to Ruby where it's implemented as well.

--
/Jacob Carlborg

Jul 24 2015
"shannon mackey" <refaQtor gmail.com> writes:
"> On Wednesday, 22 July 2015 at 19:41:16 UTC, Jack Stouffer
wrote:
On Wednesday, 22 July 2015 at 20:43:04 UTC, simendsjo wrote:
On Wednesday, 22 July 2015 at 18:47:33 UTC, simendsjo wrote:
Expressions
-----------
This probably also falls in the "too late" category, but
statements-as-expressions is really nice. auto a = if ...
<- why not?

Don't quite know what you mean here.

When "everything" is an expressions, you can write things like
auto a = if(e) c else d;

In D you have to write
type a = invalid_value;
if(e) a = c;
else  a = d;
assert(a != invalid_value);
"

I frequently do things like this in D:

auto good = ( true == true ) ? "true" : "false";

which looks a lot like your Rust example?  Is that not what you
were looking for?

Jul 23 2015
"shannon mackey" <refaQtor gmail.com> writes:
On Thursday, 23 July 2015 at 15:37:24 UTC, shannon mackey wrote:
"> On Wednesday, 22 July 2015 at 19:41:16 UTC, Jack Stouffer
wrote:
On Wednesday, 22 July 2015 at 20:43:04 UTC, simendsjo wrote:
On Wednesday, 22 July 2015 at 18:47:33 UTC, simendsjo wrote:
[...]

Don't quite know what you mean here.

When "everything" is an expressions, you can write things like
auto a = if(e) c else d;

In D you have to write
type a = invalid_value;
if(e) a = c;
else  a = d;
assert(a != invalid_value);
"

I frequently do things like this in D:

auto good = ( true == true ) ? "true" : "false";

which looks a lot like your Rust example?  Is that not what you
were looking for?

Sorry, I didn't read to the end of the thread, and this has been
covered many times.

Jul 23 2015
"bachmeier" <no spam.com> writes:
On Wednesday, 22 July 2015 at 20:43:04 UTC, simendsjo wrote:

Let me add a point for Rust somewhat related :)

Community
---------
The community is nice, helpful and doesn't condecent people.

I'd challenge you to write something other than 100% praise for
Rust and see how nice the community is. Not that their community
is bad, but they won't be winning any awards. Disclaimer: I gave
up on Rust quite a while ago.

I disagree. String mixins are much easier to abuse than hygenic
macros. String mixins allows anything, and while it offers
infinite possibilities, it also encourage abuse.

As someone that used to spend a lot of time with Lisp, I find it
funny that macros are promoted as a way to avoid abuse of
language features. On the issue of hygienic macros, there's a
reason the major Scheme implementations still offer Common
Lisp-style macros, in spite of the obvious theoretical advantages.

Jul 23 2015
Jacob Carlborg <doob me.com> writes:
On 2015-07-22 21:41, Jack Stouffer wrote:

Dub is endorsed by the leadership and is included in the same Github
organization as the complier and the standard library

sections on dlang.org, so I disagree.

--
/Jacob Carlborg

Jul 22 2015
"Joakim" <dlang joakim.fea.st> writes:
On Thursday, 23 July 2015 at 06:50:44 UTC, Jacob Carlborg wrote:
On 2015-07-22 21:41, Jack Stouffer wrote:

Dub is endorsed by the leadership and is included in the same
Github
organization as the complier and the standard library

Resources sections on dlang.org, so I disagree.

That's because it has its own top-level link in the sidebar, More
libraries.  You could argue that's not the best description of
dub, but it's certainly prominently placed.

Jul 23 2015
Jacob Carlborg <doob me.com> writes:
On 2015-07-23 09:45, Joakim wrote:

That's because it has its own top-level link in the sidebar, More
libraries.  You could argue that's not the best description of dub, but
it's certainly prominently placed.

Ah, right, I completely forgot about that. But one could expect the

--
/Jacob Carlborg

Jul 23 2015
"Alex Parrill" <initrd.gz gmail.com> writes:
I'm not at all familiar with Rust, so forgive me if I'm
misinterpreting something.

On Wednesday, 22 July 2015 at 18:47:33 UTC, simendsjo wrote:
Cargo
-----
Rust has a default package manager much like Dub. The main
difference is that Cargo has been endorsed by the Rust team and
is an official product.

I think I read that this may happen soon.

Traits
------
...

You can make a conformsToSomeInterface!T template, and use
static assert. D ranges, and the upcoming std.allocator,
already use this sort of 'interfaces without polymorphism'.

Ex. static assert(isInputRange!(MyCoolRange));

Macros
------
...

Most of what macros in C were used for are now done with
templates, static if, etc. (I don't know how Rust's macros work).
Tools could theoretically execute mixin, but it effectively
requires a D interpreter. A library to do that would be really
nice.

Borrowing
---------
...

Look into std.typecons.Unique, though I've seen people posting
that they don't like it (I haven't used it much; I had one use
case for it, which was sending it through std.concurrency.send,
but it doesn't work with that function).

Yes, D's community is pretty small. It's not something you can
just code; you have to market the language. And it's the
community that creates the many tools and packages.

Jul 22 2015
"simendsjo" <simendsjo gmail.com> writes:
On Wednesday, 22 July 2015 at 19:52:34 UTC, Alex Parrill wrote:
On Wednesday, 22 July 2015 at 18:47:33 UTC, simendsjo wrote:
Traits
------
...

You can make a conformsToSomeInterface!T template, and use
static assert. D ranges, and the upcoming std.allocator,
already use this sort of 'interfaces without polymorphism'.

Ex. static assert(isInputRange!(MyCoolRange));

Exactly. D is a lot more flexible, but looking just at
MyCoolRange, you cannot actually see it conforms to InputRange
without looking at the unittests (or wherever else the static
assert is inserted).

On Wednesday, 22 July 2015 at 19:52:34 UTC, Alex Parrill wrote:
On Wednesday, 22 July 2015 at 18:47:33 UTC, simendsjo wrote:
Borrowing
---------
...

Look into std.typecons.Unique, though I've seen people
posting that they don't like it (I haven't used it much; I had
one use case for it, which was sending it through
std.concurrency.send, but it doesn't work with that function).

I haven't actually tried to recreate the stuff that Rust does with
types in D. I expect some of it is possible, but the addition is
things like Unique (which I haven't tried). And this escalates.
You end up with Unique!(NonNull!(MyClass)) and probably even
more type decorators.

On Wednesday, 22 July 2015 at 19:52:34 UTC, Alex Parrill wrote:
Yes, D's community is pretty small. It's not something you can
just code; you have to market the language. And it's the
community that creates the many tools and packages.

If I'm not mistaken, people of the D community have tried to
market the language quite heavily. I don't know why more people
haven't joined, and it's even more baffeling to see the comments
on Reddit calling D related posts spam and speaking negatively of
the marketing on a site where upvotes dictates the ranking on the
front page.

Jul 22 2015
"John" <john.joyus gmail.com> writes:
On Wednesday, 22 July 2015 at 20:50:53 UTC, simendsjo wrote:
If I'm not mistaken, people of the D community have tried to
market the language quite heavily. I don't know why more people
haven't joined, and it's even more baffeling to see the comments
on Reddit calling D related posts spam and speaking negatively
of
the marketing on a site where upvotes dictates the ranking on
the
front page.

I wish all the D related posts go under the sub-reddit
https://www.reddit.com/r/dlang

dlang is a familiar name due to the dlang.org itself. Also, the
pattern is easy to guess, like the golang.

You may be having tens of sub-reddits for D but they all look
non-standard and confusing at best.

Jul 28 2015
"John" <john.joyus gmail.com> writes:
On Tuesday, 28 July 2015 at 15:39:13 UTC, John wrote:
I wish all the D related posts go under the sub-reddit
https://www.reddit.com/r/dlang

dlang is a familiar name due to the dlang.org itself. Also, the
pattern is easy to guess, like the golang.

You may be having tens of sub-reddits for D but they all look
non-standard and confusing at best.

To make my point more clear, the other language groups post their
announcements to their respective sub-reddits like r/rust,
r/golang etc, while D group tries to post *everything* directly
to the r/programming. This is what makes them call it a spam.

Jul 29 2015
Jacob Carlborg <doob me.com> writes:
On 2015-07-22 21:52, Alex Parrill wrote:

You can make a conformsToSomeInterface!T template, and use static
assert. D ranges, and the upcoming std.allocator, already use this sort
of 'interfaces without polymorphism'.

Ex. static assert(isInputRange!(MyCoolRange));

I would be a lot more cleaner to be able to do this:

void foo (T : MyCoolRange) (T range);

Or just:

void foo (MyCoolRange range);

Most of what macros in C were used for are now done with templates,
static if, etc. (I don't know how Rust's macros work). Tools could
theoretically execute mixin, but it effectively requires a D
interpreter. A library to do that would be really nice.

Macros in C have nothing to do with macros in Rust, which are called AST
macros or syntax macros. It's unfortunate that they use the same word,
"macro".

If D macros you could do something like this:

auto person = Person.where(e => e.name == 'Foo');

Where the lambda would be translated do an SQL string and performs a
query in a database.

--
/Jacob Carlborg

Jul 22 2015
"Dicebot" <public dicebot.lv> writes:
Traits system is awesome and pure win. Pattern matching is not
that game changing but helps often enough to feel important.
Borrowship system is damn smart but totally impractical for most
real-world cases. Macros are utterly horrible and pretty much
unusable outside of advanced library internals.

Recently I attended local Rust meetup for curious newcomers - it
was very interesting to observe reaction of unbiased devs not
familiar with D at all. General reaction was "this is awesome
interesting language that I would never use for any production
system unless I am crazy or can throw away money like crazy".
Because, well, productivity.

D has done many things wrong, but there is one right thing that
totally outshines it all - it is cost-effective and pragmatical
tool for a very wide scope of applications.

Jul 22 2015
"simendsjo" <simendsjo gmail.com> writes:
On Wednesday, 22 July 2015 at 19:54:05 UTC, Dicebot wrote:
Traits system is awesome and pure win.

Agreed.

Pattern matching is not > that game changing but helps often
enough to feel important.

The fact that you can use pattern matching many places makes it
very much a win.

if Some(InnerClass::SomeType(value)) = some_call() {
// here you can use value
}

Borrowship system is damn smart but totally impractical for
most real-world cases.

I haven't used Rust enough to really have a voice on the subject.
It looks like a pardigm shift, and it might only take some getting
used to, but it might also be very difficult to use. There are
some big stuff written in Rust though - the rust compiler and the
servo browser engine. The fact that it makes a lot of errors
impossible is the exiting thing for me.

Macros are utterly horrible and pretty much unusable outside

Not sure what you are referencing here. Macros expand to code. If
you compare this to string mixins, they are a lot easier for tool
writers, but a lot less powerful.

Recently I attended local Rust meetup for curious newcomers -
it was very interesting to observe reaction of unbiased devs
not familiar with D at all. General reaction was "this is
awesome interesting language that I would never use for any
production system unless I am crazy or can throw away money
like crazy". Because, well, productivity.

I'm having some problems interpreting this. This is people in
a Rust meetup - in other words, early adopters. And they thing
D is crazy "becuse productivity"? I don't understand what you
mean.

D has done many things wrong, but there is one right thing that
totally outshines it all - it is cost-effective and pragmatical
tool for a very wide scope of applications.

Yes, D is pragmatic and extremely powerful and meldable to every
usecase.

Jul 22 2015
"Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Wednesday, 22 July 2015 at 21:03:52 UTC, simendsjo wrote:
On Wednesday, 22 July 2015 at 19:54:05 UTC, Dicebot wrote:
Recently I attended local Rust meetup for curious newcomers -
it was very interesting to observe reaction of unbiased devs
not familiar with D at all. General reaction was "this is
awesome interesting language that I would never use for any
production system unless I am crazy or can throw away money
like crazy". Because, well, productivity.

I'm having some problems interpreting this. This is people in
a Rust meetup - in other words, early adopters. And they thing
D is crazy "becuse productivity"? I don't understand what you
mean.

What I understood that he meant was that it was interesting to
see the reaction of folks who have nothing to with D when they
learned about Rust. So, they weren't likely colored by whatever
preconceptions you'd get out of someone who's already invested in
D. They were reacting to Rust without D being in the picture at
all. And their reaction to Rust was that it was cool and
interesting but that it would be crazy to use it in production,
because it's not productive (or at least, they didn't think that
it was) - presumably because some of its features make it too
hard to use, but dicebot would have to elaborate.

- Jonathan M Davis

Jul 22 2015
"Dicebot" <public dicebot.lv> writes:
On Wednesday, 22 July 2015 at 21:03:52 UTC, simendsjo wrote:
Macros are utterly horrible and pretty much unusable outside

Not sure what you are referencing here. Macros expand to code.
If
you compare this to string mixins, they are a lot easier for
tool
writers, but a lot less powerful.

Compared to string mixins they require much much more effort to
learn and reason about. In our meetup group no one was able to
figure out meaning of any small sample macro without detailed
explanation. Compared to string mixins they are much harder to
abuse because of inherent hygiene but also much harder to start
using, while in D the concept comes quick and naturally but
requires discipline to get used in maintainable fashion.

And they definitely don't feel any easier for library writers
from my personal experience. More maintainable - yes, but not
easier at all.

Recently I attended local Rust meetup for curious newcomers -
it was very interesting to observe reaction of unbiased devs
not familiar with D at all. General reaction was "this is
awesome interesting language that I would never use for any
production system unless I am crazy or can throw away money
like crazy". Because, well, productivity.

I'm having some problems interpreting this. This is people in
a Rust meetup - in other words, early adopters. And they thing
D is crazy "becuse productivity"? I don't understand what you
mean.

Not early adopters, more like curious group learning new stuff
together. And "crazy" was about Rust and how impractical it is
for real business needs they have. No one has ever mentioned D
there.

Jul 22 2015
On Wednesday, 22 July 2015 at 21:03:52 UTC, simendsjo wrote:
On Wednesday, 22 July 2015 at 19:54:05 UTC, Dicebot wrote:
Macros are utterly horrible and pretty much unusable outside

Not sure what you are referencing here. Macros expand to code.
If
you compare this to string mixins, they are a lot easier for
tool
writers, but a lot less powerful.

I've read that someone managed to implement compile time regex
using macros in Rust. The author of it noted that D was the only
other language he knew of that had pulled that off. The fact that
it's expressive enough to pull off one of Phobo's coolest tricks
is impressive I think. I don't know enough about it to have my
own opinions of how it fairs against D in general. It could very
well be that doing stuff like that is far beyond anyone but the
most advanced users though I don't think many D users could pull
of what Dmitry has done either.

Jul 23 2015
On Wednesday, 22 July 2015 at 19:54:05 UTC, Dicebot wrote:
D has done many things wrong, but there is one right thing that
totally outshines it all - it is cost-effective and pragmatical
tool for a very wide scope of applications.

+1, from a business D user!
---
Paolo

Jul 23 2015
"Laeeth Isharc" <laeethnospam nospamlaeeth.com> writes:
Dicebot:
D has done many things wrong, but there is one right thing that
totally outshines it all - it is cost-effective and pragmatical
tool for a very wide scope of applications.

would you care to write more on this as a blog post, making it
vivid and setting out some examples?  that's my tentative
judgement too, and why I am here, but I lean very heavily on my
intuition and that's not enough to persuade other people when I
don't yet have mastery of the relevant domain knowledge, having
returned to programming quite recently.  I am talking to another
decent-sized hf that might be open to exploring the use of D, but
the more vivid people are able to make the case the better.

Jul 23 2015
"Dicebot" <public dicebot.lv> writes:
On Thursday, 23 July 2015 at 16:46:01 UTC, Laeeth Isharc wrote:
Dicebot:
D has done many things wrong, but there is one right thing that
totally outshines it all - it is cost-effective and
pragmatical tool for a very wide scope of applications.

would you care to write more on this as a blog post, making it
vivid and setting out some examples?  that's my tentative
judgement too, and why I am here, but I lean very heavily on my
intuition and that's not enough to persuade other people when I
don't yet have mastery of the relevant domain knowledge, having
returned to programming quite recently.  I am talking to
another decent-sized hf that might be open to exploring the use
of D, but the more vivid people are able to make the case the
better.

AFAIR Don had quite a good summary at DConf 2013
(http://dconf.org/2013/talks/clugston.html) for how it applies to
our business. I guess that presentation can still be used as
selling point.

I like to put it this way : only very few of Sociomantic
developers knew at least something about D before joining the
company. It were mostly C++/Java/Whatever developers which
learned everything on spot. We haven't even had any special
training courses - just giving one book (Learn Tango with D) and
few weeks of time to experiment was usually enough to start
righting some production code, learning more advanced stuff later
on per-need basis from reviewer comments.

Considering growing deficit of skilled programmers in the
industry in general being able to kickstart into new language
like that is a very big deal for business. C style syntax brings
familiarity and being able to write working apps with simple
concepts only (even if they are un-idiomatic in "modern D")
greatly improves learning curve.

No matter how many effort is put into tooling or documentation, I
simply can't see Rust ever being used like that. Well, unless it
gets studied commonly as part of computer science BSc and most
new devs will be at least faimilar with it. Writing simple number
guessing app (like one presented in Rust book) can easily take
half an hour for even experienced (but new to Rust) developer -
it simply won't compile until you get every single smallest bit
_right_. In small to medium business you simply can't afford
investments like that.

I see Rust as possible language of choice for a very small (but
important) subset of applications of big enough size that
maintenance costs are much greater than development costs AND
both performance and safety matter. AAA games, life-critical
real-time software, software monsters like your next Firefox or
Photoshop. That is big enough share of market in terms of money
for language to keep being demanded but it is tiny minority of
applications in terms of pure project count. It is clearly not

Jul 23 2015
"jmh530" <john.michael.hall gmail.com> writes:
On Wednesday, 22 July 2015 at 18:47:33 UTC, simendsjo wrote:
Traits
------
I think the ability to express an interface without buying into
inheritance is the right move. The alternative in D is
specifying the behavior as a template and verifying the
contract in a unittest for the type.

Rust only has structs. I'm not as familiar with them so it's not
as clear how they overlap with D's structs and classes. It seems
like you can put Rust structs on the stack or heap.

Others have already commented on how you can basically accomplish
the same thing with templates, static ifs, and assert. That was
my first thought as well. Nevertheless, I would add that Rust's
ability to use multiple traits in the template constraints is a
positive.

However, it seems like the essence of a trait in Rust is forcing
some user-defined type to implement some functionality. That
sounds a lot like an interface in D to me. In fact, Rust's traits
seem different from Scala and PHP traits in that I don't see any
examples with them using implementation. The two biggest
differences between Rust traits and D interfaces are 1)
inheritance of Rust traits (to my knowledge D does not have
inheritance for interfaces, am I wrong?), 2) Rust traits can be
easily used in template constraints. Those both seem like
positives to me, though I'm not sure if they can be easily
changed in D.

If you look at the Rust book's main example on traits. They use
them to implement a print_area function that can only be called
with structs that have a trait HasArea defined. Classes in pretty
much any OOP language could use inheritance to implement a
simpler version where print_area is a method instead of a
separate function.

To play devil's advocate, writing a bunch of code with
templates/static ifs/assert is definitely more confusing for
someone new to the language than Rust's traits are. A lot of the
D code makes quite a bit of sense when you understand how the
different ideas combine together, but it takes a while to figure
it out.

If traits were deemed important enough to add to D, I would
suggest just extending interfaces so that they have inheritance
and can be easily used in template constraints. I would be more
interested in seeing some of these OOP features come to structs,
but I have no idea how much work that requires. I feel like alias
this is hacky compared to real inheritance.

Algebraic data types
--------------------
Haven't looked into Algebraic!, so I won't start bashing D
here :) But given the lack of pattern matching, I doubt it will
be as pretty as Rust.

It seems like D's Algebraic does not allow for recursive
Algebraic Types currently, whereas Rust's does. I'm honestly not
that familiar with the concept. Seems cool, but I'm not sure I've
ever really needed it.

Borrowing
---------
This is probably the big thing that makes Rust really
different.  Everything is a resource, and resources have an
owner and a lifetime. As a part of this, you can either have
multiple aliases with read-only references, or a single
reference with a writeable reference. I won't say I have a lot
of experience with this, but it seems like it's not an
extremely unergonomic trade-off. I cannot even remotely imagine
the amount of possible compiler optimizations possible with
this feature.

I feel like it's hard to separate borrowing from Rust's variety
of pointers (& is borrowed pointer, ~ is for unique pointer,   is
for managed pointer). Nevertheless, I think having them part of
the language with relatively straightforward syntax is a positive
for Rust. Also, my understanding is that they are checked at
compile-time for safety (at least unique and managed).

However, I feel like borrowing and lifetimes have a bit of a
learning curve to them. I've read a few tutorials without a good
sense of them. I would probably need to program a bit in it to
grok it. I think part of it is that Rust has = potentially
meaning copy or move depending on whether it is defined. Perhaps
they need a separate move assignment, like <- and -> or something
(to steal syntax from R, even though that's not what <- and -> do
in R).

Jul 22 2015
"jmh530" <john.michael.hall gmail.com> writes:
On Wednesday, 22 July 2015 at 23:25:56 UTC, jmh530 wrote:
If traits were deemed important enough to add to D, I would
suggest just extending interfaces so that they have inheritance
and can be easily used in template constraints.

documentation does not say anything about interface inheritance,
but it seems to work when I tried it. It definitely seems weird
that this behavior isn't mentioned anywhere.

Also, the use of static* or final methods in the interface can
allow default implementations of methods. This is something that
can be done with Scala traits. I feel like this isn't
documentated as well as it could be. A weirder thing is that when
I tried to test that static and final methods could not
overridden (which is allowed in Scala), it seemed like they were
getting overridden. The tricky part is that I had been using
something like final foo() { } instead of final void foo() { }.
For some reason you can override the first, but not the second.

So really, I guess the D interfaces are more powerful than I had
thought. Only downside is that they only work for classes.

* I feel like these also aren't particularly described well in
the documentation either.

Jul 23 2015
"Adam D. Ruppe" <destructionator gmail.com> writes:
On Thursday, 23 July 2015 at 15:20:36 UTC, jmh530 wrote:
It definitely seems weird that this behavior isn't
mentioned anywhere.

It isn't really expanded upon, but the : Interfaces at the top
here tells you can inherit them:

http://dlang.org/interface.html

It doesn't show an example in the text of an interface extending
another interface, but it does say "interfaces can be inherited".

Also, the use of static* or final methods in the interface can
allow default implementations of methods.

Eh sort of but not really. static and final methods cannot be
overridden in the virtual sense.

interface Foo {
final void foo() { writeln("foo"); }
}
class Bar : Foo {
override void foo() { writeln("foo from bar"); }
}

b.d(8): Error: function b.Bar.foo does not override any function,
did you mean
o override 'b.Foo.foo'?
b.d(8): Error: function b.Bar.foo cannot override final function
Foo.b.Foo.foo

Static is similar, change those finals to static and you get:

b.d(8): Error: function b.Bar.foo cannot override a non-virtual
function

Take override off and it compiles... but is statically dispatched:

Foo bar = new Bar();
bar.foo(); // calls the version from the interface

Bar bar = new Bar();
bar.foo(); // calls the version from the class

That's the main thing with interfaces: you are supposed to get
the overridden method when you call it through the base, but that
only happens if it is virtual - neither final nor static. And
those cannot have default implementations in a D interface, only
in a class.

The tricky part is that I had been using something like final
foo() { } instead of final void foo() { }. For some reason you
can override the first, but not the second.

You just defined a template by skipping the return type...
templates are allowed in D interfaces and classes, but are always
implicitly final, and thus get the behavior above - if you call
it through the interface, you get a different func than calling
it through the class.

BTW you can also check interface conversion for objects in a
template function and get static dispatch that way... sort of:

void something(T : Foo)(T bar) {
bar.foo();
}
void main() {
Foo bar = new Bar();  // explicitly using the interface
something(bar); // leads to this calling "foo"

// but if you did "auto bar = new Bar();" or "Bar bar"
// it would call "foo from bar"
}

Jul 23 2015
"jmh530" <john.michael.hall gmail.com> writes:
On Thursday, 23 July 2015 at 15:38:53 UTC, Adam D. Ruppe wrote:
It isn't really expanded upon, but the : Interfaces at the top
here tells you can inherit them:

http://dlang.org/interface.html

It doesn't show an example in the text of an interface
extending another interface, but it does say "interfaces can be
inherited".

I see that now. It's there, but it's practically a throw-a-way
line. I've looked at that page a bunch of times and skipped over
it each time. This is a broader problem with D's reference pages.

Compare
http://dlang.org/interface.html
with
https://doc.rust-lang.org/book/traits.html
which is better?

Rust's documentation uses clear examples to show how something
should be used and the most important features. By contrast,
there are many parts of D's documentation that someone with
less-than-expert programming knowledge will find quite difficult
to understand.

For instance, look at
http://dlang.org/function.html
you have to scroll down for quite a while before you even get to
anything useful. Then, what's the first thing that comes up?
Something about contracts, return values, bodies, purity. Not
"this is what a D function is".

I'm all for a complete reference of every D feature, but there
needs to be some step up in terms of difficulty of the material.
Start with the basics, then work up into all the specifics. I'd
say at a minimum some of this documentation needs to be broken up
into several pages.

There seem to be a lot of places where I could improve the D
documentation. However, I feel like if I start going crazy with
changes I might get some push-back...

BTW you can also check interface conversion for objects in a
template function and get static dispatch that way... sort of:

void something(T : Foo)(T bar) {
bar.foo();
}
void main() {
Foo bar = new Bar();  // explicitly using the interface
something(bar); // leads to this calling "foo"

// but if you did "auto bar = new Bar();" or "Bar bar"
// it would call "foo from bar"
}

This is cool and I wasn't aware that you could do this. I was
trying to use std.traits' InterfacesTuple to test that a class

However, I also find it a bit confusing. I don't understand
precisely what it means when the interface is on the left. It's
like you're using the class to create an object of a type the
same as the interface...Nevertheless, the interface page says you
can't do this
D d = new D();
where D is an interface. However, it also says that you can
D d = cast(D) b;
where b is some an object of some other class B that inherits the
interface D. You example does not fit either of these cases.
Then, if you change Foo to not be final and Bar to not override
and run it, it will call "foo from bar" regardless of whether you
do
Foo bar = new Bar();
or
Bar bar = new Bar();

Jul 23 2015
"H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Thu, Jul 23, 2015 at 04:54:50PM +0000, jmh530 via Digitalmars-d wrote:
[...]
I see that now. It's there, but it's practically a throw-a-way line.
I've looked at that page a bunch of times and skipped over it each
time. This is a broader problem with D's reference pages.

Compare
http://dlang.org/interface.html
with
https://doc.rust-lang.org/book/traits.html
which is better?

Rust's documentation uses clear examples to show how something should
be used and the most important features. By contrast, there are many
parts of D's documentation that someone with less-than-expert
programming knowledge will find quite difficult to understand.

For instance, look at http://dlang.org/function.html you have to
scroll down for quite a while before you even get to anything useful.
Then, what's the first thing that comes up? Something about contracts,
return values, bodies, purity. Not "this is what a D function is".

I'm all for a complete reference of every D feature, but there needs
to be some step up in terms of difficulty of the material. Start with
the basics, then work up into all the specifics. I'd say at a minimum
some of this documentation needs to be broken up into several pages.

There seem to be a lot of places where I could improve the D
documentation.  However, I feel like if I start going crazy with
changes I might get some push-back...

[...]

Please bring on the PR's, we need all the help we can get!

As for push-back... as long as each PR is focused on one thing, e.g.,
improving the prose, or adding a beginner-friendly example to the top of
the page, I think it will be acceptable. Just don't put too many changes
into one PR (reformat the source, fix whitespace, rewrite 5 disparate
paragraphs, etc., you know what I mean).

T

--
Notwithstanding the eloquent discontent that you have just respectfully
expressed at length against my verbal capabilities, I am afraid that I must
unfortunately bring it to your attention that I am, in fact, NOT verbose.

Jul 23 2015
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/23/15 12:54 PM, jmh530 wrote:
There seem to be a lot of places where I could improve the D
documentation. However, I feel like if I start going crazy with changes
I might get some push-back...

Focused, specialized, and  clear pull requests are the way to go here.
-- Andrei

Jul 23 2015
"jmh530" <john.michael.hall gmail.com> writes:
On Thursday, 23 July 2015 at 16:54:52 UTC, jmh530 wrote:
This is cool and I wasn't aware that you could do this. I was
trying to use std.traits' InterfacesTuple to test that a class

Again, I just found a reference to this behavior in the template
section where it talks about Argument Deduction. No idea it was
there. There were two things that I thought Rust's traits could
do, but D's interfaces couldn't do. Now it seems like D's
interfaces could do both of them.

Jul 23 2015
"Adam D. Ruppe" <destructionator gmail.com> writes:
On Thursday, 23 July 2015 at 16:54:52 UTC, jmh530 wrote:
Rust's documentation uses clear examples to show how something
should be used and the most important features. By contrast,
there are many parts of D's documentation that someone with
less-than-expert programming knowledge will find quite
difficult to understand.

Yeah, I agree. Here's what I've been trying to write:

A template for tutorials:

show how to do it

link to features as they are discussed

Feature template:
What it is
so like a mixin template is a list of
declarations with names
Core concept in usage
mixin template example, basic use
use with templates
use with a name to mix in overloads
Where it is used in tutorials and other places IRL

That's the skeleton outline for how I want to do docs. The "what
it is" we kinda have now, though it could often be clearer, but
we don't have the nice examples (well, outside of things like my
book) and we certainly don't have the top-level how to do X in

Of course, this goes slowly because answering questions is kinda
fast, but making it archivable and presentable in depth is a slow
process and I just have a hundred other things to do too :(

speaking of which, work meeting in 30 mins, and my presentation
isn't ready yet, i shouldn't be on this forum at all right now!

I'm all for a complete reference of every D feature, but there
needs to be some step up in terms of difficulty of the
specifics. I'd say at a minimum some of this documentation
needs to be broken up into several pages.

That would be good. I also think a top-level "the boss whats you
to do X and needs it by end of day, follow these steps to learn
how and read these links to dive deeper" would be super useful.
They should feed into each other.

However, I also find it a bit confusing. I don't understand
precisely what it means when the interface is on the left.

You always create objects, but you can work with interfaces.
Ideally, in OOP theory, your functions always work with just
interfaces and they don't care what specific class was
constructed in the user code. This makes those functions most

So for the local variable, using an interface doesn't make as
much sense, but it is common to write:

void doSomething(MyInterface i) { /* work with i */ }

void main() {
Foo bar = new Foo();
doSomething(bar); // bar becomes a MyInterface for this call
}

which would work the same way. Objects will implicitly convert to
their interfaces, so you can pass them around to those generic
functions.

D also has templates which do something similar at first glace,
but work on an entirely different principal...

tutorials or documentation. D's system is very similar to Java's
so if you understand that foundation a lot more of it will make
sense.

Then the next level is the final things and the templates and how
they interact which I'm touching the surface on here.

cases. Then, if you change Foo to not be final and Bar to not
override and run it, it will call "foo from bar" regardless of
whether you do
Foo bar = new Bar();
or
Bar bar = new Bar();

yeah, if it is non-final, the child implementation gets called.
This allows you to substitute a derived class for the base class
or interface while getting new behavior.

Jul 23 2015
"jmh530" <john.michael.hall gmail.com> writes:
On Thursday, 23 July 2015 at 17:34:45 UTC, Adam D. Ruppe wrote:
Feature template:
What it is
so like a mixin template is a list of
declarations with names
Core concept in usage
mixin template example, basic use
use with templates
use with a name to mix in overloads
Where it is used in tutorials and other places IRL

That's the skeleton outline for how I want to do docs. The
"what it is" we kinda have now, though it could often be
clearer, but we don't have the nice examples (well, outside of
things like my book) and we certainly don't have the top-level
how to do X in the whole ecosystem, and the interlinking to

Definitely sounds interesting.

That would be good. I also think a top-level "the boss whats
you to do X and needs it by end of day, follow these steps to
learn how and read these links to dive deeper" would be super
useful. They should feed into each other.

I actually just had your D Cookbook delivered a few days ago.
Beyond a few cases of extra semi-colons, one of the things that
really stood out were where you took something I would find very
complicated, describe the few key steps to accomplish the
complicated task, and then follow up with a bunch of code that
does those steps. I like that teaching method.

Jul 23 2015
"Adam D. Ruppe" <destructionator gmail.com> writes:
On Thursday, 23 July 2015 at 17:43:49 UTC, jmh530 wrote:
I actually just had your D Cookbook delivered a few days ago.
Beyond a few cases of extra semi-colons

That gets better! I didn't realize how the editing process worked
when I turned in chapter one so it is embarrassingly bad in
places, absolutely horrible start, but I figured it out after
that so the code will get a lot prettier.

one of the things that really stood out were where you took
something I would find very complicated, describe the few key
with a bunch of code that does those steps. I like that
teaching method.

I think a strong foundation - knowing the basics really well - is
the key to being a good programmer. Most hard things can be
broken down into a combination of a few basic things. Andrei's
"design by introspection" hits this same principle: looking at
pieces and just working with them, knowing what they can and
can't do one part at a time, is easier than trying to name and
master all the various combinations of them.

So that was what I wanted to do in my book: start with something
bigger, but focus on the building blocks with the hope that
you'll be able to reassemble them into something new for yourself
in the end.

A concrete example, consider std.typecons.Typedef. I think that
is totally useless because if you understand what it does, it is
trivial to do your own with various customizations specific to
you (like allowing or disallowing implicit conversions, disabling
individual operators, etc).

Or to just do the basics is easy if you know how it is built (it
is really just a plain struct!).

Whereas if you don't understand the building blocks, you're lost
when you need to customize it somehow (and there's too many
options to reasonably name each of them in Phobos)... and you are
also likely to be surprised by bugs when they come up, like with

So knowing structs is IMO far more valuable knowledge than
knowing Typedef.

especially as starting points, so when i get to my tutorial
series, I want to get more of that.

Jul 23 2015
=?UTF-8?Q?Tobias=20M=C3=BCller?= <troplin bluewin.ch> writes:
"jmh530" <john.michael.hall gmail.com> wrote:
I feel like it's hard to separate borrowing from Rust's variety of
pointers (& is borrowed pointer, ~ is for unique pointer,   is for managed
pointer).

That's actually very outdated information. Two of the four pointer types
(&, ,~ and *) were ditched in favor of library solutions.

*T: Raw pointers are still the same
&T: is now called reference, not borrowed pointer
~T: Is now Box<T>
T: Is now Rc<T> (and possibly Gc<T> although a GC was never implemented)
Tobi

Jul 23 2015
Ziad Hatahet via Digitalmars-d <digitalmars-d puremagic.com> writes:
On Wed, Jul 22, 2015 at 4:25 PM, jmh530 via Digitalmars-d <
digitalmars-d puremagic.com> wrote:
I feel like it's hard to separate borrowing from Rust's variety of
pointers (& is borrowed pointer, ~ is for unique pointer,   is for managed
pointer).

Rust has ditched the ~ and   syntax for pointers for a long time now. For
unique pointers they use Box<T>, and for managed pointers it is either
Rc<T>, Arc<T>, or Gc<T>, depending on the desired behavior. The last one is
currently still under development I believe.

Jul 23 2015
"Bienlein" <jeti789 web.de> writes:
I think the ability to express an interface without buying into
inheritance is the >>right move. The alternative in D is
specifying the behavior as a template and >>verifying the
contract in a unittest for the type.

Rust only has structs. I'm not as familiar with them so it's
not as clear how they >>overlap with D's structs and classes.
It seems like you can put Rust structs on the >>stack or heap.

What some people call "interface inheritance" in Rust is not
inheritance in the sense of OOP whewre an inherited method can be
overwritten. Rust is here the same as Go. Some people sometimes
get confused and don't see that without the possibility of
overwriting an inherited method delegation applies and not
inheritance. This is also explained in this blog:
http://objectscape.blogspot.de/search/label/Go See the section
titled "Inheritance".

-- Bienlein

Jul 26 2015
"rsw0x" <anonymous anonymous.com> writes:
On Wednesday, 22 July 2015 at 18:47:33 UTC, simendsjo wrote:
...

I think rust makes the ugliness of D's "push everything into
phobos for simplicity" become very visible. D and Rust share many
equal constructs, but D's is almost always uglier.

Jul 22 2015
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/22/15 7:47 PM, rsw0x wrote:
On Wednesday, 22 July 2015 at 18:47:33 UTC, simendsjo wrote:
...

I think rust makes the ugliness of D's "push everything into phobos for
simplicity" become very visible. D and Rust share many equal constructs,
but D's is almost always uglier.

Care for a list? Thanks! -- Andrei

Jul 23 2015
"QAston" <qastonx gmail.com> writes:
On Thursday, 23 July 2015 at 13:33:48 UTC, Andrei Alexandrescu
wrote:
On 7/22/15 7:47 PM, rsw0x wrote:
On Wednesday, 22 July 2015 at 18:47:33 UTC, simendsjo wrote:
...

I think rust makes the ugliness of D's "push everything into
phobos for
simplicity" become very visible. D and Rust share many equal
constructs,
but D's is almost always uglier.

Care for a list? Thanks! -- Andrei

Ugliness is in the eyes of the beholder, for me for example is
kinda funny when people call D syntax better than Rust, for me
those are nearly the same (I do know lisp).

Rust's compiler is aware of some traits and attributes:
http://doc.rust-lang.org/std/clone/trait.Clone.html which
disables move semantics
or other traits grouped here:
http://doc.rust-lang.org/std/marker/

This works sort of the way D compiler is aware of input ranges or
druntime for foreach and makes it possible to create alternative
standard libraries like https://github.com/carllerche/mio

D has equivalent capabilities in most cases, but it's way less
consistent in a way they're provided.

As an example: D has __traits + isExpressions + template
constraints phobos wrappers where Rust has trait:
http://doc.rust-lang.org/std/marker/trait.Reflect.html + template
constraints
D has some rules which types have size at compile time, Rust has
http://doc.rust-lang.org/std/marker/trait.Sized.html
D has rules for sharing certain types, Rust has
http://doc.rust-lang.org/std/marker/trait.Sync.html
traits: http://rustbyexample.com/trait/ops.html
D has special druntime Object type, Rust has a hash trait, cmp
trait http://doc.rust-lang.org/core/cmp/index.html
http://doc.rust-lang.org/core/hash/index.html which are more
elastic.

This is in no way deal breaker because things are there in D. On
the other hand learning is much easier because things are
consistent. First example did hurt me badly, juggling 6 docs
files for type introspection. That's what I think simendsjo meant

I think the reason for this "mess" in D is that D is developed in
a very ad hoc way, while for rust things really get peer reviewed
and tested for a long period of time.

Jul 23 2015
"simendsjo" <simendsjo gmail.com> writes:
On Thursday, 23 July 2015 at 16:22:30 UTC, QAston wrote:
(... snip ...)
That's what I think simendsjo meant about "ugliness".

Someone else wrote about the "ugliness", not me.

Jul 23 2015
"ponce" <contact gam3sfrommars.fr> writes:
I've not used Rust, but don't plan to.

On Wednesday, 22 July 2015 at 18:47:33 UTC, simendsjo wrote:
While code.dlang.org has 530 packages, crates.io has 2610
packages,

I think this tells something foremost about the size of the
community. More people leads to more code.

Traits
------
I think the ability to express an interface without buying into
inheritance is the right move. The alternative in D is
specifying the behavior as a template and verifying the
contract in a unittest for the type.

Traits can't do Design by Introspection aka compile-time duck.
C++ can on the other hand.

Algebraic data types
--------------------
Haven't looked into Algebraic!, so I won't start bashing D
here :) But given the lack of pattern matching, I doubt it will
be as pretty as Rust.

You can pattern-match with visit.

Macros
------
I haven't written more than extremely simple macros in Rust,
but having macros that is possible for tools to understand is a
win.  Templates and string mixins is often used for this
purpose, but trying to build tools when string mixins exists is
probably extremely hard. If D had hygenic macros, I expect
several features could be expressed with this instead of string
mixins, making tooling easier to implement.

There is a difference though: Rust forces macros on you on the
get go, while in D string mixing are quite a rare occurence
thanks to other meta things and don't have a weird separate
syntax. Regular templates + tuple foreach + static if is just
easier to debug.

Borrowing
---------
This is probably the big thing that makes Rust really
different.  Everything is a resource, and resources have an
owner and a lifetime. As a part of this, you can either have
multiple aliases with read-only references, or a single
reference with a writeable reference. I won't say I have a lot
of experience with this, but it seems like it's not an
extremely unergonomic trade-off. I cannot even remotely imagine
the amount of possible compiler optimizations possible with
this feature.

The problem I see with this is that it is exactly like C++ scoped
ownership except enforced. Even in Rust meeting they said they
were converging on C++. It remotely feels like the same language
to me.

I've worked in fully scoped ownership codebases, it's very nice
and consistent, and you don't feel like you would need anything
else while doing it. You must train everyone to do it. Enforcing
you debug and you have to comment stuff out wildly.

Also if you are going to replace C++, it is a given to at least
compile faster, make solve problem #1.

Jul 22 2015
Jacob Carlborg <doob me.com> writes:
On 2015-07-23 08:46, ponce wrote:

Algebraic data types
--------------------
Haven't looked into Algebraic!, so I won't start bashing D here :)
But given the lack of pattern matching, I doubt it will be as pretty
as Rust.

You can pattern-match with visit.

I had a look at the example for "visit" and looked at the
implementation. I would hardly call that "pattern matching".

--
/Jacob Carlborg

Jul 23 2015
"Nick B" <nick.barbalich gmail.com> writes:
On Thursday, 23 July 2015 at 06:46:14 UTC, ponce wrote:
I've not used Rust, but don't plan to.

On Wednesday, 22 July 2015 at 18:47:33 UTC, simendsjo wrote:
While code.dlang.org has 530 packages, crates.io has 2610
packages,

I think this tells something foremost about the size of the
community. More people leads to more code.

But does it reflect the size of the community? Look at these
numbers, below.
D is ranked no 26, Rust is not in the top 50 !!

http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html

Nick

Jul 23 2015
Jacob Carlborg <doob me.com> writes:
On 2015-07-22 20:47, simendsjo wrote:

Traits
------
I think the ability to express an interface without buying into
inheritance is the right move. The alternative in D is specifying the
behavior as a template and verifying the contract in a unittest for the
type.

I completely agree and don't really like the approach D has implemented
template constraints. Yeah I know, Andrei will destroy this :)

Macros
------
I haven't written more than extremely simple macros in Rust, but having
macros that is possible for tools to understand is a win. Templates and
string mixins is often used for this purpose, but trying to build tools
when string mixins exists is probably extremely hard. If D had hygenic
macros, I expect several features could be expressed with this instead
of string mixins, making tooling easier to implement.

I completely agree. String mixins are one of the most ugliest features.

Safe by default
---------------
D is often said being safe by default, but D still has default nullable
references and mutable by default. I don't see it being possible to
change at this stage, but expressing when I want to be unsafe rather
than the opposite is very nice. I end up typing a lot more in D than
Rust because of this.

Agree.

Pattern matching
----------------
Ooooh... I don't know what to say.. D should definitely look into
implementing some pattern matching! final switch is good for making sure
all values are handled, but deconstructing is just so ergonomic.

Yeah, pattern matching is soooo nice. I've been trying to implement
something similar in D as a library, something like this:

auto foo = Foo();
match!(foo
Foo, (int a, int b) => writeln(a, b);
);

Which kind of works for deconstructing variable pattern. But then you
want to have a pattern where "a" is 1, they it becomes much harder:

match!(foo
Foo, (value!(1), int b) => writeln(b);
);

Or

match!(foo
Foo, 1, (int b) => writeln(b);
);

But now you need to use different syntax for value pattern and variable
pattern and soon everything becomes a big mess.

It will never be as good as proper language support.

Expressions
-----------
This probably also falls in the "too late" category, but
statements-as-expressions is really nice. auto a = if ... <- why not?

I really like, I use it a lot in Scala. The with the combination of
automatically return the last thing in a method and methods not needing
curly braces for single expression methods is really nice:

def foo(a: String) =
if (a == "foo")
1
else if (a == "bar")
2
else
3

On the breaking part, the
real issue is the "We're not going to break any code!" stance, while
each release still breaks every codebase. The effect is that a lot of
really long-term necessary breaking changes is never accepted - the only
breaking changes is the unintended breaking changes!

Agree.

--
/Jacob Carlborg

Jul 22 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 7/22/2015 11:47 PM, Jacob Carlborg wrote:
On 2015-07-22 20:47, simendsjo wrote:

Traits
------
I think the ability to express an interface without buying into
inheritance is the right move. The alternative in D is specifying the
behavior as a template

I completely agree and don't really like the approach D has implemented
template
constraints. Yeah I know, Andrei will destroy this :)

Consider that template constraints can be arbitrarily complex, and can even
check behavior, not just a list of function signatures ANDed together. Turns
out
many constraints in Phobos are of the form (A || B), not just (A && B).

and verifying the contract in a unittest for the type.

I am a bit puzzled by the notion of shipping template code that has never been
instantiated as being a positive thing. This has also turned up in the C++
static_if discussions.

Jul 23 2015
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/23/15 5:07 AM, Walter Bright wrote:
On 7/22/2015 11:47 PM, Jacob Carlborg wrote:
On 2015-07-22 20:47, simendsjo wrote:

Traits
------
I think the ability to express an interface without buying into
inheritance is the right move. The alternative in D is specifying the
behavior as a template

I completely agree and don't really like the approach D has
implemented template
constraints. Yeah I know, Andrei will destroy this :)

Consider that template constraints can be arbitrarily complex, and can
even check behavior, not just a list of function signatures ANDed
together. Turns out many constraints in Phobos are of the form (A || B),
not just (A && B).

Agreed. And that's just scratching the surface.

Serious question: how do you express in Rust that a type implements one
trait or another, then figure out statically which?

>> and verifying the contract in a unittest for the type.

I am a bit puzzled by the notion of shipping template code that has
never been instantiated as being a positive thing. This has also turned
up in the C++ static_if discussions.

This is easy to understand. Weeding out uncovered code during
compilation is a central feature of C++ concepts. Admitting you actually
never want to do that would be a major blow.

Andrei

Jul 23 2015
"Dicebot" <public dicebot.lv> writes:
On Thursday, 23 July 2015 at 14:15:30 UTC, Andrei Alexandrescu
wrote:
On 7/23/15 5:07 AM, Walter Bright wrote:
On 7/22/2015 11:47 PM, Jacob Carlborg wrote:
On 2015-07-22 20:47, simendsjo wrote:

Traits
------
I think the ability to express an interface without buying
into
inheritance is the right move. The alternative in D is
specifying the
behavior as a template

I completely agree and don't really like the approach D has
implemented template
constraints. Yeah I know, Andrei will destroy this :)

Consider that template constraints can be arbitrarily complex,
and can
even check behavior, not just a list of function signatures
ANDed
together. Turns out many constraints in Phobos are of the form
(A || B),
not just (A && B).

Agreed. And that's just scratching the surface.

It is definitely a big issue for designing more advanced generic
libraries and one of my major issues with Rust but you need to
realize that vast majority of application domain usage of such
constraints is simply ensuring list of methods. You may be biased
by too much standard library development ;)

At the same time one HUGE deal breaker with rust traits that
rarely gets mentioned is the fact that they are both constraints
and interfaces at the same time:

// this is template constraint, it will generate new foo symbol
for each new T
fn foo <T : InputRange> (range : T)

// this use the very same trait definition but creates "fat
pointer" on demand with simplistic dispatch table
fn foo (range : InputRange)

It kills all the necessity for hacks like RangeObject and is
quite a salvation once you get to defining dynamic shared
libraries with stable ABI.

This is probably my most loved feature of Rust.

Serious question: how do you express in Rust that a type
implements one trait or another, then figure out statically
which?

As far as I understand current idiomatics, you don't. Code that
tries to use functions that are not ensured by trait will simply
not compile and for any complicated generic programming one is
supposed to use macros. Rust does not have templates, only
trait-restricted generics.

I find it terribly unproductive but it seems to appeal to certain
developer mindset, primarily the ones that associate "templates"
with "C++ templates".

Jul 23 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 7/23/2015 8:03 AM, Dicebot wrote:
At the same time one HUGE deal breaker with rust traits that rarely gets
mentioned is the fact that they are both constraints and interfaces at the same
time:

// this is template constraint, it will generate new foo symbol for each new
T
fn foo <T : InputRange> (range : T)

// this use the very same trait definition but creates "fat pointer" on demand
with simplistic dispatch table
fn foo (range : InputRange)

It kills all the necessity for hacks like RangeObject and is quite a salvation
once you get to defining dynamic shared libraries with stable ABI.

This is probably my most loved feature of Rust.

D interface types also produce the simplistic dispatch table, and if you make
them extern(C++) they don't need a RangeObject. I know it isn't as convenient
as
what you describe above, but it can be pressed into service.

Jul 23 2015
"Dicebot" <public dicebot.lv> writes:
On Thursday, 23 July 2015 at 19:55:30 UTC, Walter Bright wrote:
On 7/23/2015 8:03 AM, Dicebot wrote:
At the same time one HUGE deal breaker with rust traits that
rarely gets
mentioned is the fact that they are both constraints and
interfaces at the same
time:

// this is template constraint, it will generate new foo
symbol for each new T
fn foo <T : InputRange> (range : T)

// this use the very same trait definition but creates "fat
pointer" on demand
with simplistic dispatch table
fn foo (range : InputRange)

It kills all the necessity for hacks like RangeObject and is
quite a salvation
once you get to defining dynamic shared libraries with stable
ABI.

This is probably my most loved feature of Rust.

D interface types also produce the simplistic dispatch table,
and if you make them extern(C++) they don't need a RangeObject.
I know it isn't as convenient as what you describe above, but
it can be pressed into service.

I am not sure how it applies. My point was about the fact that
isInputRange and InputRangeObject are the same entities in
Rust, simply interpreted differently by compiler depending on
usage context.

This is important because you normally want to design your
application in terms of template constraints and structs to get
most out of inlining and optimization. However, to define stable
ABI for shared libraries, the very same interfaces need to be
wrapped in runtime polymorphism.

Closest thing in D would be to define traits as interfaces and
use code like this:

void foo(T)()
if (  (is(T == struct) || is(T == class))
&& Matches!(T, Interface)
)
{ }

where Matches is a template helper that statically iterates
method list of interface and looks for matching methods in T.
However, making it built-in feels really convenient in Rust:

- considerably less function declaration visual noise
- much better error messages: trying to use methods of T not
defined by a trait will result in compile-time error even without
instantiating the template

Jul 23 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 7/23/2015 1:08 PM, Dicebot wrote:
I am not sure how it applies.

D interfaces (defined with the 'interface' keyword) are simple dispatch types,
they don't require an Object. Such interfaces can also have default
implementations.

My point was about the fact that isInputRange
and InputRangeObject are the same entities in Rust, simply interpreted
differently by compiler depending on usage context.

I understand.

This is important because you normally want to design your application in terms
of template constraints and structs to get most out of inlining and
optimization. However, to define stable ABI for shared libraries, the very same
interfaces need to be wrapped in runtime polymorphism.

Closest thing in D would be to define traits as interfaces and use code like

this:
void foo(T)()
if (  (is(T == struct) || is(T == class))
&& Matches!(T, Interface)
)
{ }

where Matches is a template helper that statically iterates method list of
interface and looks for matching methods in T.

I don't think the test for struct and class is necessary. It can be just:

void foo(T)() if (Matches!(T, Interface)) { ... }

as opposed to:

void foo(T : Interface)() { ... }

However, making it built-in feels
really convenient in Rust:

- considerably less function declaration visual noise

It's less noise, sure, and perhaps we can do some syntactical sugar to improve
that. But I don't think this is a fundamental shortcoming for D as it stands
now
(although nobody has written a Matches template, perhaps that should be a
priority).

- much better error messages: trying to use methods of T not defined by a trait
will result in compile-time error even without instantiating the template

The error messages will occur at compile time and will be the same if you write
a unit test to instantiate the template. As I wrote earlier, I don't really
understand the need to ship template source code that has never been
instantiated.

Jul 23 2015
"Dicebot" <public dicebot.lv> writes:
On Thursday, 23 July 2015 at 20:52:46 UTC, Walter Bright wrote:
My point was about the fact that isInputRange
and InputRangeObject are the same entities in Rust, simply

interpreted
differently by compiler depending on usage context.

I understand.

Ok, sorry, it wasn't clear from the response context :)

I don't think the test for struct and class is necessary. It
can be just:

void foo(T)() if (Matches!(T, Interface)) { ... }

as opposed to:

void foo(T : Interface)() { ... }

Correct indeed, though I don't feel it is much of a difference
considering how common such code is (remember that you endorse
ranges as The Way for designing APIs!)

- much better error messages: trying to use methods of T not

defined by a trait
will result in compile-time error even without instantiating

the template

The error messages will occur at compile time and will be the
same if you write a unit test to instantiate the template. As I
wrote earlier, I don't really understand the need to ship
template source code that has never been instantiated.

1)

It does not protect from errors in definition

void foo (R) (Range r)
if (isInputRange!Range)
{ r.save(); }

unittest
{
SomeForwardRange r;
foo(r);
}

This will compile and show 100% test coverage. Yet when user will
try using it with real input range, it will fail.

2)

There is quite a notable difference in clarity between error
message coming from some arcane part of function body and
referring to wrong usage (or even totally misleading because of
UFCS) and simple and straightforward "Your type X does not
implement method X necessary for trait Y"

3)

Coverage does not work with conditional compilation:

void foo (T) ()
{
import std.stdio;
static if (is(T == int))
writeln("1");
else
writeln("2");
}

unittest
{
foo!int();
}

$dmd -cov=100 -unittest -main ./sample.d  Jul 23 2015 Walter Bright <newshound2 digitalmars.com> writes: On 7/23/2015 2:08 PM, Dicebot wrote: It does not protect from errors in definition void foo (R) (Range r) if (isInputRange!Range) { r.save(); } unittest { SomeForwardRange r; foo(r); } This will compile and show 100% test coverage. Yet when user will try using it with real input range, it will fail. That is correct. Some care must be taken that the mock types used in the unit tests actually match what the constraint is, rather than being a superset of them. There is quite a notable difference in clarity between error message coming from some arcane part of function body and referring to wrong usage (or even totally misleading because of UFCS) and simple and straightforward "Your type X does not implement method X necessary for trait Y" I believe they are the same. "method X does not exist for type Y". Coverage does not work with conditional compilation: void foo (T) () { import std.stdio; static if (is(T == int)) writeln("1"); else writeln("2"); } unittest { foo!int(); }$ dmd -cov=100 -unittest -main ./sample.d

Let's look at the actual coverage report:
===============================
|void foo (T) ()
|{
|    import std.stdio;
|    static if (is(T == int))
1|        writeln("1");
|    else
|        writeln("2");
|}
|
|unittest
|{
1|    foo!int();
|}
|
foo.d is 100% covered
============================

I look at these all the time. It's pretty obvious that the second writeln is
not
being compiled in.

Now, if I make a mistake in the second writeln such that it is syntactically
correct yet semantically wrong, and I ship it, and it blows up when the
customer
actually instantiates that line of code,

-- where is the advantage to me? --

How am I, the developer, better off? How does "well, it looks syntactically
like
D code, so ship it!" pass any sort of professional quality assurance?

Jul 23 2015
"Dicebot" <public dicebot.lv> writes:
Sorry for somewhat delayed answer - not sure if anyone has

On Friday, 24 July 2015 at 00:19:50 UTC, Walter Bright wrote:
On 7/23/2015 2:08 PM, Dicebot wrote:
It does not protect from errors in definition

void foo (R) (Range r)
if (isInputRange!Range)
{ r.save(); }

unittest
{
SomeForwardRange r;
foo(r);
}

This will compile and show 100% test coverage. Yet when user
will try using it
with real input range, it will fail.

That is correct. Some care must be taken that the mock types
used in the unit tests actually match what the constraint is,
rather than being a superset of them.

This is absolutely impractical. I will never even consider such
attitude as a solution for production projects. If test coverage
can't be verified automatically, it is garbage, period. No one
will ever manually verify thousands lines of code after some
trivial refactoring just to make sure compiler does its job.

By your attitude -cov is not necessary at all - you can do the
same manually anyway, with some help of 3d party tool. Yet you
advertise it as crucial D feature (and are being totally right

There is quite a notable difference in clarity between error
message coming from
some arcane part of function body and referring to wrong usage
(or even totally
misleading because of UFCS) and simple and straightforward
implement method X necessary for trait Y"

I believe they are the same. "method X does not exist for type
Y".

Well, the difference is that you "believe" and I actually write
code and read those error messages. They are not the same at all.
In D error message gets evaluated in context of function body and
is likely to be completely misleading in all but most trivial
methods. For example, if there is a global UFCS function
available with the same name but different argument list, you
methods.

Coverage does not work with conditional compilation:

void foo (T) ()
{
import std.stdio;
static if (is(T == int))
writeln("1");
else
writeln("2");
}

unittest
{
foo!int();
}

$dmd -cov=100 -unittest -main ./sample.d Let's look at the actual coverage report: =============================== |void foo (T) () |{ | import std.stdio; | static if (is(T == int)) 1| writeln("1"); | else | writeln("2"); |} | |unittest |{ 1| foo!int(); |} | foo.d is 100% covered ============================ I look at these all the time. It's pretty obvious that the second writeln is not being compiled in. Again, this is impractical. You may be capable of reading with speed of light but this not the normal industry case. Programs are huge, changesets are big, time pressure is real. If something can't be verified in automated way at least for basic sanity, it is simply not good enough. This is the whole point of CI revolution. In practice I will only look into .cov files when working on adding new tests to improve the coverage and will never be able to do it more often (unless compiler notifies me to do so). This is real-world constraint one needs to deal with, not matter what your personal preferences about good development process are. Now, if I make a mistake in the second writeln such that it is syntactically correct yet semantically wrong, and I ship it, and it blows up when the customer actually instantiates that line of code, -- where is the advantage to me? -- How am I, the developer, better off? How does "well, it looks syntactically like D code, so ship it!" pass any sort of professional quality assurance? ? If compiler would actually show 0 coverage for non-instantiated lines, than automatic coverage control check in CI would complain and code would never be shipped unless it gets covered with tests (which check the semantics). Your are putting it totally backwards.  Jul 25 2015 Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes: On 7/25/15 9:35 AM, Dicebot wrote: This is absolutely impractical. I will never even consider such attitude as a solution for production projects. If test coverage can't be verified automatically, it is garbage, period. No one will ever manually verify thousands lines of code after some trivial refactoring just to make sure compiler does its job. Test coverage shouldn't totter up and down as application code is written - it should be established by the unittests. And yes one does need to examine coverage output while writing unittests. I do agree more automation is better here (as is always). For example, if a template is followed by one or more unittests, the compiler might issue an error if the unittests don't cover the template. Andrei  Jul 25 2015 "Dicebot" <public dicebot.lv> writes: On Saturday, 25 July 2015 at 14:28:31 UTC, Andrei Alexandrescu wrote: On 7/25/15 9:35 AM, Dicebot wrote: This is absolutely impractical. I will never even consider such attitude as a solution for production projects. If test coverage can't be verified automatically, it is garbage, period. No one will ever manually verify thousands lines of code after some trivial refactoring just to make sure compiler does its job. Test coverage shouldn't totter up and down as application code is written - it should be established by the unittests. And yes one does need to examine coverage output while writing unittests. Does word "refactoring" or "adding new features" ring a bell? In the first case no one manually checks coverage of all affected code because simply too much code is affected. Yet it can become reduced by an accident. In the second case developer is likely to check coverage for actual functionality he has written - and yet coverage can become reduced in different (but related) parts of code because that is how templates work. You will have a very hard time selling this approach. If official position of language authors is that one must manually check test coverage all the time over and over again, pragmatical people will look into other languages. I do agree more automation is better here (as is always). For example, if a template is followed by one or more unittests, the compiler might issue an error if the unittests don't cover the template. This isn't "better". This is bare minimum for me to call that functionality effectively testable. Manual approach to testing doesn't work, I thought everyone has figured that out by 2015. It works better than no tests at all, sure, but this is not considered enough anymore.  Jul 25 2015 Walter Bright <newshound2 digitalmars.com> writes: On 7/25/2015 6:35 AM, Dicebot wrote: If compiler would actually show 0 coverage for non-instantiated lines, than automatic coverage control check in CI would complain and code would never be shipped unless it gets covered with tests (which check the semantics). Your are putting it totally backwards. A good case. https://issues.dlang.org/show_bug.cgi?id=14825  Jul 25 2015 "Dicebot" <public dicebot.lv> writes: On Saturday, 25 July 2015 at 21:20:28 UTC, Walter Bright wrote: On 7/25/2015 6:35 AM, Dicebot wrote: If compiler would actually show 0 coverage for non-instantiated lines, than automatic coverage control check in CI would complain and code would never be shipped unless it gets covered with tests (which check the semantics). Your are putting it totally backwards. A good case. https://issues.dlang.org/show_bug.cgi?id=14825 Thanks!  Jul 26 2015 Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes: On 7/23/15 4:52 PM, Walter Bright wrote: On 7/23/2015 1:08 PM, Dicebot wrote: > I am not sure how it applies. D interfaces (defined with the 'interface' keyword) are simple dispatch types, they don't require an Object. Such interfaces can also have default implementations. Is this new? I agree we should allow it, but I don't think it was added to the language yet. Andrei  Jul 25 2015 "deadalnix" <deadalnix gmail.com> writes: On Saturday, 25 July 2015 at 12:15:04 UTC, Andrei Alexandrescu wrote: On 7/23/15 4:52 PM, Walter Bright wrote: On 7/23/2015 1:08 PM, Dicebot wrote: > I am not sure how it applies. D interfaces (defined with the 'interface' keyword) are simple dispatch types, they don't require an Object. Such interfaces can also have default implementations. Is this new? I agree we should allow it, but I don't think it was added to the language yet. Andrei This is not in the language and should not be added lightly. There is all kind of collisions that could happen, and they need proper disambiguate rules. Scala trait (different beast from Rust traits) is a successful implementation of this.  Jul 25 2015 Timon Gehr <timon.gehr gmx.ch> writes: On 07/25/2015 02:15 PM, Andrei Alexandrescu wrote: On 7/23/15 4:52 PM, Walter Bright wrote: On 7/23/2015 1:08 PM, Dicebot wrote: > I am not sure how it applies. D interfaces (defined with the 'interface' keyword) are simple dispatch types, they don't require an Object. Such interfaces can also have default implementations. Is this new? I agree we should allow it, but I don't think it was added to the language yet. Andrei In any case, the painful thing about the RangeObject interfaces is that they transform value types into reference types.  Jul 26 2015 Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes: On 7/23/15 4:08 PM, Dicebot wrote: On Thursday, 23 July 2015 at 19:55:30 UTC, Walter Bright wrote: On 7/23/2015 8:03 AM, Dicebot wrote: At the same time one HUGE deal breaker with rust traits that rarely gets mentioned is the fact that they are both constraints and interfaces at the same time: // this is template constraint, it will generate new foo symbol for each new T fn foo <T : InputRange> (range : T) // this use the very same trait definition but creates "fat pointer" on demand with simplistic dispatch table fn foo (range : InputRange) It kills all the necessity for hacks like RangeObject and is quite a salvation once you get to defining dynamic shared libraries with stable ABI. This is probably my most loved feature of Rust. D interface types also produce the simplistic dispatch table, and if you make them extern(C++) they don't need a RangeObject. I know it isn't as convenient as what you describe above, but it can be pressed into service. I am not sure how it applies. My point was about the fact that isInputRange and InputRangeObject are the same entities in Rust, simply interpreted differently by compiler depending on usage context. This is important because you normally want to design your application in terms of template constraints and structs to get most out of inlining and optimization. However, to define stable ABI for shared libraries, the very same interfaces need to be wrapped in runtime polymorphism. Closest thing in D would be to define traits as interfaces and use code like this: void foo(T)() if ( (is(T == struct) || is(T == class)) && Matches!(T, Interface) ) { } where Matches is a template helper that statically iterates method list of interface and looks for matching methods in T. I could have sworn we have implementsInterface in std.traits. However, making it built-in feels really convenient in Rust: - considerably less function declaration visual noise - much better error messages: trying to use methods of T not defined by a trait will result in compile-time error even without instantiating the template Yah, building stuff in does have its advantages. Andrei  Jul 25 2015 "Dicebot" <public dicebot.lv> writes: On Saturday, 25 July 2015 at 12:09:34 UTC, Andrei Alexandrescu wrote: However, making it built-in feels really convenient in Rust: - considerably less function declaration visual noise - much better error messages: trying to use methods of T not defined by a trait will result in compile-time error even without instantiating the template Yah, building stuff in does have its advantages. I feel it is not as much about "built-in vs library", but "generic vs templates" - somewhat deeper ideological difference that consequently calls for different tools. Metaprogramming with traits in Rust is inconvenient to the point of being almost impossible but generics have a very strong static API verification. In D you can get destroyed by flow of deeply nested error messages but the magic you can do with templates with minimal effort investment is beyond comparison. Different values, different trade-offs.  Jul 25 2015 "Enamex" <enamex+d outlook.com> writes: On Thursday, 23 July 2015 at 15:03:56 UTC, Dicebot wrote: At the same time one HUGE deal breaker with rust traits that rarely gets mentioned is the fact that they are both constraints and interfaces at the same time: ... It kills all the necessity for hacks like RangeObject and is quite a salvation once you get to defining dynamic shared libraries with stable ABI. This is probably my most loved feature of Rust. Sorry, I don't quite get this. How is the most loved feature of Rust (that interfaces are also constraints for generics), a *deal breaker*?  Jul 25 2015 "Dicebot" <public dicebot.lv> writes: On Sunday, 26 July 2015 at 01:33:09 UTC, Enamex wrote: On Thursday, 23 July 2015 at 15:03:56 UTC, Dicebot wrote: At the same time one HUGE deal breaker with rust traits that rarely gets mentioned is the fact that they are both constraints and interfaces at the same time: ... It kills all the necessity for hacks like RangeObject and is quite a salvation once you get to defining dynamic shared libraries with stable ABI. This is probably my most loved feature of Rust. Sorry, I don't quite get this. How is the most loved feature of Rust (that interfaces are also constraints for generics), a *deal breaker*? I have just checked the dictionary and it simply a matter of being having terrible English and using this phrase wrong all the time :) It was supposed to mean something like "feature that makes crucial (positive) difference"  Jul 26 2015 Walter Bright <newshound2 digitalmars.com> writes: On 7/23/2015 7:15 AM, Andrei Alexandrescu wrote: I am a bit puzzled by the notion of shipping template code that has never been instantiated as being a positive thing. This has also turned up in the C++ static_if discussions. This is easy to understand. Weeding out uncovered code during compilation is a central feature of C++ concepts. Admitting you actually never want to do that would be a major blow. But if a unit test fails at instantiating it, it fails at compile time.  Jul 23 2015 "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes: On Thu, Jul 23, 2015 at 12:49:29PM -0700, Walter Bright via Digitalmars-d wrote: On 7/23/2015 7:15 AM, Andrei Alexandrescu wrote: I am a bit puzzled by the notion of shipping template code that has never been instantiated as being a positive thing. This has also turned up in the C++ static_if discussions. This is easy to understand. Weeding out uncovered code during compilation is a central feature of C++ concepts. Admitting you actually never want to do that would be a major blow. But if a unit test fails at instantiating it, it fails at compile time. That assumes the template author is diligent (foolhardy?) enough to write unittests that cover all possible instantiations... T -- People say I'm indecisive, but I'm not sure about that. -- YHL, CONLANG  Jul 23 2015 Walter Bright <newshound2 digitalmars.com> writes: On 7/23/2015 12:50 PM, H. S. Teoh via Digitalmars-d wrote: That assumes the template author is diligent (foolhardy?) enough to write unittests that cover all possible instantiations... No, only each branch of the template code must be instantiated, not every possible instantiation. And we have a tool to help with that: -cov Does anyone believe it is a good practice to ship template code that has never been instantiated?  Jul 23 2015 "Vlad Levenfeld" <vlevenfeld gmail.com> writes: On Thursday, 23 July 2015 at 20:40:17 UTC, Walter Bright wrote: On 7/23/2015 12:50 PM, H. S. Teoh via Digitalmars-d wrote: That assumes the template author is diligent (foolhardy?) enough to write unittests that cover all possible instantiations... No, only each branch of the template code must be instantiated, not every possible instantiation. And we have a tool to help with that: -cov Does anyone believe it is a good practice to ship template code that has never been instantiated? I dunno about good practices but I have some use cases. I write a bunch of zero-parameter template methods and then pass them into a Match template which attempts to instantiate each of them in turn, settling on the first one which does compile. So the methods basically form a list of "preferred implementation of functionality X". All but one winds up uninstantiated. I also use a pattern where I mix in a zero-parameter template methods into a struct - they don't necessarily work for that struct, but they won't stop compilation unless they are instantiated. A complete interface is generated but only the subset which the context actually supports can be successfully instantiated - and anything the caller doesn't need, doesn't get compiled. Again, not sure if this is a bad or good thing. But I have found these patterns useful.  Jul 23 2015 "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes: On Thu, Jul 23, 2015 at 08:50:55PM +0000, Vlad Levenfeld via Digitalmars-d wrote: On Thursday, 23 July 2015 at 20:40:17 UTC, Walter Bright wrote: On 7/23/2015 12:50 PM, H. S. Teoh via Digitalmars-d wrote: That assumes the template author is diligent (foolhardy?) enough to write unittests that cover all possible instantiations... No, only each branch of the template code must be instantiated, not every possible instantiation. And we have a tool to help with that: -cov Does anyone believe it is a good practice to ship template code that has never been instantiated? I dunno about good practices but I have some use cases. I write a bunch of zero-parameter template methods and then pass them into a Match template which attempts to instantiate each of them in turn, settling on the first one which does compile. So the methods basically form a list of "preferred implementation of functionality X". All but one winds up uninstantiated. [...] But don't you still have to test each template, to make sure they compile when they're supposed to? T -- Without geometry, life would be pointless. -- VS  Jul 23 2015 "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes: On Thu, Jul 23, 2015 at 01:40:17PM -0700, Walter Bright via Digitalmars-d wrote: On 7/23/2015 12:50 PM, H. S. Teoh via Digitalmars-d wrote: That assumes the template author is diligent (foolhardy?) enough to write unittests that cover all possible instantiations... No, only each branch of the template code must be instantiated, not every possible instantiation. And we have a tool to help with that: -cov Does anyone believe it is a good practice to ship template code that has never been instantiated? OK, I jumped into the middle of this discussion so probably I'm speaking totally out of context... but anyway, with regards to template code, I agree that it ought to be thoroughly tested by at least instantiating the most typical use cases (as well as some not-so-typical use cases). An uninstantiated template path is worse than a branch that's never taken, because the compiler can't help you find obvious problems before you ship it to the customer. A lot of Phobos bugs lurk in rarely-used template branches that are not covered by the unittests. Instantiating all branches is only part of the solution, though. A lot of Phobos bugs also arise from undetected dependencies of the template code on the specifics of the concrete types used to test it in the unittests. The template passes the unittest but when you instantiate it with a type not used in the unittests, it breaks. For instance, a lot of range-based templates are tested with arrays in the unittests. Some of these templates wrongly depend on array behaviour (as opposed to being confined only to range API operations) while their signature constraints indicate only the generic range API. As a result, when non-array ranges are used, it breaks. Sometimes bugs like this can lurk undetected for a long time before somebody one day happens to instantiate it with a range type that violates the hidden assumption in the template code. If we had a Concepts-like construct in D, where template code is statically constrained to only use, e.g., range API when manipulating an incoming type, a lot of these bugs would've been caught. In fact, I'd argue that this should be done for *all* templates -- for example, a function like this ought to be statically rejected: auto myFunc(T)(T t) { return t + 1; } because it assumes the validity of the + operation on T, but T is not constrained in any way, so it can be *any* type, most of which, arguably, do not support the + operation. Instead, templates ought to be required to explicitly declare up-front all operations that it will perform on incoming types, so that (1) its assumptions are obvious, and (2) the compiler will reject attempts to instantiate it with an incompatible type. auto myFunc(T)(T t) if (is(typeof(T.init + 1))) { return t + 1; } The current syntax is ugly, of course, but that's easily remedied. The more fundamental problem is that the compiler does not restrict operations on T in any way, even when the sig constraint specifies how T ought to be used. Someone could easily introduce a bug: auto myFunc(T)(T t) if (is(typeof(T.init + 1))) { /* Oops, we checked that +1 is a valid operation on T, * but here we're doing -1 instead, which may or may not * be valid: */ return t - 1; } The compiler still accepts this code as long as the unittests use types that support both + and -. So this dependency on the incidental characteristics of T remains as a latent bug. If the compiler outright rejected any operation on T that hasn't been explicitly tested for, *then* we will have eliminated a whole class of template bugs. Wrong code like the last example above would be caught as soon as the compiler compiles the body of myFunc. T -- Elegant or ugly code as well as fine or rude sentences have something in common: they don't depend on the language. -- Luca De Vitis  Jul 23 2015 "Dicebot" <public dicebot.lv> writes: On Thursday, 23 July 2015 at 22:10:11 UTC, H. S. Teoh wrote: OK, I jumped into the middle of this discussion so probably I'm speaking totally out of context... This is exactly one major advantage of Rust traits I have been trying to explain, thanks for putting it up in much more understandable way :)  Jul 23 2015 Walter Bright <newshound2 digitalmars.com> writes: On 7/23/2015 3:12 PM, Dicebot wrote: On Thursday, 23 July 2015 at 22:10:11 UTC, H. S. Teoh wrote: OK, I jumped into the middle of this discussion so probably I'm speaking totally out of context... This is exactly one major advantage of Rust traits I have been trying to explain, thanks for putting it up in much more understandable way :) Consider the following: int foo(T: hasPrefix)(T t) { t.prefix(); // ok bar(t); // error, hasColor was not specified for T } void bar(T: hasColor)(T t) { t.color(); } Now consider a deeply nested chain of function calls like this. At the bottom, one adds a call to 'color', and now every function in the chain has to add 'hasColor' even though it has nothing to do with the logic in that function. This is the pit that Exception Specifications fell into. I can see these possibilities: 1. Require adding the constraint annotations all the way up the call tree. I believe that this will not only become highly annoying, it might make generic code impractical to write (consider if bar was passed as an alias). 2. Do the checking only for 1 level, i.e. don't consider what bar() requires. This winds up just pulling the teeth of the point of the constraint annotations. 3. Do inference of the constraints. I think that is indistinguishable from not having annotations as being exclusive. Anyone know how Rust traits and C++ concepts deal with this?  Jul 23 2015 =?UTF-8?Q?Tobias=20M=C3=BCller?= <troplin bluewin.ch> writes: Walter Bright <newshound2 digitalmars.com> wrote: On 7/23/2015 3:12 PM, Dicebot wrote: On Thursday, 23 July 2015 at 22:10:11 UTC, H. S. Teoh wrote: OK, I jumped into the middle of this discussion so probably I'm speaking totally out of context... This is exactly one major advantage of Rust traits I have been trying to explain, thanks for putting it up in much more understandable way :) Consider the following: int foo(T: hasPrefix)(T t) { t.prefix(); // ok bar(t); // error, hasColor was not specified for T } void bar(T: hasColor)(T t) { t.color(); } Now consider a deeply nested chain of function calls like this. At the bottom, one adds a call to 'color', and now every function in the chain has to add 'hasColor' even though it has nothing to do with the logic in that function. This is the pit that Exception Specifications fell into. I can see these possibilities: 1. Require adding the constraint annotations all the way up the call tree. I believe that this will not only become highly annoying, it might make generic code impractical to write (consider if bar was passed as an alias). 2. Do the checking only for 1 level, i.e. don't consider what bar() requires. This winds up just pulling the teeth of the point of the constraint annotations. 3. Do inference of the constraints. I think that is indistinguishable from not having annotations as being exclusive. Anyone know how Rust traits and C++ concepts deal with this? You may aus well ask "How do interfaces in OO programming deal with this?". Frankly, I've never had an issue with that. Or it's a hint for design problems. Traits (and interfaces) are mostly not that fine grained, i.e. you don't have a trait/interface for every method. They should ideally define an abstraction/entity with a semantic meaning. If your constraint "hasColor(x)" just means "x has method color()", and then implement it for every class that has this method, you can just as well omit constraints and use duck typing. Tobi  Jul 23 2015 "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes: On Fri, Jul 24, 2015 at 05:39:35AM +0000, Tobias Mller via Digitalmars-d wrote: Walter Bright <newshound2 digitalmars.com> wrote: On 7/23/2015 3:12 PM, Dicebot wrote: On Thursday, 23 July 2015 at 22:10:11 UTC, H. S. Teoh wrote: OK, I jumped into the middle of this discussion so probably I'm speaking totally out of context... This is exactly one major advantage of Rust traits I have been trying to explain, thanks for putting it up in much more understandable way :) Consider the following: int foo(T: hasPrefix)(T t) { t.prefix(); // ok bar(t); // error, hasColor was not specified for T } void bar(T: hasColor)(T t) { t.color(); } Now consider a deeply nested chain of function calls like this. At the bottom, one adds a call to 'color', and now every function in the chain has to add 'hasColor' even though it has nothing to do with the logic in that function. This is the pit that Exception Specifications fell into. I can see these possibilities: 1. Require adding the constraint annotations all the way up the call tree. I believe that this will not only become highly annoying, it might make generic code impractical to write (consider if bar was passed as an alias). 2. Do the checking only for 1 level, i.e. don't consider what bar() requires. This winds up just pulling the teeth of the point of the constraint annotations. 3. Do inference of the constraints. I think that is indistinguishable from not having annotations as being exclusive. Well, this is where the whole idea of Concepts comes from. Rather than specify the nitty-gritty of exactly which operations the type must support, you introduce an abstraction called a Concept, which basically is a group of related traits that a type must satisfy. You can think of it as a prototypical "type" that supports all the required operations. For example, an input range would be a Concept that supports the operations .front, .empty, and .popFront. A forward range would be a larger Concept derived from the input range Concept, that adds the operation .save. Your range functions don't have to specify explicitly that "type T must have methods called .empty, .front, .popFront", they simply say "type T must conform to the InputRange Concept". // Hypothetical syntax concept InputRange(ElementType) { bool empty; ElementType front(); void popFront(); } void myRangeFunc(R : InputRange)(R range) { // freely use .empty, .front, .popFront here } This also solves your objection in the other post that specifying constraints will become too onerous because you have to keep listing every individual operation the function needs, and changing a function far down the call chain will bubble up and require updating all functions that call it. With Concepts, you don't have to do this, because any change can be done in the definition of the Concept itself. If not, the function that requires more than what the current Concept provides actually needs a larger Concept that it's asking for, in which case all its callers *need* to be updated anyway. It's no different from deciding that a function that used to take struct S1 now needs to take struct S2 instead -- there's no way to avoid having to update all code that calls the function so that they pass in the new type. Concepts can derive from other Concepts too, so a ForwardRange concept need not repeat the traits specified by the InputRange concept; it can simply specify that a forward range supports all InputRange traits, plus the .save method. If you like, think of Concepts as compile-time interface definitions. Anyone know how Rust traits and C++ concepts deal with this? You may aus well ask "How do interfaces in OO programming deal with this?". Frankly, I've never had an issue with that. Or it's a hint for design problems. Traits (and interfaces) are mostly not that fine grained, i.e. you don't have a trait/interface for every method. They should ideally define an abstraction/entity with a semantic meaning. If your constraint "hasColor(x)" just means "x has method color()", and then implement it for every class that has this method, you can just as well omit constraints and use duck typing. [...] Yes, the value of Concepts mostly comes from the, um, concepts that group together a set of traits that characterize a particular category of types. Like input range, forward range, or output range. It's of more limited utility for testing traits individually. You're not really thinking in terms of individual traits, at least not directly, when you're using Concepts; you're thinking in terms of the conceptual abstraction that the Concept represents. The compiler does the checking of individual traits for you. T -- The richest man is not he who has the most, but he who needs the least.  Jul 23 2015 Walter Bright <newshound2 digitalmars.com> writes: On 7/23/2015 11:05 PM, H. S. Teoh via Digitalmars-d wrote: Yes, the value of Concepts mostly comes from the, um, concepts that group together a set of traits that characterize a particular category of types. Like input range, forward range, or output range. It's of more limited utility for testing traits individually. You're not really thinking in terms of individual traits, at least not directly, when you're using Concepts; you're thinking in terms of the conceptual abstraction that the Concept represents. The compiler does the checking of individual traits for you. That's true but it changes nothing about what I wrote. Just replace "hasPrefix" with "hasInterfaceA". The points I brought up remain.  Jul 23 2015 Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes: On 7/24/15 2:05 AM, H. S. Teoh via Digitalmars-d wrote: Well, this is where the whole idea of Concepts comes from. Rather than specify the nitty-gritty of exactly which operations the type must support, you introduce an abstraction called a Concept, which basically is a group of related traits that a type must satisfy. You can think of it as a prototypical "type" that supports all the required operations. For example, an input range would be a Concept that supports the operations .front, .empty, and .popFront. A forward range would be a larger Concept derived from the input range Concept, that adds the operation .save. Your range functions don't have to specify explicitly that "type T must have methods called .empty, .front, .popFront", they simply say "type T must conform to the InputRange Concept". As I argued in "Generic Programming Must Go", this does work on scarce-vocabulary domain. The moment you start talking about ranges that may or may not support cross-cutting primitives, it all comes unglued. -- Andrei  Jul 25 2015 Walter Bright <newshound2 digitalmars.com> writes: On 7/23/2015 10:39 PM, Tobias Müller wrote: You may aus well ask "How do interfaces in OO programming deal with this?". It's a good question. And the answer is, the top level function does not list every interface used by the call tree. Nested function calls test at runtime if a particular interface is supported by an object, using dynamic casting or QueryInterface() calls. It's fundamentally different from traits and concepts.  Jul 23 2015 Jacob Carlborg <doob me.com> writes: On 2015-07-24 08:42, Walter Bright wrote: It's a good question. And the answer is, the top level function does not list every interface used by the call tree. Nested function calls test at runtime if a particular interface is supported by an object, using dynamic casting or QueryInterface() calls. It's fundamentally different from traits and concepts. If you have an interface and then doing a dynamic cast then you're doing it wrong. Yes, I know that there are code that uses this, yes I have done that too. -- /Jacob Carlborg  Jul 24 2015 Walter Bright <newshound2 digitalmars.com> writes: On 7/24/2015 11:35 AM, Jacob Carlborg wrote: On 2015-07-24 08:42, Walter Bright wrote: It's a good question. And the answer is, the top level function does not list every interface used by the call tree. Nested function calls test at runtime if a particular interface is supported by an object, using dynamic casting or QueryInterface() calls. It's fundamentally different from traits and concepts. If you have an interface and then doing a dynamic cast then you're doing it wrong. Yes, I know that there are code that uses this, yes I have done that too. Dynamic cast is no different from QueryInterface(), which is how it's done, and the reason is the point of all this - avoiding needing to enumerate every interface needed by the leaves at the root of the call tree.  Jul 24 2015 Jacob Carlborg <doob me.com> writes: On 2015-07-24 21:04, Walter Bright wrote: Dynamic cast is no different from QueryInterface(), which is how it's done, and the reason is the point of all this - avoiding needing to enumerate every interface needed by the leaves at the root of the call tree. I'm not familiar with QueryInterface(): -- /Jacob Carlborg  Jul 25 2015 "Jonathan M Davis" <jmdavisProg gmx.com> writes: On Saturday, 25 July 2015 at 09:44:30 UTC, Jacob Carlborg wrote: On 2015-07-24 21:04, Walter Bright wrote: Dynamic cast is no different from QueryInterface(), which is how it's done, and the reason is the point of all this - avoiding needing to enumerate every interface needed by the leaves at the root of the call tree. I'm not familiar with QueryInterface(): It's part of COM. Basically, it's equivalent to doing something like auto f = cast(MyInterface)myObj; if(f is null) { /+ you can't convert it to MyInterface +/ } else { /+ you _can_ convert it +/ } except that with the way that QueryInterface works, it's actually possible to get a completely different object out of it. It doesn't have to have any relation to the original object, and the new type doesn't have to be in an inheritance hierarchy with the original type. So, you can do wacky stuff like have an interface which relates to one object hierarchy and convert it to an interface from a completely unrelated object hierarchy just so long as the underlying type knows how to give you the type you're asking for (be it because it implements both interfaces or because it's designed to give you an object that implements the interface you're asking for). So, QueryInterface isn't actually guaranteed to do anything like a cast at all. It just guarantees that you'll either get an object of the type you ask for - somehow - or that the call will fail. And the way it's used often seems to be the equivalent of something like interface A {} interface A : B {} interface Foo {} interface Bar : Foo {} class MyClass : B, Bar {} Foo f = getFoo(); // returns what it is actually a MyClass A a = cast(A)f; It happens to work, because the underlying type implements both, but why on earth would you expect that a Foo would be related to an A when they're completely unrelated? And yet that seems to be the sort of thing that gets done with QueryInterface - at least in the code that I've worked with that does COM. Personally, I think that it's nuts to even be attempting to cast from one interface to an entirely unrelated one and expect it to work - or to write code in a way that that's a normal way to do things. - Jonathan M Davis  Jul 25 2015 "Paulo Pinto" <pjmlp progtools.org> writes: On Saturday, 25 July 2015 at 10:12:15 UTC, Jonathan M Davis wrote: On Saturday, 25 July 2015 at 09:44:30 UTC, Jacob Carlborg wrote: On 2015-07-24 21:04, Walter Bright wrote: Dynamic cast is no different from QueryInterface(), which is how it's done, and the reason is the point of all this - avoiding needing to enumerate every interface needed by the leaves at the root of the call tree. I'm not familiar with QueryInterface(): It's part of COM. Basically, it's equivalent to doing something like auto f = cast(MyInterface)myObj; if(f is null) { /+ you can't convert it to MyInterface +/ } else { /+ you _can_ convert it +/ } except that with the way that QueryInterface works, it's actually possible to get a completely different object out of it. It doesn't have to have any relation to the original object, and the new type doesn't have to be in an inheritance hierarchy with the original type. So, you can do wacky stuff like have an interface which relates to one object hierarchy and convert it to an interface from a completely unrelated object hierarchy just so long as the underlying type knows how to give you the type you're asking for (be it because it implements both interfaces or because it's designed to give you an object that implements the interface you're asking for). So, QueryInterface isn't actually guaranteed to do anything like a cast at all. It just guarantees that you'll either get an object of the type you ask for - somehow - or that the call will fail. And the way it's used often seems to be the equivalent of something like interface A {} interface A : B {} interface Foo {} interface Bar : Foo {} class MyClass : B, Bar {} Foo f = getFoo(); // returns what it is actually a MyClass A a = cast(A)f; It happens to work, because the underlying type implements both, but why on earth would you expect that a Foo would be related to an A when they're completely unrelated? And yet that seems to be the sort of thing that gets done with QueryInterface - at least in the code that I've worked with that does COM. Personally, I think that it's nuts to even be attempting to cast from one interface to an entirely unrelated one and expect it to work - or to write code in a way that that's a normal way to do things. - Jonathan M Davis It makes sense when one thinks about pure interfaces and component oriented programming, as preached by one of the Component Pascal guys. Or for that matter how Go uses interfaces. The problem is when people try to model classical OOP on top of COM. This is one reason why on WinRT all classes implementing COM are required to be final.  Jul 25 2015 "deadalnix" <deadalnix gmail.com> writes: On Friday, 24 July 2015 at 06:42:47 UTC, Walter Bright wrote: On 7/23/2015 10:39 PM, Tobias Müller wrote: You may aus well ask "How do interfaces in OO programming deal with this?". It's a good question. And the answer is, the top level function does not list every interface used by the call tree. Nested function calls test at runtime if a particular interface is supported by an object, using dynamic casting or QueryInterface() calls. It's fundamentally different from traits and concepts. It is not required and probably shouldn't, or at least shouldn't in many cases. This problem is the exact same one as strongly typed vs dynamically typed languages, except at compile time. And solution are the same: interface (concepts) or duck typing and hope for the best. The end goal is pretty much the same: writing reusable code. The implementation differs, performances as well, but that is pretty much it.  Jul 24 2015 =?UTF-8?Q?Tobias=20M=C3=BCller?= <troplin bluewin.ch> writes: Walter Bright <newshound2 digitalmars.com> wrote: On 7/23/2015 10:39 PM, Tobias Müller wrote: You may aus well ask "How do interfaces in OO programming deal with this?". It's a good question. And the answer is, the top level function does not list every interface used by the call tree. Nested function calls test at runtime if a particular interface is supported by an object, using dynamic casting or QueryInterface() calls. It's fundamentally different from traits and concepts. IMO dynamic casting or QueryInterface() is a sign of bad design. But then again, I also like exception specifications, at least the way Java does it. In C++ they're pointless, that true. Tobi  Jul 25 2015 "vitus" <vitus vitus.vitus> writes: On Friday, 24 July 2015 at 04:42:59 UTC, Walter Bright wrote: On 7/23/2015 3:12 PM, Dicebot wrote: On Thursday, 23 July 2015 at 22:10:11 UTC, H. S. Teoh wrote: OK, I jumped into the middle of this discussion so probably I'm speaking totally out of context... This is exactly one major advantage of Rust traits I have been trying to explain, thanks for putting it up in much more understandable way :) Consider the following: int foo(T: hasPrefix)(T t) { t.prefix(); // ok bar(t); // error, hasColor was not specified for T } void bar(T: hasColor)(T t) { t.color(); } Now consider a deeply nested chain of function calls like this. At the bottom, one adds a call to 'color', and now every function in the chain has to add 'hasColor' even though it has nothing to do with the logic in that function. This is the pit that Exception Specifications fell into. I can see these possibilities: 1. Require adding the constraint annotations all the way up the call tree. I believe that this will not only become highly annoying, it might make generic code impractical to write (consider if bar was passed as an alias). 2. Do the checking only for 1 level, i.e. don't consider what bar() requires. This winds up just pulling the teeth of the point of the constraint annotations. 3. Do inference of the constraints. I think that is indistinguishable from not having annotations as being exclusive. Fun begins: void foo(T)(T t) if(hasPrefix!T || hasSuffix!T){ static if(...)t.prefix(); else t.suffix(); mixin("t.bar();"); }  Jul 23 2015 Artur Skawina via Digitalmars-d <digitalmars-d puremagic.com> writes: On 07/24/15 06:43, Walter Bright via Digitalmars-d wrote: On 7/23/2015 3:12 PM, Dicebot wrote: On Thursday, 23 July 2015 at 22:10:11 UTC, H. S. Teoh wrote: OK, I jumped into the middle of this discussion so probably I'm speaking totally out of context... This is exactly one major advantage of Rust traits I have been trying to explain, thanks for putting it up in much more understandable way :) Consider the following: int foo(T: hasPrefix)(T t) { t.prefix(); // ok bar(t); // error, hasColor was not specified for T The fact that some other concept/trait implementations got it wrong is not really an argument against a sane implementation. Basically, it can work like this: - traits, like your 'hasPrefix', check that 'T' implements an interface (defined in hasPrefix). (this does the job that template constraints do right now, and more [1]) - the compiler instantiates the template with a mock (defined inside 'hasPrefix'). (this immediately catches every illegal access, like 't.suffix' and results in a clear and informative error message) - the 'T' inside foo is still the original type that it was called with, so 'bar(t)' will succeed. But it needs to be conditionally enabled for just the types that implement 'hasColor' -- and this is exactly what you'd want from traits. So guard it, for example, static if (is(T:hasColor)) bar(t);; note that when 'bar(t) is an alias or mixin, this can be done inside the aliased or mixed-in code. There are some syntax sugar possibilities here (aot there should be a way to access other traits without introducing a named function). http://forum.dlang.org/post/mailman.4484.1434139778.7663.digitalmars-d puremagic.com has one example, using a slightly different syntax (the 'idiomatic D' way would be an is-expression inside static-if introducing the alias, but is() makes code extremely ugly and unreadable). [1] "and more": it allows for overloading on traits, something that can not be (cleanly) done with constraints. When there is more than one candidate template, the compiler can easily determine the most specialized one, eg if both 'InputRange' and 'ForwardRange' matches, it just needs to try to instantiate IR with the mock from FW range trait (and vice versa); if that fails then it means that FW should be chosen. IOW it works similarly to "normal" template overload resolution. Note that this only needs to be done (lazily/on-demand) once per trait-set. } void bar(T: hasColor)(T t) { t.color(); } Now consider a deeply nested chain of function calls like this. At the bottom, one adds a call to 'color', and now every function in the chain has to add 'hasColor' even though it has nothing to do with the logic in that function. No, as long as the extra functionality is optional no changes to callers are required, at least not for statically dispatched code that we're talking about here. If the new code /requires/ extra functionality then it needs to be explicitly requested. This is no different from how D classes work -- you either have to request a subclass to use it, or check with cast(SubClass) at run-time. Traits work at compile-time, that's all. artur  Jul 24 2015 Walter Bright <newshound2 digitalmars.com> writes: On 7/24/2015 4:55 AM, Artur Skawina via Digitalmars-d wrote: Basically, it can work like this: - traits, like your 'hasPrefix', check that 'T' implements an interface (defined in hasPrefix). (this does the job that template constraints do right now, and more [1]) - the compiler instantiates the template with a mock (defined inside 'hasPrefix'). (this immediately catches every illegal access, like 't.suffix' and results in a clear and informative error message) That really isn't any different from a user calling it with a 'hasPrefix' that doesn't have a 'suffix'. - the 'T' inside foo is still the original type that it was called with, so 'bar(t)' will succeed. But it needs to be conditionally enabled for just the types that implement 'hasColor' -- and this is exactly what you'd want from traits. So guard it, for example, static if (is(T:hasColor)) bar(t);; note that when 'bar(t) is an alias or mixin, this can be done inside the aliased or mixed-in code. As I mentioned in the antecedent, this pulls the teeth of the point of the trait, because what the function needs is 'hasPrefix && hasSuffix', and your position is that only 'hasPrefix' is required. This leaves us exactly where D is now. There are some syntax sugar possibilities here (aot there should be a way to access other traits without introducing a named function). http://forum.dlang.org/post/mailman.4484.1434139778.7663.digitalmars-d puremagic.com has one example, using a slightly different syntax (the 'idiomatic D' way would be an is-expression inside static-if introducing the alias, but is() makes code extremely ugly and unreadable). As I mentioned to Dicebot, syntactical improvements are possible (though I don't think we should rush into things here). [1] "and more": it allows for overloading on traits, something that can not be (cleanly) done with constraints. Overloading with constraints is commonplace in Phobos. Haven't really had any trouble with it. Now consider a deeply nested chain of function calls like this. At the bottom, one adds a call to 'color', and now every function in the chain has to add 'hasColor' even though it has nothing to do with the logic in that function. No, as long as the extra functionality is optional no changes to callers are required, at least not for statically dispatched code that we're talking about here. That's how D works today.  Jul 24 2015 "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes: On Fri, Jul 24, 2015 at 11:29:28AM -0700, Walter Bright via Digitalmars-d wrote: [...] Overloading with constraints is commonplace in Phobos. Haven't really had any trouble with it. [...] Actually, I've had trouble with overloading with constraints. Unlike C++, D does not allow multiple possible template instantiations in overload sets. While this is ostensibly a good thing, it makes writing fallbacks extremely cumbersome. For example, suppose I have a set of overloads of myFunc(), each of which specifies some set of constraints defining which subset of types they are implemented for: void myFunc(T)(T t) if (someSetOfConditions!T) { ... } void myFunc(T)(T t) if (someOtherSetOfConditions!T) { ... } void myFunc(T)(T t) if (yetAnotherSetOfConditions!T) { ... } As long as the conditions are mutually exclusive, everything is OK. But suppose these conditions describe specializations of the function for specific concrete types (e.g. I want to take advantage of extra properties of the concrete types to implement faster algorithms), but I want a fallback function containing a generic implementation that works for all types. Unfortunately, I can't just write another overload of myFunc without constraints, because it causes conflicts with the preceding overloads. The only way to achieve this is to explicitly negate every condition in all other overloads: // generic fallback void myFunc(T)(T t) if (!someSetOfConditions!T && !someOtherSetOfConditions!T && !yetAnotherSetOfConditions!T) { ... } This isn't too bad at first glance -- a little extra typing never hurt nobody, right? Unfortunately, this doesn't work if, say, all these overloads are part of some module M, but the user wishes to extend the functionality of myFunc by providing his own specialization for his own user-defined type, say, in a different module. That is prohibited because the generic myFunc above doesn't have the negation of whatever conditions the user placed in his specialization, so it will cause an overload conflict. It is also a maintenance issue that whenever somebody adds or removes a new specialization of myFunc (even within module M), the sig constraints of the generic fallback must be updated accordingly. It gets worse if the original sig constraints were buggy -- then you have to fix them in both the specialization and the generic fallback -- and hope you didn't miss any conditions (e.g. a typo causes the generic fallback not to pick up something that the specialization now declines). If you think this is a contrived scenario, you should take a look at std.conv, where this particular problem has become a maintenance headache. Among the several dozens of overloads of toImpl, there are all kinds of sig constraints that, at first glance, isn't obvious whether or not they cover all the necessary cases, and whether various fallbacks (yes there are multiple! the above scenario is a simplified description) correctly catch all the cases they ought to catch. When there is a bug in one of the toImpl overloads, it's a nightmare to find out which one it is -- because you have to parse and evaluate all the sig constraints of every overload just to locate the offending function. Maybe as a Phobos *user* you perceive that overloading with sig constraints is nice and clean... But as someone who was foolhardy enough once to attempt to sort out the tangled mess that is the sig constraints of toImpl overloads, I'm getting a rather different perception of the situation. T -- What do you mean the Internet isn't filled with subliminal messages? What about all those buttons marked "submit"??  Jul 24 2015 Walter Bright <newshound2 digitalmars.com> writes: On 7/24/2015 11:50 AM, H. S. Teoh via Digitalmars-d wrote: The only way to achieve this is to explicitly negate every condition in all other overloads: Another way is to list everything you accept in the constraint, and then separate out the various implementations in the template body using static if. It's a lot easier making the documentation for that, too.  Jul 24 2015 "Jonathan M Davis" <jmdavisProg gmx.com> writes: On Saturday, 25 July 2015 at 05:27:48 UTC, Walter Bright wrote: On 7/24/2015 11:50 AM, H. S. Teoh via Digitalmars-d wrote: The only way to achieve this is to explicitly negate every condition in all other overloads: Another way is to list everything you accept in the constraint, and then separate out the various implementations in the template body using static if. It's a lot easier making the documentation for that, too. I've considered off and on arguing that a function like find should have a top level template that has the constraints that cover all of the overloads, and then either putting each of the individual functions with their own constraints internally or use separate static ifs within a single function (or some combination of the two). That way, you end up with a simple template constraint that the user sees rather than the huge mess that you get now - though if you still have individual functions within that outer template, then that doesn't really fix the overloading problem except insomuch as the common portion of their template constraints (which is then in the outer template's constraint) would then not have to be repeated. However, when anyone has brought up anything like this, Andrei has argued against it, though I think that those arguments had to do primarily with the documentation, because the person suggesting the change was looking for simplified documentation, and Andrei thought that the ddoc generation should be smart enough to be able to combine things for you. So, maybe it wouldn't be that hard to convince him of what I'm suggesting, but I don't know. I haven't tried yet. It's just something that's occurred to me from time to time, and I've wondered if we should change how we go about things in a manner along those lines. It could help with the documentation and understanding the template constraint as well as help reduce the pain with the overloads. Andrei has definitely been against overloading via static if though whenever that suggestion has been made. I think that he thinks that if you do that, it's a failure of template constraints - though if you use an outer template and then overload the function internally, then you're still using template constraints rather than static if, and you get simplified template constraints anyway. So, maybe we should look at something along those lines rather than proliferating the top-level function overloading like we're doing now. - Jonathan M Davis  Jul 24 2015 Walter Bright <newshound2 digitalmars.com> writes: On 7/24/2015 11:10 PM, Jonathan M Davis wrote: So, maybe we should look at something along those lines rather than proliferating the top-level function overloading like we're doing now. Consider the following pattern, which I see often in Phobos: void foo(T)(T t) if (A) { ... } void foo(T)(T t) if (!A && B) { ... } from a documentation (i.e. user) perspective. Now consider: void foo(T)(T t) if (A || B) { static if (A) { ... } else static if (B) { ... } else static assert(0); } Makes a lot more sense to the user, who just sees one function that needs A or B, and doesn't see the internal logic.  Jul 25 2015 "Jonathan M Davis" <jmdavisProg gmx.com> writes: On Saturday, 25 July 2015 at 08:48:40 UTC, Walter Bright wrote: On 7/24/2015 11:10 PM, Jonathan M Davis wrote: So, maybe we should look at something along those lines rather than proliferating the top-level function overloading like we're doing now. Consider the following pattern, which I see often in Phobos: void foo(T)(T t) if (A) { ... } void foo(T)(T t) if (!A && B) { ... } from a documentation (i.e. user) perspective. Now consider: void foo(T)(T t) if (A || B) { static if (A) { ... } else static if (B) { ... } else static assert(0); } Makes a lot more sense to the user, who just sees one function that needs A or B, and doesn't see the internal logic. Yeah, though, I believe that Andrei has argued against that every time that someone suggests doing that. IIRC, he wants ddoc to that for you somehow rather than requiring that we write code that way. And from a test perspective, it's actually a bit ugly to take function overloads and turn them into static ifs, because instead of having separate functions that you can put unittest blocks under, you have to put all of those tests in a single unittest block or put the unittest blocks in a row with comments on them to indicate which static if branch they go with. It also has the problem that the function can get _way_ too long (e.g. putting all of the overloads of find in one function would be a really bad idea). Alternatively, you could do something like template foo(T) if(A || B) { void foo()(T t) if(A) {} void foo()(T t) if(B) {} } which gives you the simplified template constraint for the documentation, though for better or worse, you'd still get the individual template constraints listed separately for each overload - though given how often each overload needs an explanation, that's not necessarily bad. And in many cases, what you really have is overlapping constraints rather than truly distinct ones. So, you'd have something like auto foo(alias pred, R)(R r) if(testPred!pred && isInputRange!R && !isForwardRange!R) {} auto foo(alias pred, R)(R r) if(testPred!pred && isForwardRange!R) {} and be turning it into something like template foo(alias pred) if(testPred!pred) { auto foo(R)(R r) if(isInputRange!R && !isForwardRange!R) {} auto foo(R)(R r) if(isForwardRange!R) {} } So, part of the template constraint gets factored out completely. And if you want to factor it out more than that but still don't want to use static if because of how it affects the unit tests, or because you don't want the function to get overly large, then you can just forward it to another function. e.g. auto foo(alias pred, R)(R r) if(testPred!pred && isInputRange!R) { return _foo(pred, r); } auto _foo(alias pred, R)(R r) if(!isForwardRange!R) {} auto _foo(alias pred, R)(R r) if(isForwardRange!R) {} or go for both the outer template and forwarding, and do template foo(alias pred) if(testPred!pred) { auto foo(R)(R r) if(isInputRange!R) { return _foo(pred, r); } auto _foo(R)(R r) if(!isForwardRange!R) {} auto _foo(R)(R r) if(isForwardRange!R) {} } We've already created wrapper templates for at least some of the functions in Phobos so that you can partially instantiate them - e.g. alias myMap = map!(a => a.func()); So, it we're already partially moving stuff up a level in some cases. We just haven't used it as a method to simplify the main template constraint that user sees or to simplify overloads. I do think that it can make sense to put very similar overloads in a single function with static if branches like you're suggesting, but I do think that it's a bit of a maintenance issue to do it for completely distinct overloads - especially if there are several of them rather than just a couple. But it's still possible to combine their template constraints at a higher level and have overloaded functions rather than simply using static ifs. - Jonathan M Davis  Jul 25 2015 Walter Bright <newshound2 digitalmars.com> writes: On 7/25/2015 2:14 AM, Jonathan M Davis wrote: I do think that it can make sense to put very similar overloads in a single function with static if branches like you're suggesting, but I do think that it's a bit of a maintenance issue to do it for completely distinct overloads - especially if there are several of them rather than just a couple. But it's still possible to combine their template constraints at a higher level and have overloaded functions rather than simply using static ifs. I also sometimes see: void foo(T)(T t) if (A && B) { ... } void foo(T)(T t) if (A && !B) { ... } The user should never have to see the B constraint in the documentation. This should be handled internally with static if.  Jul 25 2015 Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes: On 7/25/15 5:34 AM, Walter Bright wrote: On 7/25/2015 2:14 AM, Jonathan M Davis wrote: I do think that it can make sense to put very similar overloads in a single function with static if branches like you're suggesting, but I do think that it's a bit of a maintenance issue to do it for completely distinct overloads - especially if there are several of them rather than just a couple. But it's still possible to combine their template constraints at a higher level and have overloaded functions rather than simply using static ifs. I also sometimes see: void foo(T)(T t) if (A && B) { ... } void foo(T)(T t) if (A && !B) { ... } The user should never have to see the B constraint in the documentation. This should be handled internally with static if. There are problems with that but I do agree with the sentiment. -- Andrei  Jul 25 2015 "Enamex" <enamex+d outlook.com> writes: On Saturday, 25 July 2015 at 09:14:04 UTC, Jonathan M Davis wrote: . . . auto foo(alias pred, R)(R r) if(testPred!pred && isInputRange!R && !isForwardRange!R) {} auto foo(alias pred, R)(R r) if(testPred!pred && isForwardRange!R) {} and be turning it into something like template foo(alias pred) if(testPred!pred) { auto foo(R)(R r) if(isInputRange!R && !isForwardRange!R) {} auto foo(R)(R r) if(isForwardRange!R) {} } . . . - Jonathan M Davis The example(s) is confusing me. foo!(first)(second); isn't really an alternative to foo(first, second);. Am I misreading something?  Jul 25 2015 "Nicholas Wilson" <iamthewilsonator hotmail.com> writes: On Sunday, 26 July 2015 at 01:55:12 UTC, Enamex wrote: On Saturday, 25 July 2015 at 09:14:04 UTC, Jonathan M Davis wrote: . . . auto foo(alias pred, R)(R r) if(testPred!pred && isInputRange!R && !isForwardRange!R) {} auto foo(alias pred, R)(R r) if(testPred!pred && isForwardRange!R) {} and be turning it into something like template foo(alias pred) if(testPred!pred) { auto foo(R)(R r) if(isInputRange!R && !isForwardRange!R) {} auto foo(R)(R r) if(isForwardRange!R) {} } . . . - Jonathan M Davis The example(s) is confusing me. foo!(first)(second); isn't really an alternative to foo(first, second);_?_. Am I misreading something? No. Yes. first is a compile time parameter and can be anything. In this case it defines a type (in examples T is used for generic type, and R for a range). but it can be anything, a type literal i.e. foo!(size_t a)(Bar b); , it can be another symbol ( a function, class or even another template (with is own set of args)! it can be varaidic as well i.e. foo!(T...)(some_args) Second is the set of runtime parameters, which can be of types defined in the compile time args.  Jul 25 2015 Manu via Digitalmars-d <digitalmars-d puremagic.com> writes: On 25 July 2015 at 18:48, Walter Bright via Digitalmars-d <digitalmars-d puremagic.com> wrote: On 7/24/2015 11:10 PM, Jonathan M Davis wrote: So, maybe we should look at something along those lines rather than proliferating the top-level function overloading like we're doing now. Consider the following pattern, which I see often in Phobos: void foo(T)(T t) if (A) { ... } void foo(T)(T t) if (!A && B) { ... } from a documentation (i.e. user) perspective. Now consider: void foo(T)(T t) if (A || B) { static if (A) { ... } else static if (B) { ... } else static assert(0); } Makes a lot more sense to the user, who just sees one function that needs A or B, and doesn't see the internal logic. This! I've felt this way with phobos in particular for ages. I've argued this exact case before, and it's been rejected. I much prefer static if inside functions rather than pollute the namespace (and docs) with a bunch of overloads. Also, these symbols with lots of constraints can get really long!  Jul 25 2015 Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes: On 7/24/15 2:50 PM, H. S. Teoh via Digitalmars-d wrote: Maybe as a Phobos*user* you perceive that overloading with sig constraints is nice and clean... But as someone who was foolhardy enough once to attempt to sort out the tangled mess that is the sig constraints of toImpl overloads, I'm getting a rather different perception of the situation. I think we're in good shape there. -- Andrei  Jul 25 2015 Artur Skawina via Digitalmars-d <digitalmars-d puremagic.com> writes: On 07/24/15 20:29, Walter Bright via Digitalmars-d wrote: On 7/24/2015 4:55 AM, Artur Skawina via Digitalmars-d wrote: Basically, it can work like this: - traits, like your 'hasPrefix', check that 'T' implements an interface (defined in hasPrefix). (this does the job that template constraints do right now, and more [1]) - the compiler instantiates the template with a mock (defined inside 'hasPrefix'). (this immediately catches every illegal access, like 't.suffix' and results in a clear and informative error message) That really isn't any different from a user calling it with a 'hasPrefix' that doesn't have a 'suffix'. The difference is that right now the developer has to write a unit-test per function that uses hasPrefix, otherwise the code might not even be verified to compile. 100% unit-test coverage is not going to happen in practice, and just like with docs, making things easier and reducing boilerplate to a minimum would improve the situation dramatically. - the 'T' inside foo is still the original type that it was called with, so 'bar(t)' will succeed. But it needs to be conditionally enabled for just the types that implement 'hasColor' -- and this is exactly what you'd want from traits. So guard it, for example, static if (is(T:hasColor)) bar(t);; note that when 'bar(t) is an alias or mixin, this can be done inside the aliased or mixed-in code. As I mentioned in the antecedent, this pulls the teeth of the point of the trait, because what the function needs is 'hasPrefix && hasSuffix', and your position is that only 'hasPrefix' is required. The function only needs what it uses, ie the i/f defined by hasPrefix. It can optionally access other i/fs like hasSuffix if the type supports them, and it can do so w/o affecting callers (or callees). Iff you modify the function to /require/ both traits, then, yes, you need to also update the traits, eg create and use a hasAdfixes trait instead. This is a feature and not a problem; traits would of course be opt-in and using them only makes sense when the interfaces are very stable, like D's ranges -- if a required primitive is added, removed or modified then the range-traits *should* be updated. This leaves us exactly where D is now. No, right now D does not provide any (built-in) functionality for restricting (and automatically documenting) non-dynamic interfaces. [1] "and more": it allows for overloading on traits, something that can not be (cleanly) done with constraints. Overloading with constraints is commonplace in Phobos. Haven't really had any trouble with it. It doesn't work (ie does not scale) when the constraints are non- exclusive. At some point one reaches for: template _pick_f(T) { static if (is(T:ForwardRange)/*&&smth&&(!smth_else||wever)*/) enum _pick_f = 1; else static if (is(T:ForwardRange)/*&&!(smth&&(!smth_else||wever))*/) enum _pick_f = 2; else static if (is(T:InputRange)) enum _pick_f = 3; /*...*/ } auto f(T)(T a) if (_pick_f!T==1) {/*...*/} auto f(T)(T a) if (_pick_f!T==2) {/*...*/} auto f(T)(T a) if (_pick_f!T==3) {/*...*/} Ugly as it is, it's still better than the alternative (where the ugliness isn't contained, but spread out over all f definitions and not guaranteed to be coherent). artur  Jul 24 2015 "Jonathan M Davis" <jmdavisProg gmx.com> writes: On Friday, 24 July 2015 at 20:57:34 UTC, Artur Skawina wrote: The difference is that right now the developer has to write a unit-test per function that uses hasPrefix, otherwise the code might not even be verified to compile. 100% unit-test coverage is not going to happen in practice, and just like with docs, making things easier and reducing boilerplate to a minimum would improve the situation dramatically. But you see. This is exactly wrong attitude. Why on earth should we make life easier for folks who don't bother to get 100% unit test coverage? A feature has to add more value than to simply make it so that you're slightly less screwed if you don't write unit tests. - Jonathan M Davis  Jul 24 2015 "Tofu Ninja" <emmons0 purdue.edu> writes: On Friday, 24 July 2015 at 21:32:19 UTC, Jonathan M Davis wrote: This is exactly wrong attitude. Why on earth should we make life easier for folks who don't bother to get 100% unit test coverage? Because that is 99% of D users...  Jul 24 2015 "Jonathan M Davis" <jmdavisProg gmx.com> writes: On Friday, 24 July 2015 at 21:48:23 UTC, Tofu Ninja wrote: On Friday, 24 July 2015 at 21:32:19 UTC, Jonathan M Davis wrote: This is exactly wrong attitude. Why on earth should we make life easier for folks who don't bother to get 100% unit test coverage? Because that is 99% of D users... If so, they have no excuse. D has made it ridiculously easy to unit test your code. And I very much doubt that 99% of D users don't unit test their code. There are cases where 100% isn't possible - e.g. because of an assert(0) or because you're dealing with UI code or the like where it simply isn't usable without running the program - but even then, the test coverage should be as close to 100% as can be achieved, which isn't usually going to be all that far from 100%. We should be ashamed when our code is not as close to 100% code coverage as is feasible (which is usually 100%). - Jonathan M Davis  Jul 24 2015 "Tofu Ninja" <emmons0 purdue.edu> writes: On Friday, 24 July 2015 at 22:07:14 UTC, Jonathan M Davis wrote: On Friday, 24 July 2015 at 21:48:23 UTC, Tofu Ninja wrote: On Friday, 24 July 2015 at 21:32:19 UTC, Jonathan M Davis wrote: This is exactly wrong attitude. Why on earth should we make life easier for folks who don't bother to get 100% unit test coverage? Because that is 99% of D users... If so, they have no excuse. D has made it ridiculously easy to unit test your code. And I very much doubt that 99% of D users don't unit test their code. There are cases where 100% isn't possible - e.g. because of an assert(0) or because you're dealing with UI code or the like where it simply isn't usable without running the program - but even then, the test coverage should be as close to 100% as can be achieved, which isn't usually going to be all that far from 100%. We should be ashamed when our code is not as close to 100% code coverage as is feasible (which is usually 100%). - Jonathan M Davis I ment 99% don't 100% unit tests, but even close to 100% is still probably not that common, most D users are hobbyists I think(though I could be wrong), and hobbyists are lazy.  Jul 24 2015 Justin Whear <justin economicmodeling.com> writes: On Fri, 24 Jul 2015 22:07:12 +0000, Jonathan M Davis wrote: On Friday, 24 July 2015 at 21:48:23 UTC, Tofu Ninja wrote: On Friday, 24 July 2015 at 21:32:19 UTC, Jonathan M Davis wrote: This is exactly wrong attitude. Why on earth should we make life easier for folks who don't bother to get 100% unit test coverage? Because that is 99% of D users... If so, they have no excuse. D has made it ridiculously easy to unit test your code. And I very much doubt that 99% of D users don't unit test their code. There are cases where 100% isn't possible - e.g. because of an assert(0) or because you're dealing with UI code or the like where it simply isn't usable without running the program - but even then, the test coverage should be as close to 100% as can be achieved, which isn't usually going to be all that far from 100%. We should be ashamed when our code is not as close to 100% code coverage as is feasible (which is usually 100%). - Jonathan M Davis Commercial (though in-house) D library and tools writer here. We run code-coverage as part of our CI process and report results back to Gitlab (our self-hosted Github-like). Merge requests all report the code coverage of the pull (haven't figured out how to do a delta against the old coverage yet). I regularly test code to 100% of coverable lines, where coverable lines are all but: assert(0, ...) Test case lines that aren't supposed to execute (e.g. lambdas in a predSwitch) I agree that there's really no excuse and think we ought to orient the language towards serious professionals who will produce quality code. Bad code is bad code, regardless of the language.  Jul 24 2015 Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes: On 7/24/15 6:58 PM, Justin Whear wrote: I agree that there's really no excuse and think we ought to orient the language towards serious professionals who will produce quality code. Bad code is bad code, regardless of the language. YES! Amen to that. -- Andrei  Jul 25 2015 Walter Bright <newshound2 digitalmars.com> writes: On 7/24/2015 3:07 PM, Jonathan M Davis wrote: If so, they have no excuse. D has made it ridiculously easy to unit test your code. And I very much doubt that 99% of D users don't unit test their code. D has done a great job of making unit tests the rule, rather than the exception. There are cases where 100% isn't possible - e.g. because of an assert(0) or because you're dealing with UI code or the like where it simply isn't usable without running the program - but even then, the test coverage should be as close to 100% as can be achieved, which isn't usually going to be all that far from 100%. We should be ashamed when our code is not as close to 100% code coverage as is feasible (which is usually 100%). Right on, Jonathan!  Jul 24 2015 "Jonathan M Davis" <jmdavisProg gmx.com> writes: On Saturday, 25 July 2015 at 00:28:19 UTC, Walter Bright wrote: On 7/24/2015 3:07 PM, Jonathan M Davis wrote: D has done a great job of making unit tests the rule, rather than the exception. Yeah. I wonder what would happen with some of the folks that I've worked with who were anti-unit testing if they were programming in D. It would be more or less shoved in their face at that point rather than having it in a separate set of code somewhere that they could ignore, and it would be so easy to put them in there that it would have to be embarrassing on some level at least if they didn't write them. But they'd probably still argue against them and argue that D was stupid for making them so prominent... :( I do think that our built-in unit testing facilities are a huge win for us though. It actually seems kind of silly at this point that most other languages don't have something similar given how critical they are to high quality, maintainable code. We should be ashamed when our code is not as close to 100% code coverage as is feasible (which is usually 100%). Right on, Jonathan! I must say that this is a rather odd argument to be having though, since normally I'm having to argue that 100% test coverage isn't enough rather than that code needs to have 100% (e.g. how range-based algorithms need to be tested with both value type ranges and reference type ranges, which doesn't increase the code coverage at all but does catch bugs with how save is used, and without that, those bugs won't be caught). So, having to argue that all code should have 100% code coverage (or as close to it as is possible anyway) is kind of surreal. I would have thought that that was a given at this point. The real question is how far you need to go past that to ensure that your code works correctly. - Jonathan M Davis  Jul 25 2015 Walter Bright <newshound2 digitalmars.com> writes: On 7/25/2015 12:08 AM, Jonathan M Davis wrote: I must say that this is a rather odd argument to be having though, since normally I'm having to argue that 100% test coverage isn't enough rather than that code needs to have 100% (e.g. how range-based algorithms need to be tested with both value type ranges and reference type ranges, which doesn't increase the code coverage at all but does catch bugs with how save is used, and without that, those bugs won't be caught). So, having to argue that all code should have 100% code coverage (or as close to it as is possible anyway) is kind of surreal. I would have thought that that was a given at this point. The real question is how far you need to go past that to ensure that your code works correctly. It's still unusual to have 100% coverage in Phobos, and this is not because it is hard. Most of the time, it is easy to do. It's just that nobody checks it. Although we have succeeded in making unit tests part of the culture, the next step is 100% coverage. I know that 100% unit test coverage hardly guarantees code correctness. However, since I started using code coverage analyzers in the 1980s, the results are surprising - code with 100% test coverage has at LEAST an order of magnitude fewer bugs showing up in the field. It's surprisingly effective. I would have had a LOT more trouble shipping the Warp project if I hadn't gone with 100% coverage from the ground up. Nearly all the bugs it had in the field were due to my misunderstandings of the peculiarities of gpp - the code had worked as I designed it. This is a huge reason why I want to switch to ddmd. I want to improve the quality of the compiler with unit tests. The various unit tests schemes I've tried for C++ are all ugly, inconvenient, and simply a bitch. It's like trying to use a slide rule after you've been given a calculator. (I remember the calculator revolution. It happened my freshman year at college. September 1975 had$125 slide rules in the campus bookstore. December they were
at $5 cutout prices, and were gone by January. I never saw anyone use a slide rule again. I've never seen a technological switchover happen so fast, before or since.)  Jul 25 2015 "Jonathan M Davis" <jmdavisProg gmx.com> writes: On Saturday, 25 July 2015 at 08:58:26 UTC, Walter Bright wrote: It's still unusual to have 100% coverage in Phobos, and this is not because it is hard. Most of the time, it is easy to do. It's just that nobody checks it. Yeah. I thought there had been a change to make it so that the coverage got printed out as part of the build, but I don't see it right now, at least on FreeBSD. Folks would still have to pay attention to those numbers though. Writing the tests is easy, and if someone is conscientious about their tests, I'd expect them to typically hit 100% without having to check (though you can still miss the occasional branch even then - especially with templated code), but frequently folks just write a few tests to make sure that the most basic functionality works and then call it a day. I know that I'm too often guilty of assuming that I hit 100%, because I was thorough with my testing (since I tend to be _very_ thorough with my tests), and I should do better at verifying that I didn't miss something. I did put an effort a while back in making sure that std.datetime was as close to 100% as was possible, but I haven't checked it in a while... Well, that's embarrassing. Almost all of the uncovered lines are lines that should never be run - e.g. assert(0) - but it does look like there are some lines which aren't covered which should be (not many in comparison to the whole, but there shouldn't be any). Interestingly though, the coverage is worse than it should be because it was generated from the release build on the unit tests, and stuff like invariants doesn't get run. I'll have to figure out how to get it to give me the coverage for the debug run of the tests, since that would be more accurate - though std.datetime can never actually hit 100% thanks to all of the assert(0) lines in it and the scope(failure) lines for printing out extra information when a test does fail. I suspect that I'm all of a percentage point off of what the max is. Interestingly enough, disable this() {} counts as a 0 too, even though the compiler should know that it's impossible for that to run even if the code is wrong - unlike assert(0). It _would_ be nice though if the assert(0) lines at least weren't counted, and it is kind of weird that the unit test lines count (though aside from scope(failure) lines, those should all run, though since I tend to put scope(failure) lines in unit tests for better output on failures, that's going to hurt my code coverage). So, _actually_ hitting 100% is likely to be annoyingly elusive for a lot of code, even if it's actually fully tested. But while std.datetime is almost as close as it can get, it's still _almost_ as close as it can get rather than all the way. :( Clearly, I have a PR or two to write... Although we have succeeded in making unit tests part of the culture, the next step is 100% coverage. Agreed. I know that 100% unit test coverage hardly guarantees code correctness. However, since I started using code coverage analyzers in the 1980s, the results are surprising - code with 100% test coverage has at LEAST an order of magnitude fewer bugs showing up in the field. It's surprisingly effective. Oh, definitely. But while 100% unit test coverage is a huge step forward, I also think that for truly solid code, you want to go beyond that and make sure that you test corner cases and the like, test with a large enough variety of types with templates to catch behavioral bugs, etc. So, I don't think that we want to stop at 100% code coverage, but we do need to make sure that we're at 100% first and foremost. This is a huge reason why I want to switch to ddmd. I want to improve the quality of the compiler with unit tests. That would definitely be cool. The various unit tests schemes I've tried for C++ are all ugly, inconvenient, and simply a bitch. It's like trying to use a slide rule after you've been given a calculator. Yeah. It's not that hard to write one, and the tests themselves generally end up being pretty much the same as what you'd have in D, but there's still an annoying amount of boilerplate in getting them declared and set up - which is annoying enough in and of itself, but yeah, once you're used to D's unit tests, it seems particularly onerous. (I remember the calculator revolution. It happened my freshman year at college. September 1975 had$125 slide rules in the
campus bookstore. December they were at $5 cutout prices, and were gone by January. I never saw anyone use a slide rule again. I've never seen a technological switchover happen so fast, before or since.) If only folks thought that D's advantages over C++ were that obvious. ;) - Jonathan M Davis  Jul 25 2015 Walter Bright <newshound2 digitalmars.com> writes: On 7/25/2015 2:53 AM, Jonathan M Davis wrote: Oh, definitely. But while 100% unit test coverage is a huge step forward, I also think that for truly solid code, you want to go beyond that and make sure that you test corner cases and the like, test with a large enough variety of types with templates to catch behavioral bugs, etc. So, I don't think that we want to stop at 100% code coverage, but we do need to make sure that we're at 100% first and foremost. There's another thing I discovered. If functions are broken up into smaller logical units, the unit testing gets easier and there are fewer bugs. For example, the dmd code that reads the dmd.conf file was one function that read the files, allocated memory, did the parsing, built the data structures, etc. By splitting all these things up, suddenly it gets a lot easier to test! For example, just use the normal file I/O functions to read the files, which are tested elsewhere. Boom, don't need to construct test files. The parsing logic can be easily handled by its own unit tests. And so on. I think there's also a learned skill to having the fewest number of orthogonal unit tests that give 100% coverage, rather than a blizzard of tests that overlap each other. (I remember the calculator revolution. It happened my freshman year at college. September 1975 had$125 slide rules in the campus bookstore. December
they were at \$5 cutout prices, and were gone by January. I never saw anyone
use a slide rule again. I've never seen a technological switchover happen so
fast, before or since.)

If only folks thought that D's advantages over C++ were that obvious. ;)

Unfortunately, D's advantages only become more than a grab-bag of features
after
you've used it for a while. We are also still discovering the right way to use
them.

Jul 25 2015
"Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Saturday, 25 July 2015 at 08:58:26 UTC, Walter Bright wrote:
I would have had a LOT more trouble shipping the Warp project
if I hadn't gone with 100% coverage from the ground up. Nearly
all the bugs it had in the field were due to my
misunderstandings of the peculiarities of gpp - the code had
worked as I designed it.

This is a huge reason why I want to switch to ddmd. I want to
improve the quality of the compiler with unit tests. The
various unit tests schemes I've tried for C++ are all ugly,
inconvenient, and simply a bitch. It's like trying to use a
slide rule after you've been given a calculator.

LOL. I finally got some bugs sorted out on the project that I'm
working on at work (in C++), which means that I can get back to
what I was working on implementing before, and about all I recall
for sure is that I was working on the unit tests for it. I don't
know where I was with them. I find myself wishing that I had -cov
so that I could figure out what I had left to test... :(

It often seems like the advantages of some of D's features are
more obvious when you have to go back to another language like
C++ which doesn't have them.

- Jonathan M Davis

Aug 04 2015
"Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
On Tuesday, 4 August 2015 at 20:47:00 UTC, Jonathan M Davis wrote:
LOL. I finally got some bugs sorted out on the project that I'm
working on at work (in C++), which means that I can get back to
what I was working on implementing before, and about all I
recall for sure is that I was working on the unit tests for it.
I don't know where I was with them. I find myself wishing that
I had -cov so that I could figure out what I had left to
test... :(

It often seems like the advantages of some of D's features are
more obvious when you have to go back to another language like
C++ which doesn't have them.

What do you dislike about C++ coverage tooling in comparison with
D's?

Aug 04 2015
"Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Tuesday, 4 August 2015 at 22:42:50 UTC, Ola Fosheim Grøstad
wrote:
On Tuesday, 4 August 2015 at 20:47:00 UTC, Jonathan M Davis
wrote:
LOL. I finally got some bugs sorted out on the project that
I'm working on at work (in C++), which means that I can get
back to what I was working on implementing before, and about
all I recall for sure is that I was working on the unit tests
for it. I don't know where I was with them. I find myself
wishing that I had -cov so that I could figure out what I had
left to test... :(

It often seems like the advantages of some of D's features are
more obvious when you have to go back to another language like
C++ which doesn't have them.

What do you dislike about C++ coverage tooling in comparison
with D's?

To get code coverage in C++, I'd have to go track down a tool to
do it. There is none which is used as part of our normal build
process at work. As it is, we only have unit tests because I went
and added what was needed to write them and have been writing
them. No one else has been writing them, and if I want any kind
of code coverage stuff set up, I'd have to go spend the time to
figure it out. With D, it's all built-in, and I don't have to
figure out which tools to use or write any of them myself -
either for unit testing or code coverage. They're just there and

- Jonathan M Davis

Aug 04 2015
"rsw0x" <anonymous anonymous.com> writes:
On Wednesday, 5 August 2015 at 04:10:22 UTC, Jonathan M Davis
wrote:
On Tuesday, 4 August 2015 at 22:42:50 UTC, Ola Fosheim Grøstad
wrote:
On Tuesday, 4 August 2015 at 20:47:00 UTC, Jonathan M Davis
wrote:
[...]

What do you dislike about C++ coverage tooling in comparison
with D's?

To get code coverage in C++, I'd have to go track down a tool
to do it. There is none which is used as part of our normal
build process at work. As it is, we only have unit tests
because I went and added what was needed to write them and have
been writing them. No one else has been writing them, and if I
want any kind of code coverage stuff set up, I'd have to go
spend the time to figure it out. With D, it's all built-in, and
I don't have to figure out which tools to use or write any of
them myself - either for unit testing or code coverage. They're
just there and ready to go.

- Jonathan M Davis

This is nonsense, what major C++ compiler doesn't provide code
coverage?

I feel like 99% of C++ vs D arguments on this forum are comparing
C++98 to D.

Aug 04 2015
"Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
On Wednesday, 5 August 2015 at 04:10:22 UTC, Jonathan M Davis
wrote:
To get code coverage in C++, I'd have to go track down a tool
to do it. There is none which is used as part of our normal
build process at work. As it is, we only have unit tests
because I went and added what was needed to write them and have
been writing them.

I also tend to use the features that can be directly used from
the compiler switches more than external programs. I tend to look
at "--help" first. Maybe one should also list programs that are
distributed with the compiler in the compiler "--help" listing.

Aug 04 2015
Jacob Carlborg <doob me.com> writes:
On 2015-07-25 09:08, Jonathan M Davis wrote:

I do think that our built-in unit testing facilities are a huge win for
us though. It actually seems kind of silly at this point that most other
languages don't have something similar given how critical they are to
high quality, maintainable code.

Most modern languages are capable to implement something similar or
better purely in library code.

--
/Jacob Carlborg

Jul 28 2015
Artur Skawina via Digitalmars-d <digitalmars-d puremagic.com> writes:
On 07/24/15 23:32, Jonathan M Davis via Digitalmars-d wrote:
On Friday, 24 July 2015 at 20:57:34 UTC, Artur Skawina wrote:
The difference is that right now the developer has to write a unit-test per
function that uses hasPrefix, otherwise the code might not even be verified
to compile. 100% unit-test coverage is not going to happen in practice, and
just like with docs, making things easier and reducing boilerplate to a minimum
would improve the situation dramatically.

But you see. This is exactly wrong attitude. Why on earth should we make life
easier for folks who don't bother to get 100% unit test coverage?

How exactly does making it harder to write tests translate into
having better coverage? Why is requiring the programmer to write
unnecessary, redundant, and potentially buggy tests preferable?

artur

Jul 24 2015
"Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Friday, 24 July 2015 at 22:09:24 UTC, Artur Skawina wrote:
On 07/24/15 23:32, Jonathan M Davis via Digitalmars-d wrote:
On Friday, 24 July 2015 at 20:57:34 UTC, Artur Skawina wrote:
The difference is that right now the developer has to write a
unit-test per function that uses hasPrefix, otherwise the
code might not even be verified to compile. 100% unit-test
coverage is not going to happen in practice, and just like
with docs, making things easier and reducing boilerplate to a
minimum would improve the situation dramatically.

But you see. This is exactly wrong attitude. Why on earth
should we make life easier for folks who don't bother to get
100% unit test coverage?

How exactly does making it harder to write tests translate into
having better coverage? Why is requiring the programmer to
write unnecessary, redundant, and potentially buggy tests
preferable?

And how are we making it harder to write tests? We're merely
saying that you have to actually instantiate your template and
test those instantiations. If someone don't catch a bug in their
template, because they didn't try the various combinations of
stuff that it supports (and potentially verifying that it doesn't
compile with stuff that it's not supposed to support), then they
didn't test it enough. Having the compiler tell you that you're
using a function that you didn't require in your template
constraint might be nice, but if the programmer didn't catch that
anyway, then they didn't test enough. And if you don't test
enough, you're bound to have other bugs. So, the folks this helps
are the folks that aren't testing their code sufficiently and
thus likely have buggy code anyway.

- Jonathan M Davis

Jul 24 2015
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/24/15 6:09 PM, Artur Skawina via Digitalmars-d wrote:
On 07/24/15 23:32, Jonathan M Davis via Digitalmars-d wrote:
On Friday, 24 July 2015 at 20:57:34 UTC, Artur Skawina wrote:
The difference is that right now the developer has to write a unit-test per
function that uses hasPrefix, otherwise the code might not even be verified
to compile. 100% unit-test coverage is not going to happen in practice, and
just like with docs, making things easier and reducing boilerplate to a minimum
would improve the situation dramatically.

But you see. This is exactly wrong attitude. Why on earth should we make life
easier for folks who don't bother to get 100% unit test coverage?

How exactly does making it harder to write tests translate into
having better coverage? Why is requiring the programmer to write
unnecessary, redundant, and potentially buggy tests preferable?

False choice. -- Andrei

Jul 25 2015
"jmh530" <john.michael.hall gmail.com> writes:
On Friday, 24 July 2015 at 04:42:59 UTC, Walter Bright wrote:
Consider the following:

int foo(T: hasPrefix)(T t) {
t.prefix();    // ok
bar(t);        // error, hasColor was not specified for T
}

void bar(T: hasColor)(T t) {
t.color();
}

Now consider a deeply nested chain of function calls like this.
At the bottom, one adds a call to 'color', and now every
function in the chain has to add 'hasColor' even though it has
nothing to do with the logic in that function. This is the pit
that Exception Specifications fell into.

I'm a little confused here. I seem to be of the belief that D's
interfaces can accomplish virtually the same thing as Rust's
traits. In your example, if the type you pass to foo also
inherits from hasColor, then it shouldn't be a problem.

I fleshed out what you said a bit more with respect to D's
interfaces, adding another part to the chain as well. Obviously
baz in my example can't call objects from classes A and B because
they don't inherit from hasAlt. Isn't this the behavior you would
want? Another alternative is to have hasAlt inherit from hasColor
and hasPrefix.

import std.stdio : writeln;

interface hasColor
{
final void color()
{
writeln("calling color");
}
}

interface hasPrefix
{
final void prefix()
{
writeln("calling prefix");
}
}

interface hasAlt
{
final void alt()
{
writeln("calling alt");
}
}

class A : hasColor { }

class B : A, hasPrefix { }

class C : B, hasAlt { }

void foo(T: hasColor)(T t)
{
t.color();
}

void bar(T: hasPrefix)(T t)
{
t.prefix();
foo(t);
}

void baz(T: hasAlt)(T t)
{
t.alt();
bar(t);
}

void main()
{
auto a = new A;
foo(a);
auto b = new B;
foo(b);
bar(b);
auto c = new C;
foo(c);
bar(c);
baz(c);
}

Jul 24 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 7/24/2015 7:50 AM, jmh530 wrote:
I'm a little confused here. I seem to be of the belief that D's interfaces can
accomplish virtually the same thing as Rust's traits. In your example, if the
type you pass to foo also inherits from hasColor, then it shouldn't be a
problem.

As I replied earlier,

"It's a good question. And the answer is, the top level function does not list
every interface used by the call tree. Nested function calls test at runtime if
a particular interface is supported by an object, using dynamic casting or
QueryInterface() calls. It's fundamentally different from traits and concepts."

Jul 24 2015
Timon Gehr <timon.gehr gmx.ch> writes:
On 07/24/2015 08:30 PM, Walter Bright wrote:
On 7/24/2015 7:50 AM, jmh530 wrote:
I'm a little confused here. I seem to be of the belief that D's
interfaces can
accomplish virtually the same thing as Rust's traits. In your example,
if the
type you pass to foo also inherits from hasColor, then it shouldn't be
a problem.

As I replied earlier,

"It's a good question. And the answer is, the top level function does
not list every interface used by the call tree. Nested function calls
test at runtime if a particular interface is supported by an object,
using dynamic casting or QueryInterface() calls. It's fundamentally
different from traits and concepts."

This kind of testing for conformance can easily be allowed for
traits/concepts at compile time. (E.g. by default, or add a special Meta
trait.)

Jul 24 2015
Jacob Carlborg <doob me.com> writes:
On 2015-07-24 06:43, Walter Bright wrote:

Consider the following:

int foo(T: hasPrefix)(T t) {
t.prefix();    // ok
bar(t);        // error, hasColor was not specified for T
}

void bar(T: hasColor)(T t) {
t.color();
}

Now consider a deeply nested chain of function calls like this. At the
bottom, one adds a call to 'color', and now every function in the chain
has to add 'hasColor' even though it has nothing to do with the logic in
that function. This is the pit that Exception Specifications fell into.

I don't see the difference compared to a regular parameter. If you don't
specify any constraints/traits/whatever it like using "Object" for all

--
/Jacob Carlborg

Jul 24 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 7/24/2015 11:42 AM, Jacob Carlborg wrote:
I don't see the difference compared to a regular parameter. If you don't
specify
any constraints/traits/whatever it like using "Object" for all your parameter
types in Java.

So constraints then will be an all-or-nothing proposition? I believe that would
make them essentially useless.

I suspect I am not getting across the essential point. If I have a call tree,
and at the bottom I add a call to interface X, then I have to add a constraint
that additionally specifies X on each function up the call tree to the root.
That is antiethical to writing generic code, and will prove to be more of a
nuisance than an asset.

Exactly what sunk Exception Specifications.

Jul 24 2015
"Tofu Ninja" <emmons0 purdue.edu> writes:
On Friday, 24 July 2015 at 19:10:33 UTC, Walter Bright wrote:
On 7/24/2015 11:42 AM, Jacob Carlborg wrote:
I don't see the difference compared to a regular parameter. If
you don't specify
any constraints/traits/whatever it like using "Object" for all
types in Java.

So constraints then will be an all-or-nothing proposition? I
believe that would make them essentially useless.

I suspect I am not getting across the essential point. If I
have a call tree, and at the bottom I add a call to interface
X on each function up the call tree to the root. That is
antiethical to writing generic code, and will prove to be more
of a nuisance than an asset.

Exactly what sunk Exception Specifications.

But thats exactly how normal interfaces work...

eg:
interface Iface{ void foo(){} }

void func1(Iface x){ func2(x); }
void func2(Iface x){ func3(x); }
void func3(Iface x){ x.bar(); } // ERROR no bar in Iface

Only options here are A: update Iface to have bar() or B: make a
new interface and change it on the whole tree. The same "problem"
would exist for the concepts, but its the reason why people want
it.

Jul 24 2015
On Friday, 24 July 2015 at 21:27:09 UTC, Tofu Ninja wrote:
On Friday, 24 July 2015 at 19:10:33 UTC, Walter Bright wrote:
On 7/24/2015 11:42 AM, Jacob Carlborg wrote:
I don't see the difference compared to a regular parameter.
If you don't specify
any constraints/traits/whatever it like using "Object" for
types in Java.

So constraints then will be an all-or-nothing proposition? I
believe that would make them essentially useless.

I suspect I am not getting across the essential point. If I
have a call tree, and at the bottom I add a call to interface
X on each function up the call tree to the root. That is
antiethical to writing generic code, and will prove to be more
of a nuisance than an asset.

Exactly what sunk Exception Specifications.

But thats exactly how normal interfaces work...

eg:
interface Iface{ void foo(){} }

void func1(Iface x){ func2(x); }
void func2(Iface x){ func3(x); }
void func3(Iface x){ x.bar(); } // ERROR no bar in Iface

Only options here are A: update Iface to have bar() or B: make
a new interface and change it on the whole tree. The same
"problem" would exist for the concepts, but its the reason why
people want it.

C: do a runtime check or downcast.

Jul 24 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 7/24/2015 2:27 PM, Tofu Ninja wrote:
But thats exactly how normal interfaces work...

No it isn't. Google QueryInterface(). Nobody lists all the interfaces at the
top
level functions, which is that Rust traits and C++ concepts require.

eg:
interface Iface{ void foo(){} }

void func1(Iface x){ func2(x); }
void func2(Iface x){ func3(x); }
void func3(Iface x){ x.bar(); } // ERROR no bar in Iface

Only options here are A: update Iface to have bar() or B: make a new interface
and change it on the whole tree. The same "problem" would exist for the
concepts, but its the reason why people want it.

Sigh. Nothing I post here is understood.

Jul 24 2015
"Tofu Ninja" <emmons0 purdue.edu> writes:
On Saturday, 25 July 2015 at 00:49:38 UTC, Walter Bright wrote:
On 7/24/2015 2:27 PM, Tofu Ninja wrote:
No it isn't. Google QueryInterface(). Nobody lists all the
interfaces at the top level functions, which is that Rust
traits and C++ concepts require.

The only time you don't use the right interface for your needs is
if you plan on casting somewhere down the line. But certainly
there are people who don't do that, I for one feel it's bad
practice to need to use casts to circumvent the type system like
that.

Jul 24 2015
"Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Saturday, 25 July 2015 at 01:22:15 UTC, Tofu Ninja wrote:
On Saturday, 25 July 2015 at 00:49:38 UTC, Walter Bright wrote:
On 7/24/2015 2:27 PM, Tofu Ninja wrote:
No it isn't. Google QueryInterface(). Nobody lists all the
interfaces at the top level functions, which is that Rust
traits and C++ concepts require.

The only time you don't use the right interface for your needs
is if you plan on casting somewhere down the line. But
certainly there are people who don't do that, I for one feel
it's bad practice to need to use casts to circumvent the type
system like that.

I confess that I've always thought that QueryInterface was a
_horrible_ idea, and that if you need to cast your type to
something else like that, you're doing something wrong. *shudder*
I really have nothing good to say about COM actually...

- Jonathan M Davis

Jul 24 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 7/24/2015 7:28 PM, Jonathan M Davis wrote:
I confess that I've always thought that QueryInterface was a _horrible_ idea,

Specifying every interface that a type must support at the top of the hierarchy
is worse. Once again, Exception Specifications.

I suspect that 3 or 4 years after concepts and traits go into wide use, there's
going to be a quiet backlash against them. Where, once again, they'll be
implementing D's semantics. Heck, C++17 could just as well be renamed C++D :-)
given all the D senabtucs they're quietly adopting.

Jul 24 2015
"Tofu Ninja" <emmons0 purdue.edu> writes:
On Saturday, 25 July 2015 at 03:11:59 UTC, Walter Bright wrote:
On 7/24/2015 7:28 PM, Jonathan M Davis wrote:
I confess that I've always thought that QueryInterface was a
_horrible_ idea,

Specifying every interface that a type must support at the top
of the hierarchy is worse. Once again, Exception Specifications.

But that is what every one does.... you are really making me
"wut" right now? I never use casts and every thing works out
fine. And generally that is what is considered good oop practice.
Its not like there is a list at the top of the tree of all the
interfaces, there is just a type at the top of the tree that
implements them all, its not like Exception Specifications.

Jul 24 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 7/24/2015 8:43 PM, Tofu Ninja wrote:
there is just a type at the top of the tree that
implements them all

The one type that encompasses everything defeats the whole point of type
checking, traits, concepts, etc.

Jul 24 2015
Jacob Carlborg <doob me.com> writes:
On 2015-07-25 07:25, Walter Bright wrote:

The one type that encompasses everything defeats the whole point of type
checking, traits, concepts, etc.

Most methods only operate on a very specific set of data.

--
/Jacob Carlborg

Jul 25 2015
=?UTF-8?Q?Tobias=20M=C3=BCller?= <troplin bluewin.ch> writes:
Walter Bright <newshound2 digitalmars.com> wrote:
On 7/24/2015 8:43 PM, Tofu Ninja wrote:
there is just a type at the top of the tree that
implements them all

The one type that encompasses everything defeats the whole point of type
checking, traits, concepts, etc.

That's exactly my feeling with dynamic casts / QueryInterface... Pass
'object' everywhere and then cast it to the desired interface in demand.

Tobi

Jul 25 2015
"Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Saturday, 25 July 2015 at 03:11:59 UTC, Walter Bright wrote:
On 7/24/2015 7:28 PM, Jonathan M Davis wrote:
I confess that I've always thought that QueryInterface was a
_horrible_ idea,

Specifying every interface that a type must support at the top
of the hierarchy is worse. Once again, Exception Specifications.

Well, in most code, I would think that you should be getting the
actual object with its full type and converting that to an
interface to use for whatever you're using rather than trying to
convert something that's one interface into another. It usually
doesn't even make sense to attempt it. So, the only place that
has all of the functions is the actual class which _has_ to have
every function that it implements. I certainly wouldn't argue for
trying to combine the interfaces themselves in most cases, since
they're usually distinct, and combining them wouldn't make sense.
But similarly, it doesn't usually make sense to convert on
interface to a totally distinct one and have any expectation that
that conversion is going to work, because they're distinct.

Most code I've dealt with that is at all clean doesn't cast from
a base type to a derived type except in rare circumstances, and
converting across the interface hierarchy never happens.

I've never seen QueryInterface used in a way that I wouldn't have
considered messy or simply an annoyance that you have to deal
with because you're dealing with COM and can't use pure C++ and
just implicitly cast from the derived type to the
interface/abstract type that a particular section of code is
using.

But maybe I'm just not looking at the problem the right way. I
don't know.

I suspect that 3 or 4 years after concepts and traits go into
wide use, there's going to be a quiet backlash against them.
Where, once again, they'll be implementing D's semantics. Heck,
C++17 could just as well be renamed C++D :-) given all the D

Well, even if concepts _were_ where it was at, at least D
basically lets you implement them or do something far more lax or
ad hoc, because template constraints and static if give you a
_lot_ flexibility. We're not tied down in how we go about writing
template constraints or even in using the function level to
separate out functionality, because we can do that internally
with static if where appropriate. So, essentially, we're in a
great place regardless.

On the other hand, if we had built template constraints around
concepts or interfaces or anything like that, then our hands
would be tied. By making them accept any boolean expression
that's known at compile time, we have maximum flexibility. What
we do with them then becomes a matter of best practices.

The downside is that it's sometimes hard to decipher why a
template constraint is failing, and the compiler is less able to
help us with stuff like that, since it's not rigid like it would
be with a concept supported directly by the language, but the
sheer simplicity and flexibility of it makes it a major win
anyway IMHO.

- Jonathan M Davis

Jul 24 2015
"Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
On Saturday, 25 July 2015 at 05:29:39 UTC, Jonathan M Davis wrote:
Well, even if concepts _were_ where it was at, at least D
basically lets you implement them or do something far more lax
or ad hoc, because template constraints and static if give you
a _lot_ flexibility. We're not tied down in how we go about
writing template constraints or even in using the function
level to separate out functionality, because we can do that
internally with static if where appropriate. So, essentially,
we're in a great place regardless.

The point of having a type system is to catch as many mistakes at
compile time as possible. The primary purpose of a type system is
to reduce flexibility.

Jul 25 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 7/25/2015 12:19 AM, Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?=
The point of having a type system is to catch as many mistakes at compile time
as possible. The primary purpose of a type system is to reduce flexibility.

Again, the D constraint system *is* a compile time system, and if the template
body uses an interface not present in the type and not checked for in the
constraint, you will *still* get a compile time error.

The idea that Rust traits check at compile time and D does not is a total
misunderstanding.

BTW, you might want to remove the UTF-8 characters from your user name.
Evidently, NNTP doesn't do well with them.

Jul 25 2015
"Tofu Ninja" <emmons0 purdue.edu> writes:
On Saturday, 25 July 2015 at 09:40:52 UTC, Walter Bright wrote:
if the template body uses an interface not present in the type
and not checked for in the constraint, you will *still* get a
compile time error.

But only if the template gets instantiated with a bad type. Unit
tests don't catch every thing and have to be written properly. A
proper type system should catch it.

Jul 25 2015
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/25/15 6:05 AM, Tofu Ninja wrote:
On Saturday, 25 July 2015 at 09:40:52 UTC, Walter Bright wrote:
if the template body uses an interface not present in the type and not
checked for in the constraint, you will *still* get a compile time error.

But only if the template gets instantiated with a bad type. Unit tests
don't catch every thing and have to be written properly. A proper type
system should catch it.

I disagree. -- Andrei

Jul 25 2015
On Saturday, 25 July 2015 at 10:05:35 UTC, Tofu Ninja wrote:
On Saturday, 25 July 2015 at 09:40:52 UTC, Walter Bright wrote:
if the template body uses an interface not present in the type
and not checked for in the constraint, you will *still* get a
compile time error.

But only if the template gets instantiated with a bad type.
Unit tests don't catch every thing and have to be written
properly. A proper type system should catch it.

This unitest argument is becoming ridiculous. Unless some strong
argument is brought to the table that this differs from the
"dynamic typing is not a problem if you write unitest" we we all
should know is bogus at this point, it can't be taken seriously.

Jul 25 2015
"Tofu Ninja" <emmons0 purdue.edu> writes:
On Saturday, 25 July 2015 at 22:54:07 UTC, deadalnix wrote:
This unitest argument is becoming ridiculous. Unless some
strong argument is brought to the table that this differs from
the "dynamic typing is not a problem if you write unitest" we
we all should know is bogus at this point, it can't be taken
seriously.

+1000

Jul 25 2015
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/25/15 6:54 PM, deadalnix wrote:
On Saturday, 25 July 2015 at 10:05:35 UTC, Tofu Ninja wrote:
On Saturday, 25 July 2015 at 09:40:52 UTC, Walter Bright wrote:
if the template body uses an interface not present in the type and
not checked for in the constraint, you will *still* get a compile
time error.

But only if the template gets instantiated with a bad type. Unit tests
don't catch every thing and have to be written properly. A proper type
system should catch it.

This unitest argument is becoming ridiculous. Unless some strong
argument is brought to the table that this differs from the "dynamic
typing is not a problem if you write unitest" we we all should know is
bogus at this point, it can't be taken seriously.

To me that's self understood. Run time is fundamentally different from
everything preceding it. -- Andrei

Jul 26 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 7/26/2015 8:20 AM, Andrei Alexandrescu wrote:
On 7/25/15 6:54 PM, deadalnix wrote:
On Saturday, 25 July 2015 at 10:05:35 UTC, Tofu Ninja wrote:
On Saturday, 25 July 2015 at 09:40:52 UTC, Walter Bright wrote:
if the template body uses an interface not present in the type and
not checked for in the constraint, you will *still* get a compile
time error.

But only if the template gets instantiated with a bad type. Unit tests
don't catch every thing and have to be written properly. A proper type
system should catch it.

This unitest argument is becoming ridiculous. Unless some strong
argument is brought to the table that this differs from the "dynamic
typing is not a problem if you write unitest" we we all should know is
bogus at this point, it can't be taken seriously.

To me that's self understood. Run time is fundamentally different from
everything preceding it. -- Andrei

Unit tests are also not exclusively about runtime. Using a unit test to
instantiate a template is a compile time test.

Jul 26 2015
On Sunday, 26 July 2015 at 19:54:28 UTC, Walter Bright wrote:
Unit tests are also not exclusively about runtime. Using a unit
test to instantiate a template is a compile time test.

Yes, it test the instantiation to some extent. It tests that the
instantiate works granted you pass it what is expected.

It does not test that the instantiation will fail if you pass it
anything else, or worse, do random unexpected stuff.

The problem is the exact same as for dynamic typing and unitests.
A dynamically typed function that expect a string can be test
exhaustively with warious string passed as arguments. Still, none
knows what happen when passed an int, float, array, object or
whatever. Worse, it is either going to blow up in some unexpected
way or not explode and do random stuff.

The same way, instantiate your template with something it doesn't
expect and you get absurdly complex errors (and it is getting
worse as phobos get more and more "enterprise" with Foo
forwarding to FooImpl and 25 different helpers).

The problem is the same, will it fail at call site/instanciation
site with "I expected X you gave me Y" or will it fail randomly
somewhere down the road in some unrelated part of the code, or
worse, not fail at all when it should have ?

Jul 26 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 7/26/2015 3:53 PM, deadalnix wrote:
On Sunday, 26 July 2015 at 19:54:28 UTC, Walter Bright wrote:
Unit tests are also not exclusively about runtime. Using a unit test to
instantiate a template is a compile time test.

Yes, it test the instantiation to some extent. It tests that the instantiate
works granted you pass it what is expected.

It does not test that the instantiation will fail if you pass it anything else,
or worse, do random unexpected stuff.

If the template constraint is 'isInputRange', and you pass it an 'InputRange'
that is nothing beyond an input range, and it compiles, it is JUST AS GOOD as
Rust traits, without needing to add 'isInputRange' to every template up the
call
tree.

In fact, I do just this in my D dev projects. I have a set of mock ranges that
match each of the range types.

The problem is the exact same as for dynamic typing and unitests. A dynamically
typed function that expect a string can be test exhaustively with warious
string
passed as arguments. Still, none knows what happen when passed an int, float,
array, object or whatever. Worse, it is either going to blow up in some
unexpected way or not explode and do random stuff.

Flatly no, it is not at all the same. Dynamic typing systems do not have
constraints. Furthermore, dynamic typed languages tend to do random s**t when
presented with the wrong type rather than fail. (Such as concatenating strings
when the code was intended to do an arithmetic add.) D does not, it presents
the
user with a compilation error.

The same way, instantiate your template with something it doesn't expect and
you
get absurdly complex errors (and it is getting worse as phobos get more and
more
"enterprise" with Foo forwarding to FooImpl and 25 different helpers).

This is incorrect. In my D project development, when I send the wrong thing, I
get a list of the template instantiation stack. The bottom gives the method not
found, and the stack gives how it got there. I find it adequate in that it
doesn't take much effort to figure out where things went wrong.

BTW, when you discover that a constraint is wrong on a Phobos template, please
file a bug report on it.

The problem is the same, will it fail at call site/instanciation site with "I
expected X you gave me Y" or will it fail randomly somewhere down the road in
some unrelated part of the code, or worse, not fail at all when it should have
?

If the constraint is InputRange, and the body assumes ForwardRange, and I pass
it a ForwardRange, and it works, how is that 'worse'?

Jul 26 2015
On Sunday, 26 July 2015 at 23:21:50 UTC, Walter Bright wrote:
If the template constraint is 'isInputRange', and you pass it
an 'InputRange' that is nothing beyond an input range, and it
compiles, it is JUST AS GOOD as Rust traits, without needing to
add 'isInputRange' to every template up the call tree.

In fact, I do just this in my D dev projects. I have a set of
mock ranges that match each of the range types.

That is not enough, it has been covered. It is clear that this is
what needs to be done for good testing. It is also clear and
explained in this thread that this is not enough.

The problem is the exact same as for dynamic typing and
unitests. A dynamically
typed function that expect a string can be test exhaustively
with warious string
passed as arguments. Still, none knows what happen when passed
an int, float,
array, object or whatever. Worse, it is either going to blow
up in some
unexpected way or not explode and do random stuff.

Flatly no, it is not at all the same. Dynamic typing systems do
not have constraints. Furthermore, dynamic typed languages tend
to do random s**t when presented with the wrong type rather
than fail. (Such as concatenating strings when the code was
intended to do an arithmetic add.) D does not, it presents the
user with a compilation error.

else. For the precise example, concatenate with + is know to be
bad in dynamic typing for this very reason. Even PHP got around
that trap and pretty much only javascript got into that road.
You'll find noone defneding this outside the Node.js crowd, but
once you start thinking that a monothreaded event loop is the
definition of scalable, you are already lost for science anyway.

But more generally there is a point. Dynamic languages tends to
do random shit rather than failing. Hopefully, some won't. For
instance python will fail hard rather than do random stuff when
passed the wrong type.

If that was the problem faced, then using a type system would be
pointless. Using something like python is sufficient.

The same way, instantiate your template with something it
doesn't expect and you
get absurdly complex errors (and it is getting worse as phobos
get more and more
"enterprise" with Foo forwarding to FooImpl and 25 different
helpers).

This is incorrect. In my D project development, when I send the
wrong thing, I get a list of the template instantiation stack.

Which is often 3 pages long.

The bottom gives the method not found, and the stack gives how
it got there. I find it adequate in that it doesn't take much
effort to figure out where things went wrong.

That is learned helplessness. This is bad and we should feel bad
to propose such a user experience. There are reasons why template
are frowned upon by many devs, and this is one of them.

I even heard Andrei suggest at a conf to pipe the output of the
compiler to head (he was talking about C++, but D suffer from the
same problem in that regard). Yes it works, and yes you can
eventually make sens of the error, but that is terrible user
experience.

BTW, when you discover that a constraint is wrong on a Phobos
template, please file a bug report on it.

It is not just phobos, but everybody's code. Why all this need to
be done manually when the compiler could do it for us ?

Isn't it why we use computer in the first place ?

If I follow your post, one have to maintain a set of minimal mock
to test instantiations, a constraint for the template and a body,
the 3 of them requiring to be kept in synch over time while
nothing checks it is.

That is completely unmaintainable.

The problem is the same, will it fail at call
site/instanciation site with "I
expected X you gave me Y" or will it fail randomly somewhere
some unrelated part of the code, or worse, not fail at all
when it should have ?

If the constraint is InputRange, and the body assumes
ForwardRange, and I pass it a ForwardRange, and it works, how
is that 'worse'?

Maybe now my complexity is quadratic ? Maybe it doesn't do what
it is supposed to do anymore, but else ?

Jul 27 2015
"Tofu Ninja" <emmons0 purdue.edu> writes:
On Monday, 27 July 2015 at 19:11:53 UTC, deadalnix wrote:
That is completely unmaintainable.

I really don't get how the mess of unittests, mock data types,
template constraints, and type interfaces that are just
convention(ranges are just a convention, they don't exist
anywhere) is supposed to some how be more maintainable than a
single system that can serve the function of all of them and
forces them all to be in sync 100% of the time. Seriously...

Jul 27 2015
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/27/15 3:35 PM, Tofu Ninja wrote:
On Monday, 27 July 2015 at 19:11:53 UTC, deadalnix wrote:
That is completely unmaintainable.

I really don't get how the mess of unittests, mock data types, template
constraints, and type interfaces that are just convention(ranges are
just a convention, they don't exist anywhere) is supposed to some how be
more maintainable than a single system that can serve the function of
all of them and forces them all to be in sync 100% of the time.
Seriously...

Clearly adding more language features to D would have certain payoff.
But we should not just go ahead and pull today's hot topic, which really
does change very often.

As the language's main proponents, we should be more inclined toward
using the language that we have creatively for solving interesting
tasks, instead of keeping on wishing that just one more feature would
make it perfect. The lure of the eternally-open design stage is very
powerful (I basked in its glow a number of times), but it inevitably
there's always contemplating how the language design could be tweaked to
better accommodate the task at hand.

The withdrawal is unpleasant, I know. But we must acknowledge that the
design of D is done. The large stones and pillars we have in place are
there to stay, and D shall not be radically different five years from
now. We need to acknowledge that a lot of grass seems greener on the
other side (and some really is), but we need to make do with the
gardening tools we got.

I'll do my best to limit my participation in emotional debates, and
suggest other D luminaries to do the same. We should put strong emphasis
on finalizing the definition and implementation of the language we have
(those imprecisions and fuzzy corners are really counterproductive),
forge forward to do great work with D, and show others how it's done.

Thanks,

Andrei

Jul 27 2015
"Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Monday, 27 July 2015 at 20:49:54 UTC, Andrei Alexandrescu
wrote:
I'll do my best to limit my participation in emotional debates,
and suggest other D luminaries to do the same.

LOL. That's why I was originally planning to not say anything in

- Jonathan M Davis

Jul 27 2015
"jmh530" <john.michael.hall gmail.com> writes:
On Monday, 27 July 2015 at 21:54:23 UTC, Jonathan M Davis wrote:
On Monday, 27 July 2015 at 20:49:54 UTC, Andrei Alexandrescu
wrote:
I'll do my best to limit my participation in emotional
debates, and suggest other D luminaries to do the same.

LOL. That's why I was originally planning to not say anything

- Jonathan M Davis

Your comments were very clear and much appreciated, but I see the
point.

Jul 27 2015
"Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Monday, 27 July 2015 at 22:47:05 UTC, jmh530 wrote:
On Monday, 27 July 2015 at 21:54:23 UTC, Jonathan M Davis wrote:
On Monday, 27 July 2015 at 20:49:54 UTC, Andrei Alexandrescu
wrote:
I'll do my best to limit my participation in emotional
debates, and suggest other D luminaries to do the same.

LOL. That's why I was originally planning to not say anything

- Jonathan M Davis

the point.

Well, I ended up commenting, because there were some very import
points relevant to what we do with D that needed clarifying. What
I wanted to avoid (and mostly did) was arguing over Rust vs D.
For instance, I'd hate to lose the ternary operator in favor of
the expression if-else blocks that were being suggested, but
there's no point in arguing over it, because we're not going to
lose the the ternary operator, and we're not going to make it so
that if-else blocks can be used as expressions. Arguing about it
at this point just creates contention. And it's far too easy to
come at that sort of discussion from an emotional point of view
that D is better because I really like it and am invested in it,
and what's being suggested is alien to me or doesn't fit with my
aesthetics or whatever. Sometimes what another language has _is_
better, but often, it's a trade-off or even completely
subjective, and regardless, it generally isn't going to have any
effect on D at this point. Rather, it's just going to make
emotions flare. So, at this point, I'd prefer to generally avoid
discussions of D vs any other language. I got into a really nasty
argument about ranges vs iterators the other day on reddit, and I
just don't want to be doing that sort of thing anymore.

However, what we _do_ stand to learn from is what's work welling
for other languages (like Rust) in terms of process and the
things that they do that don't necessarily have to do with the
language itself which help them succeed, particularly, since even
though we're generally pretty strong on the language front (not
perfect, but we definitely have a very strong offering), we tend
to have problems with marketing, getting folks to contribute,
getting those contributions merged in in a timely manner, getting
releases out, etc. We've definitely improved on that front, but
it's probably our weakest point, whereas the language itself is
pretty fantastic.

But I would like to avoid arguments over which language is better
or which feature in which language is better or anything like
that, particularly since we're unlikely to add anything to D at
this point because of such discussions. Rather, we need to finish
what we have and make it solid.

- Jonathan M Davis

Jul 27 2015
"Chris" <wendlec tcd.ie> writes:
On Tuesday, 28 July 2015 at 05:49:40 UTC, Jonathan M Davis wrote:
On Monday, 27 July 2015 at 22:47:05 UTC, jmh530 wrote:
On Monday, 27 July 2015 at 21:54:23 UTC, Jonathan M Davis
wrote:
On Monday, 27 July 2015 at 20:49:54 UTC, Andrei Alexandrescu
wrote:
I'll do my best to limit my participation in emotional
debates, and suggest other D luminaries to do the same.

LOL. That's why I was originally planning to not say anything

- Jonathan M Davis

the point.

Well, I ended up commenting, because there were some very
import points relevant to what we do with D that needed
clarifying. What I wanted to avoid (and mostly did) was arguing
over Rust vs D. For instance, I'd hate to lose the ternary
operator in favor of the expression if-else blocks that were
being suggested, but there's no point in arguing over it,
because we're not going to lose the the ternary operator, and
we're not going to make it so that if-else blocks can be used
as expressions. Arguing about it at this point just creates
contention. And it's far too easy to come at that sort of
discussion from an emotional point of view that D is better
because I really like it and am invested in it, and what's
being suggested is alien to me or doesn't fit with my
aesthetics or whatever. Sometimes what another language has
_is_ better, but often, it's a trade-off or even completely
subjective, and regardless, it generally isn't going to have
any effect on D at this point. Rather, it's just going to make
emotions flare. So, at this point, I'd prefer to generally
avoid discussions of D vs any other language. I got into a
really nasty argument about ranges vs iterators the other day
on reddit, and I just don't want to be doing that sort of thing
anymore.

However, what we _do_ stand to learn from is what's work
welling for other languages (like Rust) in terms of process and
the things that they do that don't necessarily have to do with
the language itself which help them succeed, particularly,
since even though we're generally pretty strong on the language
front (not perfect, but we definitely have a very strong
offering), we tend to have problems with marketing, getting
folks to contribute, getting those contributions merged in in a
timely manner, getting releases out, etc. We've definitely
improved on that front, but it's probably our weakest point,
whereas the language itself is pretty fantastic.

But I would like to avoid arguments over which language is
better or which feature in which language is better or anything
like that, particularly since we're unlikely to add anything to
D at this point because of such discussions. Rather, we need to
finish what we have and make it solid.

- Jonathan M Davis

Very wise. More often than not it is useless and irrelevant to
discuss features other languages have. This does not mean that we
shouldn't get inspiration from other languages. However, D's
reality is different from Rust's or Go's and features don't
necessarily translate directly into D (or even make sense in D).
Another thing is, as I pointed out earlier, that a lot of "new"
features other languages have are not yet tested well enough to
be able to say whether or not they are really that good[1]. After
all we are still experimenting with features and fixing things
here and there after using them in the real world.

There is a tendency to think that any feature we don't have
absolutely soooo has to be incorporated, else D will never take
off. I beg to differ. The question should not be "Why doesn't D
have this feature?", but "How do I get the same effect in D?".
Often we don't have to introduce a new feature, we merely have to
use the tools we have to get the same effect. And we do have a
lot of tools.

[1] I wonder what kind of bugs will be introduced, when if-else
is used as an expression.

Jul 28 2015
"Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
On Tuesday, 28 July 2015 at 09:29:28 UTC, Chris wrote:
[1] I wonder what kind of bugs will be introduced, when if-else
is used as an expression.

I believe most Algol-like languages outside the C-family have
it...

Jul 29 2015
"Chris" <wendlec tcd.ie> writes:
On Thursday, 30 July 2015 at 02:30:45 UTC, Ola Fosheim Grøstad
wrote:
On Tuesday, 28 July 2015 at 09:29:28 UTC, Chris wrote:
[1] I wonder what kind of bugs will be introduced, when
if-else is used as an expression.

I believe most Algol-like languages outside the C-family have
it...

So can you tell me what pitfalls there are? Sure people must have
come across some nasty bugs related to this.

Jul 30 2015
Timon Gehr <timon.gehr gmx.ch> writes:
On 07/30/2015 11:25 AM, Chris wrote:
On Thursday, 30 July 2015 at 02:30:45 UTC, Ola Fosheim Grøstad wrote:
On Tuesday, 28 July 2015 at 09:29:28 UTC, Chris wrote:
[1] I wonder what kind of bugs will be introduced, when if-else is
used as an expression.

I believe most Algol-like languages outside the C-family have it...

So can you tell me what pitfalls there are?

What kind of special pitfall do you envision here?

Sure people must have come across some nasty bugs related to this.

They are the intersection of nasty bugs involving ?: and nasty bugs
involving if/else statements.

Jul 30 2015
"Chris" <wendlec tcd.ie> writes:
On Thursday, 30 July 2015 at 13:32:29 UTC, Timon Gehr wrote:
On 07/30/2015 11:25 AM, Chris wrote:
On Thursday, 30 July 2015 at 02:30:45 UTC, Ola Fosheim Grøstad
wrote:
On Tuesday, 28 July 2015 at 09:29:28 UTC, Chris wrote:
[1] I wonder what kind of bugs will be introduced, when
if-else is
used as an expression.

I believe most Algol-like languages outside the C-family have
it...

So can you tell me what pitfalls there are?

What kind of special pitfall do you envision here?

Sure people must have come across some nasty bugs related to
this.

They are the intersection of nasty bugs involving ?: and nasty
bugs involving if/else statements.

My point was that any (new) feature introduces its own problems.
Be it "everything is a statement" or built-in "bug prevention"
(rigid features). While preventing certain types of bugs, new
types may be introduced by features that have been introduced to
prevent old bugs. It would be foolish to believe that most bugs
will be erased, if only a language is rigid enough. As I said,
I'll wait and see what Rust users have to say after a year or two.

Jul 30 2015
Ziad Hatahet via Digitalmars-d <digitalmars-d puremagic.com> writes:
On Thu, Jul 30, 2015 at 7:19 AM, Chris via Digitalmars-d <
digitalmars-d puremagic.com> wrote:

My point was that any (new) feature introduces its own problems... As I
said, I'll wait and see what Rust users have to say after a year or two.

Except, as it was pointed out, this is not a new feature. It has been
around in many languages way before Rust.

You don't have to wait a year or two, check what the experience of users of
languages such as Scala, Haskell, F#, and OCaml has been like.

Jul 31 2015
"Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Friday, 31 July 2015 at 09:08:00 UTC, Ziad Hatahet wrote:
On Thu, Jul 30, 2015 at 7:19 AM, Chris via Digitalmars-d <
digitalmars-d puremagic.com> wrote:

My point was that any (new) feature introduces its own
problems... As I said, I'll wait and see what Rust users have
to say after a year or two.

Except, as it was pointed out, this is not a new feature. It
has been around in many languages way before Rust.

You don't have to wait a year or two, check what the experience
of users of languages such as Scala, Haskell, F#, and OCaml has
been like.

It really doesn't mean much if you're talking about functional
languages, because they're fundamentally different from
imperative languages in how they're constructed. Almost
everything in functional languages is an expression, and it works
fine for them, but the way that code is written in those
languages is also fundamentally different from how you'd write it
in C, C++, Java, C#, D, etc. As I understand it, Rust is much
closer to C++ and friends than a functional language, so how it
interacts with the rest of the language is going to be quite
different. That doesn't mean that it won't work just fine, but it
does mean that the fact that it works fine in functional
languages doesn't necessarily mean that it'll work well for Rust.

Now, there may be other imperative languages which have something
similar - be it that all statements are expressions or that a
larger subset of them are - so there may already be one or more
languages out there which show that it can work just fine with an
imperative language, but AFAIK, all of the languages you listed
are either outright functional languages or lean heavily in that
direction rather than being imperative. So, I don't think that
experiences with those languages necessarily says much about how
well it will work for Rust.

- Jonathan M Davis

Jul 31 2015
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/26/15 3:54 PM, Walter Bright wrote:
On 7/26/2015 8:20 AM, Andrei Alexandrescu wrote:
On 7/25/15 6:54 PM, deadalnix wrote:
On Saturday, 25 July 2015 at 10:05:35 UTC, Tofu Ninja wrote:
On Saturday, 25 July 2015 at 09:40:52 UTC, Walter Bright wrote:
if the template body uses an interface not present in the type and
not checked for in the constraint, you will *still* get a compile
time error.

But only if the template gets instantiated with a bad type. Unit tests
don't catch every thing and have to be written properly. A proper type
system should catch it.

This unitest argument is becoming ridiculous. Unless some strong
argument is brought to the table that this differs from the "dynamic
typing is not a problem if you write unitest" we we all should know is
bogus at this point, it can't be taken seriously.

To me that's self understood. Run time is fundamentally different from
everything preceding it. -- Andrei

Unit tests are also not exclusively about runtime. Using a unit test to
instantiate a template is a compile time test.

YES! For templates unittests have a dual role. -- Andrei

Jul 26 2015
Russel Winder via Digitalmars-d <digitalmars-d puremagic.com> writes:
On Sat, 2015-07-25 at 02:40 -0700, Walter Bright via Digitalmars-d
wrote:
=20

[[=E2=80=A6]
BTW, you might want to remove the UTF-8 characters from your user=20
name.=20
Evidently, NNTP doesn't do well with them.

Conversely someone should fix the NNTP implementation to deal properly
with UTF-8 encoded Unicode codepoints.
=20
--=20
Russel.
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D
Dr Russel Winder      t: +44 20 7585 2200   voip: sip:russel.winder ekiga.n=
et
41 Buckmaster Road    m: +44 7770 465 077   xmpp: russel winder.org.uk
London SW11 1EN, UK   w: www.russel.org.uk  skype: russel_winder

Jul 25 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 7/25/2015 3:30 AM, Russel Winder via Digitalmars-d wrote:
Conversely someone should fix the NNTP implementation to deal properly
with UTF-8 encoded Unicode codepoints.

I propose that you do it!

Jul 25 2015
"Brendan Zabarauskas" <bjzaba yahoo.com.au> writes:
On Saturday, 25 July 2015 at 09:40:52 UTC, Walter Bright wrote:
On 7/25/2015 12:19 AM, Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?=
The point of having a type system is to catch as many mistakes
at compile time
as possible. The primary purpose of a type system is to reduce
flexibility.

Again, the D constraint system *is* a compile time system, and
if the template body uses an interface not present in the type
and not checked for in the constraint, you will *still* get a
compile time error.

The idea that Rust traits check at compile time and D does not
is a total misunderstanding.

BTW, you might want to remove the UTF-8 characters from your
user name. Evidently, NNTP doesn't do well with them.

I think the point is that trait based constraints force
compilation errors to be raised at the call site, and not
potentially from deep within a template expansion. Template
errors are stack traces coming from duck typed, compile time
programs. Library authors can't rely on the typechecker to pick
up on mistakes that may only appear at expansion time in client
programs.

Jul 25 2015
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/25/15 9:17 AM, Brendan Zabarauskas wrote:
On Saturday, 25 July 2015 at 09:40:52 UTC, Walter Bright wrote:
On 7/25/2015 12:19 AM, Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?=
The point of having a type system is to catch as many mistakes at
compile time
as possible. The primary purpose of a type system is to reduce
flexibility.

Again, the D constraint system *is* a compile time system, and if the
template body uses an interface not present in the type and not
checked for in the constraint, you will *still* get a compile time error.

The idea that Rust traits check at compile time and D does not is a
total misunderstanding.

BTW, you might want to remove the UTF-8 characters from your user
name. Evidently, NNTP doesn't do well with them.

I think the point is that trait based constraints force compilation
errors to be raised at the call site, and not potentially from deep
within a template expansion. Template errors are stack traces coming
from duck typed, compile time programs. Library authors can't rely on
the typechecker to pick up on mistakes that may only appear at expansion
time in client programs.

Understood, but by the same token library authors shouldn't ship
untested code. This is basic software engineering. Once we agree on
that, we figure that concepts help nobody. -- Andrei

Jul 25 2015
=?UTF-8?Q?Tobias=20M=C3=BCller?= <troplin bluewin.ch> writes:
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
Once we agree on that, we figure that concepts help nobody.

You keep saying that, but I cannot find an explanation. Care to elaborate
or give me a pointer?

Tobi

Jul 25 2015
On Saturday, 25 July 2015 at 13:59:11 UTC, Andrei Alexandrescu
wrote:
Understood, but by the same token library authors shouldn't
ship untested code. This is basic software engineering. Once we
agree on that, we figure that concepts help nobody. -- Andrei

Understood, but by the same token library authors shouldn't ship
untested code. This is basic software engineering. Once we agree
on that, we figure that [type system|grizzly|unicorns] help
nobody.

That is a statement, not an argument.

Jul 25 2015
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/25/15 7:14 PM, deadalnix wrote:
On Saturday, 25 July 2015 at 13:59:11 UTC, Andrei Alexandrescu wrote:
Understood, but by the same token library authors shouldn't ship
untested code. This is basic software engineering. Once we agree on
that, we figure that concepts help nobody. -- Andrei

Understood, but by the same token library authors shouldn't ship
untested code. This is basic software engineering. Once we agree on
that, we figure that [type system|grizzly|unicorns] help nobody.

That is a statement, not an argument.

Well then don't clip the context. -- Andrei

Jul 26 2015
"Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
On Saturday, 25 July 2015 at 09:40:52 UTC, Walter Bright wrote:
On 7/25/2015 12:19 AM, Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?=
The point of having a type system is to catch as many mistakes
at compile time
as possible. The primary purpose of a type system is to reduce
flexibility.

Again, the D constraint system *is* a compile time system, and
if the template body uses an interface not present in the type
and not checked for in the constraint, you will *still* get a
compile time error.

Well, I am not sure if the flexibility scales up when clever
library authors start to write flexible introspective code. It
basically requires library authors to be careful and conservative.

Code coverage and unit tests cannot replace a robust type system
when you get down to composable datastructures due to the
combinatorial explosion you get.

The idea that Rust traits check at compile time and D does not
is a total misunderstanding.

I'm not arguing in favour of copying Rust… I don't think becoming
more like Rust will buy D more friends. It will just be an
argument for picking Rust over D.

If I'd argue for something it would be for having a real
deductive database at the heart of the templating type system.

BTW, you might want to remove the UTF-8 characters from your
user name. Evidently, NNTP doesn't do well with them.

Hm. It works in the web interface when I reply to my own
messages, maybe just a client issue?

Jul 25 2015
=?UTF-8?B?SsOpcsO0bWUgTS4gQmVyZ2Vy?= <jeberger free.fr> writes:
On 07/25/2015 05:03 PM, Ola Fosheim =3D?UTF-8?B?R3LDuHN0YWQi?=3D
On Saturday, 25 July 2015 at 09:40:52 UTC, Walter Bright wrote:
BTW, you might want to remove the UTF-8 characters from your=20
user name. Evidently, NNTP doesn't do well with them.

=20
Hm. It works in the web interface when I reply to my own=20
messages, maybe just a client issue?
=20

I'd say it is a problem with the way the web interface encodes the
sender name, and especially the fact that it starts with a double quote.
In the message source, it looks like:

From: "Ola Fosheim =3D?UTF-8?B?R3LDuHN0YWQi?=3D

According to RFC 2047 [1]: "An 'encoded-word' MUST NOT appear within
a 'quoted-string'." (top of page 7), so this should be written as:

From: Ola Fosheim =3D?UTF-8?B?R3LDuHN0YWQ=3D?=3D

Jerome

[1] https://tools.ietf.org/html/rfc2047
--=20
mailto:jeberger free.fr
http://jeberger.free.fr
Jabber: jeberger jabber.fr

Jul 25 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 7/25/2015 8:03 AM, Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?=
BTW, you might want to remove the UTF-8 characters from your user name.
Evidently, NNTP doesn't do well with them.

Hm. It works in the web interface when I reply to my own messages, maybe just a
client issue?

Looking at the raw text of your posting, it contains:

From: "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?=

I don't know where that comes from, but it is not coming from my NNTP client
(Thunderbird).

Jul 25 2015
=?UTF-8?Q?Tobias=20M=C3=BCller?= <troplin bluewin.ch> writes:
Walter Bright <newshound2 digitalmars.com> wrote:
BTW, you might want to remove the UTF-8 characters from your user name.
Evidently, NNTP doesn't do well with them.

I don't think NNTP has any problems with that. My newsreader displays it
just fine.

Tobi

Jul 25 2015
On Saturday, 25 July 2015 at 09:40:52 UTC, Walter Bright wrote:
On 7/25/2015 12:19 AM, Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?=
The point of having a type system is to catch as many mistakes
at compile time
as possible. The primary purpose of a type system is to reduce
flexibility.

Again, the D constraint system *is* a compile time system, and
if the template body uses an interface not present in the type
and not checked for in the constraint, you will *still* get a
compile time error.

The idea that Rust traits check at compile time and D does not
is a total misunderstanding.

Obvious, everything is at compile time here. Still, there is 2
steps, compiling the template (equivalent to compile time in the
dynamic dispatch case), and instantiating the template
(equivalent to runtime in the dynamic dispatch case).

That is the exact same problem.

Jul 25 2015
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/25/15 6:48 PM, deadalnix wrote:
On Saturday, 25 July 2015 at 09:40:52 UTC, Walter Bright wrote:
On 7/25/2015 12:19 AM, Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?=
The point of having a type system is to catch as many mistakes at
compile time
as possible. The primary purpose of a type system is to reduce
flexibility.

Again, the D constraint system *is* a compile time system, and if the
template body uses an interface not present in the type and not
checked for in the constraint, you will *still* get a compile time error.

The idea that Rust traits check at compile time and D does not is a
total misunderstanding.

Obvious, everything is at compile time here. Still, there is 2 steps,
compiling the template (equivalent to compile time in the dynamic
dispatch case), and instantiating the template (equivalent to runtime in
the dynamic dispatch case).

That is the exact same problem.

Probably that's the root of all disagreement. So we have template
writing time, template instantiation time, and just run time. I think
template instantiation time is a lot "closer" to template writing time
than run time. -- Andrei

Jul 26 2015
On Sunday, 26 July 2015 at 15:19:06 UTC, Andrei Alexandrescu
wrote:
Probably that's the root of all disagreement. So we have
template writing time, template instantiation time, and just
run time. I think template instantiation time is a lot "closer"
to template writing time than run time. -- Andrei

It is closer, but it doesn't matter for the argument being made.

You have some code that expect its argument to conform to some
API. Be it dynamically typed code (which will blow up at runtime,
or worse, do random shit) or template code (which will blow up at
instanciation time, or worse, do random shit).

The problem being fundamentally the same, arguments going for or
against one go for the other.

Jul 26 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 7/26/2015 3:44 PM, deadalnix wrote:
or template code (which will blow up at instanciation time, or worse, do random
shit).

Um, all Rust traits do is test for a method signature match, so it compiles. It
is NOT a defense against a random method that just happens to match and does
some other unrelated random shit.

For example, the '+' operator. Rust traits sez "gee, there's a + operator, it's
good to go. Ship it!" Meanwhile, you thought the function was summing some
data,
when it actually is creating a giant string, or whatever idiot thing someone
decided to use '+' for.

Rust still has not obviated the necessity for unit tests, nor is Rust remotely
able to guarantee your code doesn't "do random shit" if it compiles.

Jul 26 2015
=?UTF-8?Q?Tobias=20M=C3=BCller?= <troplin bluewin.ch> writes:
Walter Bright <newshound2 digitalmars.com> wrote:
On 7/26/2015 3:44 PM, deadalnix wrote:
or template code (which will blow up at instanciation time, or worse, do random
shit).

Um, all Rust traits do is test for a method signature match, so it
compiles. It is NOT a defense against a random method that just happens
to match and does some other unrelated random shit.

Rust traits have to be implemented *explicitly*. It's not just an implicit
test for a matching signature.

For example, the '+' operator. Rust traits sez "gee, there's a +
operator, it's good to go. Ship it!" Meanwhile, you thought the function
was summing some data, when it actually is creating a giant string, or
whatever idiot thing someone decided to use '+' for.

+ operator is somewhat special because it can only be implemented via
trait. That doesn't apply for normal methods.

Rust still has not obviated the necessity for unit tests, nor is Rust
remotely able to guarantee your code doesn't "do random shit" if it compiles.

An example:
Rust std lib defines two traits, PartialOrd and Ord. Ord depends on
PartialOrd but doesn't provide any new methods.
And it's clearly documented when to implement Ord and when PartialOrd.
So sure, someone could decide to deliberately ignore that, but then I just
don't care anymore.

Tobi

Jul 27 2015
"Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
On Monday, 27 July 2015 at 07:21:36 UTC, Tobias Müller wrote:
Walter Bright <newshound2 digitalmars.com> wrote:
On 7/26/2015 3:44 PM, deadalnix wrote:
or template code (which will blow up at instanciation time,
or worse, do random
shit).

Um, all Rust traits do is test for a method signature match,
so it compiles. It is NOT a defense against a random method
that just happens to match and does some other unrelated
random shit.

Rust traits have to be implemented *explicitly*. It's not just
an implicit test for a matching signature.

Explicit is good, but D's problem is that it already have
numerous language concepts covering the same type of semantics:
classes, interfaces, alias this, template duck-typing, template
constraints…

C++ only have classes and template SFINAE duck-typing. Everything
else is just idioms or library constructs.

Adding yet another langauge level interface mechanism to D would
IMO require language redesign. Which is not a bad idea, but not
likely in the near term?

Jul 27 2015
Timon Gehr <timon.gehr gmx.ch> writes:
On 07/27/2015 01:29 AM, Walter Bright wrote:
On 7/26/2015 3:44 PM, deadalnix wrote:
or template code (which will blow up at instanciation time, or worse,
do random
shit).

Um, all Rust traits do is test for a method signature match, so it
compiles. It is NOT a defense against a random method that just happens
to match and does some other unrelated random shit.

You are describing Go interfaces, not Rust traits.

Jul 27 2015
"Max Samukha" <maxsamukha gmail.com> writes:
On Sunday, 26 July 2015 at 23:29:18 UTC, Walter Bright wrote:

For example, the '+' operator. Rust traits sez "gee, there's a
+ operator, it's good to go. Ship it!" Meanwhile, you thought
the function was summing some data, when it actually is
creating a giant string, or whatever idiot thing someone
decided to use '+' for.

Number addition and string concatenation are monoid operations.
In this light, '+' for both makes perfect sense.

Aug 02 2015
"Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Sunday, 2 August 2015 at 19:02:22 UTC, Max Samukha wrote:
On Sunday, 26 July 2015 at 23:29:18 UTC, Walter Bright wrote:

For example, the '+' operator. Rust traits sez "gee, there's a
+ operator, it's good to go. Ship it!" Meanwhile, you thought
the function was summing some data, when it actually is
creating a giant string, or whatever idiot thing someone
decided to use '+' for.

Number addition and string concatenation are monoid operations.
In this light, '+' for both makes perfect sense.

Well, using + for "adding" strings together does make sense on
some level, which is why it's done in so many languages, and I
don't think that it causes as much confusion as Walter sometimes
seems to think (at least in C/C++-derived languages). That being
said, I think that it's definitely an improvement that D has
another operator for it. It makes it clearer when concatenation
is occurring without having to figure out what types you're
dealing with, and it allows user-defined code to have both an
addition operator and a concatenation operator on the same type.

Where distinguishing between + and ~ would likely make a big
difference though is dynamic languages that aren't strict with
types and allow nonsense like "5" + 2. And in that case, I expect
that Walter is completely right. It's just error-prone to use +
for concatenation in cases like that, and providing a separate
concatenation operator would reduce bugs.

Regardless, I definitely like the fact that we have ~ and ~=
instead of reusing + and += for that. It's a small improvement,
but it is an improvement.

- Jonathan M Davis

Aug 02 2015
"Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
On Sunday, 2 August 2015 at 21:17:10 UTC, Jonathan M Davis wrote:
types you're dealing with, and it allows user-defined code to
have both an addition operator and a concatenation operator on
the same type.

I assume you mean vectors, though I would prefer binary "++" for
that.

Where distinguishing between + and ~ would likely make a big
difference though is dynamic languages that aren't strict with
types and allow nonsense like "5" + 2. And in that case, I
expect that Walter is completely right. It's just error-prone
to use + for concatenation in cases like that, and providing a
separate concatenation operator would reduce bugs.

I've never run into such bugs, have you? The ambigious case
would be "result:" + 3 + 8 , but you can solve this by giving
numeric plus higher precedence or avoid implicit conversion.
Though, these days it is more common to support something like
"result: {{3+8}}".

Regardless, I definitely like the fact that we have ~ and ~=
instead of reusing + and += for that. It's a small improvement,
but it is an improvement.

It's a weird thing to do for a C-decendant as I would expect "~="
to do binary negation.

Aug 02 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 8/2/2015 8:17 PM, Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?=
It's a weird thing to do for a C-decendant as I would expect "~=" to do binary
negation.

If you really felt this way, you'd expect the C != operator

a != b

to be the same as:

a = !b

Aug 05 2015
"Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
On Thursday, 6 August 2015 at 06:50:38 UTC, Walter Bright wrote:
On 8/2/2015 8:17 PM, Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?=
It's a weird thing to do for a C-decendant as I would expect

"~=" to do binary
negation.

If you really felt this way, you'd expect the C != operator

a != b

to be the same as:

a = !b

I don't because "!=" is frequently used and usually in a context
where expectations points towards comparison and not assignment.

But I would prefer "=", "≠","<" and "≤" for comparison and
constants... then have something else for variable assignment.

Aug 06 2015
"Idan Arye" <GenericNPC gmail.com> writes:
On Thursday, 6 August 2015 at 11:26:10 UTC, Ola Fosheim Grøstad
wrote:
On Thursday, 6 August 2015 at 06:50:38 UTC, Walter Bright wrote:
On 8/2/2015 8:17 PM, Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?=
It's a weird thing to do for a C-decendant as I would expect

"~=" to do binary
negation.

If you really felt this way, you'd expect the C != operator

a != b

to be the same as:

a = !b

I don't because "!=" is frequently used and usually in a
context where expectations points towards comparison and not
assignment.

But I would prefer "=", "≠","<" and "≤" for comparison and
constants... then have something else for variable assignment.

go well?

Aug 06 2015
"Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
On Thursday, 6 August 2015 at 11:30:45 UTC, Idan Arye wrote:
didn't go well?

There is no good reason to avoid unicode operators these days. A
language without a dedicated editor-mode is pretty much DOA.

Aug 06 2015
"Kagamin" <spam here.lot> writes:
On Thursday, 6 August 2015 at 11:33:29 UTC, Ola Fosheim Grøstad
wrote:
DOA.

http://www.acronymfinder.com/DOA.html (Degenerate Overclockers
Anonymous?)

Aug 06 2015
"Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
On Thursday, 6 August 2015 at 12:33:33 UTC, Kagamin wrote:
On Thursday, 6 August 2015 at 11:33:29 UTC, Ola Fosheim Grøstad
wrote:
DOA.

http://www.acronymfinder.com/DOA.html (Degenerate Overclockers
Anonymous?)


Aug 06 2015
"burjui" <bytefu gmail.com> writes:
On Thursday, 6 August 2015 at 12:33:33 UTC, Kagamin wrote:
On Thursday, 6 August 2015 at 11:33:29 UTC, Ola Fosheim Grøstad
wrote:
DOA.

http://www.acronymfinder.com/DOA.html (Degenerate Overclockers
Anonymous?)

http://www.urbandictionary.com/define.php?term=DOA
Without this great site it would often be hard to understand what
people from USA are talking about.

Aug 06 2015
"rsw0x" <anonymous anonymous.com> writes:
On Thursday, 6 August 2015 at 11:30:45 UTC, Idan Arye wrote:
On Thursday, 6 August 2015 at 11:26:10 UTC, Ola Fosheim Grøstad
wrote:
On Thursday, 6 August 2015 at 06:50:38 UTC, Walter Bright
wrote:
On 8/2/2015 8:17 PM, Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?=
[...]

"~=" to do binary
[...]

If you really felt this way, you'd expect the C != operator

a != b

to be the same as:

a = !b

I don't because "!=" is frequently used and usually in a
context where expectations points towards comparison and not
assignment.

But I would prefer "=", "≠","<" and "≤" for comparison and
constants... then have something else for variable assignment.

didn't go well?

Compose keys have existed for a long time.
The aversion to unicode is ridiculous.

Aug 06 2015
"Chris" <wendlec tcd.ie> writes:
On Thursday, 6 August 2015 at 12:56:05 UTC, rsw0x wrote:
On Thursday, 6 August 2015 at 11:30:45 UTC, Idan Arye wrote:
On Thursday, 6 August 2015 at 11:26:10 UTC, Ola Fosheim
On Thursday, 6 August 2015 at 06:50:38 UTC, Walter Bright
wrote:
On 8/2/2015 8:17 PM, Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?=
[...]

"~=" to do binary
[...]

If you really felt this way, you'd expect the C != operator

a != b

to be the same as:

a = !b

I don't because "!=" is frequently used and usually in a
context where expectations points towards comparison and not
assignment.

But I would prefer "=", "≠","<" and "≤" for comparison and
constants... then have something else for variable assignment.

didn't go well?

Compose keys have existed for a long time.
The aversion to unicode is ridiculous.

That's because everything in IT is Anglo-centric (mainly US). To
this day we suffer from the fact that nobody in the English
speaking world bothered to cater for "special characters"[1],
when computers and programming languages emerged as ever more
important.

[1] the term "special character" tells a lot about the attitude.
For French or Portuguese speakers "ç" is not a "special
character" nor is "ñ" for Spanish speakers (not to mention other
writing systems!).

Aug 06 2015
Timon Gehr <timon.gehr gmx.ch> writes:
On 08/06/2015 01:30 PM, Idan Arye wrote:
On Thursday, 6 August 2015 at 11:26:10 UTC, Ola Fosheim Grøstad wrote:
On Thursday, 6 August 2015 at 06:50:38 UTC, Walter Bright wrote:
On 8/2/2015 8:17 PM, Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?=
It's a weird thing to do for a C-decendant as I would expect

"~=" to do binary
negation.

If you really felt this way, you'd expect the C != operator

a != b

to be the same as:

a = !b

I don't because "!=" is frequently used and usually in a context where
expectations points towards comparison and not assignment.

But I would prefer "=", "≠","<" and "≤" for comparison and
constants... then have something else for variable assignment.

I understand your attempt to auction your old APL keyboard didn't go well?

I can actually type ≠ and ≤ more quickly than != or <= in my editor.

Aug 06 2015
"Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Thursday, 6 August 2015 at 13:27:34 UTC, Timon Gehr wrote:
I can actually type ≠ and ≤ more quickly than != or <= in my
editor.

Wow. The only way that I'd know how to get those characters would
be to copy-paste them from somewhere. I'm sure that there's a far
easier way to generate them, but if a symbol isn't actually on my
keyboard, I wouldn't have a clue how to type it, and I would have
thought that support for typing it would be editor-specific. In
general, I would have expected it to be a total disaster if a
language used any non-ASCII characters in its syntax.

- Jonathan M Davis

Aug 06 2015
Russel Winder via Digitalmars-d <digitalmars-d puremagic.com> writes:
On Thu, 2015-08-06 at 14:14 +0000, Jonathan M Davis via Digitalmars-d
wrote:
On Thursday, 6 August 2015 at 13:27:34 UTC, Timon Gehr wrote:
I can actually type =E2=89=A0 and =E2=89=A4 more quickly than !=3D or <=

=3D in my=20
editor.

=20
Wow. The only way that I'd know how to get those characters would=20
be to copy-paste them from somewhere. I'm sure that there's a far=20
easier way to generate them, but if a symbol isn't actually on my=20
keyboard, I wouldn't have a clue how to type it, and I would have=20
thought that support for typing it would be editor-specific. In=20
general, I would have expected it to be a total disaster if a=20
language used any non-ASCII characters in its syntax.
=20
- Jonathan M Davis

As rsw0x said previously, compose keys and construction of non-ASCII
Unicode codepoints have been around for decades. The fixation on "only
ASCII characters" is a hang-over from the 1970s I'm afraid and now it
is 2015 =E2=80=93 supposedly.

A neat alternative to the compose key =E2=80=93 well actually a strong
accompaniment really =E2=80=93 is to allow for multiple keyboard bindings. =
In
particular I have Greek set up so I can switch from en (en_UK obviously
since that is the only real form of en) to gr very quickly and then I
am typing Greek characters on my UK keyboard. Damn useful for all this
LaTeX maths (*) and general "calculating approximations to =CF=80" type
stuff.

More programming languages should get with the Unicode programme.
Obviously not to allow the silly emoticon programs that did the rounds
on Swift's release, but exactly to allow for =E2=89=A0 =C2=AB =C2=BB  =E2=
=89=A4 =E2=89=A5 =C2=A8 etc., etc.,
etc. All the sensible stuff that would make reading programs easier.

(*) NB not math, that is an un-English diminutive :-)
--=20
Russel.
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D
Dr Russel Winder      t: +44 20 7585 2200   voip: sip:russel.winder ekiga.n=
et
41 Buckmaster Road    m: +44 7770 465 077   xmpp: russel winder.org.uk
London SW11 1EN, UK   w: www.russel.org.uk  skype: russel_winder

Aug 06 2015
"Kagamin" <spam here.lot> writes:
On Thursday, 6 August 2015 at 15:09:01 UTC, Russel Winder wrote:
Damn useful for all this LaTeX maths (*) and general
"calculating approximations to π" type stuff.

\pi ?
https://en.wikibooks.org/wiki/LaTeX/Mathematics#Greek_letters

Aug 06 2015
"rsw0x" <anonymous anonymous.com> writes:
On Thursday, 6 August 2015 at 15:31:25 UTC, Kagamin wrote:
On Thursday, 6 August 2015 at 15:09:01 UTC, Russel Winder wrote:
Damn useful for all this LaTeX maths (*) and general
"calculating approximations to π" type stuff.

\pi ?
https://en.wikibooks.org/wiki/LaTeX/Mathematics#Greek_letters

Using a compose key or alternate layout is much, *much* faster
than the LaTeX notation.

Aug 06 2015
"rsw0x" <anonymous anonymous.com> writes:
On Thursday, 6 August 2015 at 15:09:01 UTC, Russel Winder wrote:
More programming languages should get with the Unicode
programme.
Obviously not to allow the silly emoticon programs that did the
rounds
on Swift's release, but exactly to allow for ≠ « »  ≤ ≥ ¨ etc.,
etc.,
etc. All the sensible stuff that would make reading programs
easier.

A unicode DIP would be nice to see. Haskell offers Unicode
support and is completely compatible with ASCII.
Would probably be simple to create a tool to quickly shift
between the two.

It definitely does look much better.

Aug 06 2015
"Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
On Thursday, 6 August 2015 at 14:14:30 UTC, Jonathan M Davis
wrote:
Wow. The only way that I'd know how to get those characters
would be to copy-paste them from somewhere. I'm sure that
there's a far easier way to generate them, but if a symbol
isn't actually on my keyboard, I wouldn't have a clue how to
type it, and I would have thought that support for typing it
would be editor-specific. In general, I would have expected it
to be a total disaster if a language used any non-ASCII
characters in its syntax.

I think it is a big advantage if the default editor and syntax is
developed in tandem, it can enable less cluttered syntax and a
better editing experience. Some syntaxes can improve a lot by
having sensible colouring/visual layout.

On many non-US keyboards the C-language syntax-characters are put
in annoying positions too.

To get braces "{}" I have to type: alt-shift-8 alt-shift-9.

To get "<=" I have to type: < shift-0

To get "≤" I type: alt-<

Aug 06 2015
"jmh530" <john.michael.hall gmail.com> writes:
On Thursday, 6 August 2015 at 14:14:30 UTC, Jonathan M Davis
wrote:
In general, I would have expected it to be a total disaster if
a language used any non-ASCII characters in its syntax.

I still think it would be...not everyone uses the same editor. I
had to look up how to do it in Notepad++. It requires knowing the
Unicode key. Not exactly user-friendly. I wouldn't have an issue
if the editor had a layout like Lyx.

Aug 06 2015
Timon Gehr <timon.gehr gmx.ch> writes:
On 08/06/2015 07:13 PM, jmh530 wrote:
On Thursday, 6 August 2015 at 14:14:30 UTC, Jonathan M Davis wrote:
In general, I would have expected it to be a total disaster if a
language used any non-ASCII characters in its syntax.

I still think it would be...not everyone uses the same editor. I had to
look up how to do it in Notepad++. It requires knowing the Unicode key.
Not exactly user-friendly.

Not a Windows user, but I bet it wouldn't take long to find a better
solution.

I wouldn't have an issue if the editor had a
layout like Lyx.

Configuration is an O(1) operation. The constant becomes smaller after
the first three users of the hypothetical language have set up their
environment.

Aug 06 2015
"rsw0x" <anonymous anonymous.com> writes:
On Thursday, 6 August 2015 at 11:26:10 UTC, Ola Fosheim Grøstad
wrote:
But I would prefer "=", "≠","<" and "≤" for comparison and
constants... then have something else for variable assignment.

:=, or ≔

Aug 06 2015
Timon Gehr <timon.gehr gmx.ch> writes:
On 08/06/2015 04:14 PM, rsw0x wrote:
On Thursday, 6 August 2015 at 11:26:10 UTC, Ola Fosheim Grøstad wrote:
But I would prefer "=", "≠","<" and "≤" for comparison and
constants... then have something else for variable assignment.

:=, or ≔

← or ⇐ are also fine choices.

Aug 06 2015
Russel Winder via Digitalmars-d <digitalmars-d puremagic.com> writes:
On Thu, 2015-08-06 at 16:20 +0200, Timon Gehr via Digitalmars-d wrote:
On 08/06/2015 04:14 PM, rsw0x wrote:
On Thursday, 6 August 2015 at 11:26:10 UTC, Ola Fosheim Gr=C3=B8stad=

=20
wrote:
But I would prefer "=3D", "=E2=89=A0","<" and "=E2=89=A4" for compari=

son and
constants... then have something else for variable assignment.

=20
:=3D, or =E2=89=94

=20
=E2=86=90 or =E2=87=90 are also fine choices.

And indeed ones taken in Scala, <- can be =E2=86=90, which looks so much ni=
cer
--=20
Russel.
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D
Dr Russel Winder      t: +44 20 7585 2200   voip: sip:russel.winder ekiga.n=
et
41 Buckmaster Road    m: +44 7770 465 077   xmpp: russel winder.org.uk
London SW11 1EN, UK   w: www.russel.org.uk  skype: russel_winder

Aug 06 2015
"Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
On Thursday, 6 August 2015 at 15:11:59 UTC, Russel Winder wrote:
On Thu, 2015-08-06 at 16:20 +0200, Timon Gehr via Digitalmars-d
wrote:
← or ⇐ are also fine choices.

And indeed ones taken in Scala, <- can be ←, which looks so

Yes, probably doesn't matter too much which way the arrow points,
you can make up mnemonics for both. I.e. are you transferring a
reference/value to the symbol or is the symbol pointing
at/pinpointing an object/value. I guess the first one is the more
common mnemonic, although for references maybe the latter is more
in line with how you draw diagrams (the arrow pointing to the
instance).

IIRC, in Beta you had this pipeline like assignment/function call
notation

(s,t,v) => func1 => func2 => ((x,y,z), (a,b,c))

Which would be similar to the more conventional

((x,y,z), (a,b,c)) := func2(func1(s,t,v))

With arrows you can allow both directions. The conventional right
to left is easier to read for short expressions. But the
pipelining left to right is easier to read for longer expressions
that go through multiple stages.

I think Rust also allows you to bind elements to a tuple using
both "let" and "mut" in the same tuple expression, so that you
can declare and bind both to variables and constants in a single
expression.

If you have serveral visually distinct array types you probably
could get a coherent and easy to remember syntax for function
calls, value assignment, reference assignment, array assignment,
ranges/dataflow pipelining etc.

Aug 06 2015
"Kagamin" <spam here.lot> writes:
On Sunday, 2 August 2015 at 21:17:10 UTC, Jonathan M Davis wrote:
Where distinguishing between + and ~ would likely make a big
difference though is dynamic languages that aren't strict with
types and allow nonsense like "5" + 2.

Using '~' instead of '+' to concatenate strings is just a syntax
and says nothing about type system.

Aug 03 2015
Timon Gehr <timon.gehr gmx.ch> writes:
On 08/02/2015 09:02 PM, Max Samukha wrote:
On Sunday, 26 July 2015 at 23:29:18 UTC, Walter Bright wrote:

For example, the '+' operator. Rust traits sez "gee, there's a +
operator, it's good to go. Ship it!" Meanwhile, you thought the
function was summing some data, when it actually is creating a giant
string, or whatever idiot thing someone decided to use '+' for.

Number addition and string concatenation are monoid operations. In this
light, '+' for both makes perfect sense.

'+' is usually used to denote the operation of an abelian group.

Aug 02 2015
"Max Samukha" <maxsamukha gmail.com> writes:
On Monday, 3 August 2015 at 06:52:41 UTC, Timon Gehr wrote:
On 08/02/2015 09:02 PM, Max Samukha wrote:
On Sunday, 26 July 2015 at 23:29:18 UTC, Walter Bright wrote:

For example, the '+' operator. Rust traits sez "gee, there's
a +
operator, it's good to go. Ship it!" Meanwhile, you thought
the
function was summing some data, when it actually is creating
a giant
string, or whatever idiot thing someone decided to use '+'
for.

Number addition and string concatenation are monoid
operations. In this
light, '+' for both makes perfect sense.

'+' is usually used to denote the operation of an abelian group.

The point is that '+' for string concatenation is no more of an
'idiot thing' than '~'.

Aug 03 2015
Timon Gehr <timon.gehr gmx.ch> writes:
On 08/03/2015 11:19 AM, Max Samukha wrote:
On Monday, 3 August 2015 at 06:52:41 UTC, Timon Gehr wrote:
On 08/02/2015 09:02 PM, Max Samukha wrote:
On Sunday, 26 July 2015 at 23:29:18 UTC, Walter Bright wrote:

For example, the '+' operator. Rust traits sez "gee, there's a +
operator, it's good to go. Ship it!" Meanwhile, you thought the
function was summing some data, when it actually is creating a giant
string, or whatever idiot thing someone decided to use '+' for.

Number addition and string concatenation are monoid operations. In this
light, '+' for both makes perfect sense.

'+' is usually used to denote the operation of an abelian group.

The point is that '+' for string concatenation is no more of an 'idiot
thing' than '~'.

My point is that it is. String concatenation is not commutative.

Aug 05 2015
"Max Samukha" <maxsamukha gmail.com> writes:
On Wednesday, 5 August 2015 at 15:58:28 UTC, Timon Gehr wrote:

The point is that '+' for string concatenation is no more of
an 'idiot
thing' than '~'.

My point is that it is. String concatenation is not commutative.

Ok, good point. Except that '+' in a programming language is not
the mathematical '+'. Why define '+' as strictly commutative
operation and not more generally as an abstract binary operation,
considering the middle dot is unavailable? Or, if we want to
stick to the math notation, then '*' would be more appropriate
than the idiot thing '~'.

Aug 05 2015
On Wednesday, 5 August 2015 at 17:12:29 UTC, Max Samukha wrote:
On Wednesday, 5 August 2015 at 15:58:28 UTC, Timon Gehr wrote:

The point is that '+' for string concatenation is no more of
an 'idiot
thing' than '~'.

My point is that it is. String concatenation is not
commutative.

Ok, good point. Except that '+' in a programming language is
not the mathematical '+'. Why define '+' as strictly
commutative operation and not more generally as an abstract
binary operation, considering the middle dot is unavailable?
Or, if we want to stick to the math notation, then '*' would be
more appropriate than the idiot thing '~'.

Nobody want to stay in the math world. Not that math are
worthless, but it has this tendency to make simple things
absurdly complex by requiring you to learn a whole area of math
to understand the introduction.

This is commonly referred as the monad curse: once you understand
what a monad is, you loose all capacity to explain it. In fact,
Most developers have used some sort of monad, but only a very
small portion know they were using one or can explain you what it
is.

Mathematical language is geared toward generality and
correctness, not practicality. That makes sens in the context of
math, that do not in the context of every day programming.

Aug 05 2015
Timon Gehr <timon.gehr gmx.ch> writes:
On 08/05/2015 07:32 PM, deadalnix wrote:
On Wednesday, 5 August 2015 at 17:12:29 UTC, Max Samukha wrote:
On Wednesday, 5 August 2015 at 15:58:28 UTC, Timon Gehr wrote:

The point is that '+' for string concatenation is no more of an 'idiot
thing' than '~'.

My point is that it is. String concatenation is not commutative.

Ok, good point. Except that '+' in a programming language is not the
mathematical '+'. Why define '+' as strictly commutative operation and
not more generally as an abstract binary operation, considering the
middle dot is unavailable? Or, if we want to stick to the math
notation, then '*' would be more appropriate than the idiot thing '~'.

Nobody want to stay in the math world. Not that math are worthless, but
it has this tendency to make simple things absurdly complex by requiring
you to learn a whole area of math to understand the introduction.

I assume the set of examples you are generalizing this from has
cardinality close to one? Anyway, it seems like an exaggeration.

This is commonly referred as the monad curse: once you understand what a
monad is, you loose all capacity to explain it.

In fact, Most developers
have used some sort of monad, but only a very small portion know they
were using one or can explain you what it is.
...

Which isn't surprising. This isn't a very useful name in their (quite
specific) use cases.

Mathematical language is geared toward generality and correctness, not
practicality. That makes sens in the context of math, that do not in the
context of every day programming.

I don't see what you are trying to get at here, but I guess it is almost
entirely unrelated to choosing a notation for string concatenation.

Aug 05 2015
"Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
On Wednesday, 5 August 2015 at 19:56:37 UTC, Timon Gehr wrote:
On 08/05/2015 07:32 PM, deadalnix wrote:
Mathematical language is geared toward generality and
correctness, not
practicality. That makes sens in the context of math, that do
not in the
context of every day programming.

I don't see what you are trying to get at here, but I guess it
is almost entirely unrelated to choosing a notation for string
concatenation.

Well, I don't think practicality is the main issue, but the
mnemonic aspect of syntax is important.

It is not unreasonable to make the identity of
operators/functions consist of both name and parameter types like
in C++ and D. So you don't have "+" as the operator name, you
have "+(int,int)" and "+(string,string)".

If one makes mathematical properties intrinsic to untyped part of
the name then a lot of overloading scenarios break down e.g. for
non-euclidean types.

It has been argued that functional languages would benefit from
teaching functional programming in a less mathematical manner

https://youtu.be/oYk8CKH7OhE

Aug 05 2015
Timon Gehr <timon.gehr gmx.ch> writes:
On 08/06/2015 07:50 AM, "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?=
On Wednesday, 5 August 2015 at 19:56:37 UTC, Timon Gehr wrote:
On 08/05/2015 07:32 PM, deadalnix wrote:
Mathematical language is geared toward generality and correctness, not
practicality. That makes sens in the context of math, that do not in the
context of every day programming.

I don't see what you are trying to get at here, but I guess it is
almost entirely unrelated to choosing a notation for string
concatenation.

Well, I don't think practicality is the main issue, but the mnemonic
aspect of syntax is important.

It is not unreasonable to make the identity of operators/functions
consist of both name and parameter types like in C++ and D. So you don't
have "+" as the operator name, you have "+(int,int)" and
"+(string,string)".
...

It has been argued that functional languages would benefit from teaching
functional programming in a less mathematical manner (e.g. talk about

https://youtu.be/oYk8CKH7OhE

That's not less "mathematical". It is less abstract, maybe. Also, I
think he is optimizing for people who pick up the language on their own.

Aug 06 2015
Timon Gehr <timon.gehr gmx.ch> writes:
On 08/05/2015 07:12 PM, Max Samukha wrote:
On Wednesday, 5 August 2015 at 15:58:28 UTC, Timon Gehr wrote:

The point is that '+' for string concatenation is no more of an 'idiot
thing' than '~'.

My point is that it is. String concatenation is not commutative.

Ok, good point. Except that '+' in a programming language is not the
mathematical '+'.

It's obvious where the notation has been borrowed from.

Why define '+' as strictly commutative operation and
not more generally as an abstract binary operation,

Descriptive names do have some value.

considering the middle dot is unavailable?

(It isn't.)

Or, if we want to stick to the math notation,
then '*' would be more appropriate than the idiot thing '~'.

That's a different discussion. '*' is certainly more appropriate than
'+'. Anyway, I think it is sensible to use distinct names for distinct
operations when they are used in the same system.

Aug 05 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 8/3/2015 2:19 AM, Max Samukha wrote:
The point is that '+' for string concatenation is no more of an 'idiot thing'
than '~'.

Sure it is. What if you've got:

T add(T)(T a, T b) { return a + b; }

and some idiot overloaded + for T to be something other than addition?

Aug 05 2015
"Idan Arye" <GenericNPC gmail.com> writes:
On Thursday, 6 August 2015 at 06:54:45 UTC, Walter Bright wrote:
On 8/3/2015 2:19 AM, Max Samukha wrote:
The point is that '+' for string concatenation is no more of
an 'idiot thing'
than '~'.

Sure it is. What if you've got:

T add(T)(T a, T b) { return a + b; }

and some idiot overloaded + for T to be something other than

Having add("a", "b") return "ab" is not that weird. But consider
this: http://pastebin.com/R3csc5Pa

I can't put it in dpaste because it doesn't allow threading, but
here is an example output:

45
45
45
45
45
45
45
45
45
45
MyString("0361572489")
MyString("0379158246")
MyString("0369158247")
MyString("0582361479")
MyString("0482579136")
MyString("0369147258")
MyString("0371482569")
MyString("0469137258")
MyString("0369147258")
MyString("0561472389")

Aug 06 2015
"Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
On Thursday, 6 August 2015 at 09:15:25 UTC, Idan Arye wrote:
Having add("a", "b") return "ab" is not that weird. But
consider this: http://pastebin.com/R3csc5Pa

I can't put it in dpaste because it doesn't allow threading,
but here is an example output:

This would just be an argument for having static typing through
and through, or Rust traits…

If you wan't to address usability you have to look at what kind
of problems people run into, not what they could construct if
they tried really hard to create problems.

Aug 06 2015
Max Samukha <maxsamukha gmail.com> writes:
On Thursday, 6 August 2015 at 06:54:45 UTC, Walter Bright wrote:
On 8/3/2015 2:19 AM, Max Samukha wrote:
The point is that '+' for string concatenation is no more of
an 'idiot thing'
than '~'.

Sure it is. What if you've got:

T add(T)(T a, T b) { return a + b; }

and some idiot overloaded + for T to be something other than

That is a general problem with structural typing. Why not assume
that if a type defines 'length', it must be a range? Then call an
idiot everyone who defines it otherwise. I admit that special
treatment of '+' is justified by its long history, but 'idiot
thing' is obviously out of place.

BTW, it happens that '+' does not always have to be commutative:
https://en.wikipedia.org/wiki/Near-ring

Jul 11 2016
Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Monday, 11 July 2016 at 17:02:50 UTC, Max Samukha wrote:
BTW, it happens that '+' does not always have to be
commutative: https://en.wikipedia.org/wiki/Near-ring

Yes, although concatenations ought to be «*»... and the empty
string «""» the identity (1).

e.g. a free monoid:

"abc" * "d" == "abcd"
"abc"*"" == "abc"
"abc"*""*"" == "abc"

Then if you want to represent a set of alternate sets like a
regexp «ab|cd» you could replace the alternative operator «|»
with «+» and let the empty set «{}» be zero (0). Thus get a
semiring:

( {"a"} + {"b"} ) + {"c"} == {"a"} + ( {"b"} + {"c"} )  == {"a" +
"b" + "c"}
{} + {"a"} == {"a"} + {} == {"a"}
{"a"} + {"b"} == {"b"} + {"a"}  == {"a" + "b"}

( {"a"} * {"b"} ) * {"c"} == {"a"} * ( {"b"} * {"c"} ) == {"abc"}

{"a"} * ({"b"} + {"c"}) == ({"a"} * {"b"}) + ({"a"} * {"c"}) == {
"ab" + "ac" }
({"a"} + {"b"}) * {"c"} == ({"a"} * {"c"}) + ({"b"} * {"c"}) ==
{"ab + "bc" }

{""} * {"a"} == {"a"} * {""} == {"a"}
{} * {"a"} == {"a"} * {} == {}

Sortof...

Jul 13 2016
Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Wednesday, 13 July 2016 at 21:33:39 UTC, Ola Fosheim Grøstad
wrote:
Then if you want to represent a set of alternate sets like a

Typo: a set of alternative strings.

Jul 13 2016
"Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Saturday, 25 July 2015 at 09:40:52 UTC, Walter Bright wrote:
BTW, you might want to remove the UTF-8 characters from your
user name. Evidently, NNTP doesn't do well with them.

Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?=

It (or more likely, his user agent) does deal with them well.
It's correctly quoted according to RFC2047:

http://www.faqs.org/rfcs/rfc2047.html

I've opened an enhancement request at DFeed's issue tracker:


Jul 27 2015
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/24/15 11:12 PM, Walter Bright wrote:
On 7/24/2015 7:28 PM, Jonathan M Davis wrote:
I confess that I've always thought that QueryInterface was a
_horrible_ idea,

Specifying every interface that a type must support at the top of the
hierarchy is worse.

That would be an oversimplification.

Once again, Exception Specifications.

And that simile would be superficial.

Andrei

Jul 25 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 7/24/2015 7:28 PM, Jonathan M Davis wrote:
I confess that I've always thought that QueryInterface was a _horrible_ idea,
and that if you need to cast your type to something else like that, you're
doing
something wrong. *shudder* I really have nothing good to say about COM
actually...

I am not explaining this properly. Trying again,

void foo(T: hasPrefix)(T t) {
t.prefix();    // ok
bar(t);        // error, hasColor was not specified for T
}

void bar(T: hasColor)(T t) {
t.color();
}

The Java, COM interface systems do not write code like:

void foo(T: hasPrefix, hasSuffix)(T t) {
t.prefix(); // ok
bar(t);
}

They write it something like:

void foo(hasPrefix t) {
t.prefix();
s = cast(hasSuffix)t;
if (s) bar(s);
else RuntimeError(message);
}

So no, statically checked traits and concepts are not used in the OOP world.

Jul 25 2015
=?UTF-8?Q?Tobias=20M=C3=BCller?= <troplin bluewin.ch> writes:
Walter Bright <newshound2 digitalmars.com> wrote:
They write it something like:

void foo(hasPrefix t) {
t.prefix();
s = cast(hasSuffix)t;
if (s) bar(s);
else RuntimeError(message);
}

That's horrible!

Tobi

Jul 25 2015
"Jordan Miner" <jminer7 gmail.com> writes:
On Saturday, 25 July 2015 at 19:23:43 UTC, Tobias Müller wrote:
Walter Bright <newshound2 digitalmars.com> wrote:
They write it something like:

void foo(hasPrefix t) {
t.prefix();
s = cast(hasSuffix)t;
if (s) bar(s);
else RuntimeError(message);
}

That's horrible!

Tobi

I agree. Seriously, that's horrible. I can't remember ever seeing
code written like that. Why even use a statically-typed language
if you are just going to bypass the type system?

Jordan

Jul 25 2015
On Saturday, 25 July 2015 at 02:28:49 UTC, Jonathan M Davis wrote:
On Saturday, 25 July 2015 at 01:22:15 UTC, Tofu Ninja wrote:
On Saturday, 25 July 2015 at 00:49:38 UTC, Walter Bright wrote:
On 7/24/2015 2:27 PM, Tofu Ninja wrote:
No it isn't. Google QueryInterface(). Nobody lists all the
interfaces at the top level functions, which is that Rust
traits and C++ concepts require.

The only time you don't use the right interface for your needs
is if you plan on casting somewhere down the line. But
certainly there are people who don't do that, I for one feel
it's bad practice to need to use casts to circumvent the type
system like that.

I confess that I've always thought that QueryInterface was a
_horrible_ idea, and that if you need to cast your type to
something else like that, you're doing something wrong.
*shudder* I really have nothing good to say about COM
actually...

- Jonathan M Davis

Well yes and now. When you design is new and shinny, sure, that
is a sign that somewhere it is broken. When you are patching a
hundred of thousand line of code, you may not be able to get the
refactoring in all at once in a realistic manner and need to
build some debt. Hopefully, as the refactoring progress, these
hacks are removed, but not having just makes cost of change

Jul 25 2015
"Tofu Ninja" <emmons0 purdue.edu> writes:
On Saturday, 25 July 2015 at 00:49:38 UTC, Walter Bright wrote:
Sigh. Nothing I post here is understood.

Then make yourself more clear...

Jul 24 2015
On Friday, 24 July 2015 at 19:10:33 UTC, Walter Bright wrote:
On 7/24/2015 11:42 AM, Jacob Carlborg wrote:
I don't see the difference compared to a regular parameter. If
you don't specify
any constraints/traits/whatever it like using "Object" for all
types in Java.

So constraints then will be an all-or-nothing proposition? I
believe that would make them essentially useless.

I suspect I am not getting across the essential point. If I
have a call tree, and at the bottom I add a call to interface
X on each function up the call tree to the root. That is
antiethical to writing generic code, and will prove to be more
of a nuisance than an asset.

Exactly what sunk Exception Specifications.

In many language you have an instaceof keyword or something
similar. You'd get :

if (foo instanceof X) {
// You can use X insterface on foo.
}

vs

static if (foo instanceof X) {
// You can use X insterface on foo.
}

The whole runtime vs compile time is essentially an
implementation detail. The idea is the very same.

The most intriguing part of this conversation is that the
than for dynamic vs strong typing (and there is hard data that
strong typing is better).

Yet, if someone would make the very same argument in the case of
dynamic typing, both Walter and Andrei would not give it a second
though (and rightly so). Yet, nowhere the reason why this differs
in ay that make the cost/benefit ratio shift is mentioned. It is
simply asserted as such.

Jul 24 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 7/24/2015 3:12 PM, deadalnix wrote:
On Friday, 24 July 2015 at 19:10:33 UTC, Walter Bright wrote:
If I have a call tree,
and at the bottom I add a call to interface X, then I have to add a constraint
that additionally specifies X on each function up the call tree to the root.
That is antiethical to writing generic code, and will prove to be more of a
nuisance than an asset.

Exactly what sunk Exception Specifications.

In many language you have an instaceof keyword or something similar. You'd get
:

if (foo instanceof X) {
// You can use X insterface on foo.
}

vs

static if (foo instanceof X) {
// You can use X insterface on foo.
}

The whole runtime vs compile time is essentially an implementation detail. The
idea is the very same.

The most intriguing part of this conversation is that the argument made about
unitests and complexity are the very same than for dynamic vs strong typing
(and
there is hard data that strong typing is better).

Yet, if someone would make the very same argument in the case of dynamic
typing,
both Walter and Andrei would not give it a second though (and rightly so). Yet,
nowhere the reason why this differs in ay that make the cost/benefit ratio
shift
is mentioned. It is simply asserted as such.

I don't see how this addresses my point at all. This is very frustrating.

Jul 24 2015
"Tofu Ninja" <emmons0 purdue.edu> writes:
On Saturday, 25 July 2015 at 00:45:06 UTC, Walter Bright wrote:
On 7/24/2015 3:12 PM, deadalnix wrote:
On Friday, 24 July 2015 at 19:10:33 UTC, Walter Bright wrote:
If I have a call tree,
and at the bottom I add a call to interface X, then I have to
that additionally specifies X on each function up the call
tree to the root.
That is antiethical to writing generic code, and will prove
to be more of a
nuisance than an asset.

Exactly what sunk Exception Specifications.

In many language you have an instaceof keyword or something
similar. You'd get :

if (foo instanceof X) {
// You can use X insterface on foo.
}

vs

static if (foo instanceof X) {
// You can use X insterface on foo.
}

The whole runtime vs compile time is essentially an
implementation detail. The
idea is the very same.

The most intriguing part of this conversation is that the
unitests and complexity are the very same than for dynamic vs
strong typing (and
there is hard data that strong typing is better).

Yet, if someone would make the very same argument in the case
of dynamic typing,
both Walter and Andrei would not give it a second though (and
rightly so). Yet,
nowhere the reason why this differs in ay that make the
cost/benefit ratio shift
is mentioned. It is simply asserted as such.

I don't see how this addresses my point at all. This is very
frustrating.

The same "problems" that you are claiming will happen with with
the compile time interfaces are the exact same as the problems
that happen with normal types systems.

With the normal type system, if somewhere down the call tree you
need X you need to either update your interface, make a new one
and add it to the whole call tree, or cast.

If somewhere down the call tree a template needs X, then with
this compile time interface thing, you would need to either
update your interface, make a new one and add it to the whole
call tree, or do some kind of static cast.

ITS THE SAME.

Your arguments for why not to do it are even the same that people
give for dynamic typing, and we know how well that works out.

Current template types work like duck typing, which works, but
in the context of duck typing. We want a real type system for our
template types.

You may be thinking, but why would you want a type system for
template types, why not just use the normal type system? Well
because we want static dispatch, and compile time introspection
and static if and all the other great things we have at compile
time.

Jul 24 2015
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/24/15 9:16 PM, Tofu Ninja wrote:
Current template types work like duck typing, which works, but its error
prone, and your argument of unittests is obviously bad in the context of
duck typing.

Could you please make the obvious explicit?

We want a real type system for our template types.

Every time this (or really any apology of C++ concepts) comes up, the
discussion has a similar shape:

1. Concepts are great because they're a type system for the type system!
And better error messages! And look at these five-liners! And Look at
iterators! And other nice words!

2. I destroy them.

3. But we want concepts because they're a type system for the type
system! And ... etc. etc.

I have no idea how people can simply ignore the fact that their
arguments have been systematically dismantled.

Andrei

Jul 25 2015
On Saturday, 25 July 2015 at 13:47:05 UTC, Andrei Alexandrescu
wrote:
On 7/24/15 9:16 PM, Tofu Ninja wrote:
Current template types work like duck typing, which works, but
its error
context of
duck typing.

Could you please make the obvious explicit?

We want a real type system for our template types.

Every time this (or really any apology of C++ concepts) comes
up, the discussion has a similar shape:

1. Concepts are great because they're a type system for the
type system! And better error messages! And look at these
five-liners! And Look at iterators! And other nice words!

2. I destroy them.

So far, you've just rehashed bogous claim made for dynamic typing

3. But we want concepts because they're a type system for the
type system! And ... etc. etc.

I have no idea how people can simply ignore the fact that their
arguments have been systematically dismantled.

Andrei

Because you only think you did, but really didn't.

Jul 25 2015
On Saturday, 25 July 2015 at 00:45:06 UTC, Walter Bright wrote:
On 7/24/2015 3:12 PM, deadalnix wrote:
On Friday, 24 July 2015 at 19:10:33 UTC, Walter Bright wrote:
If I have a call tree,
and at the bottom I add a call to interface X, then I have to
that additionally specifies X on each function up the call
tree to the root.
That is antiethical to writing generic code, and will prove
to be more of a
nuisance than an asset.

Exactly what sunk Exception Specifications.

In many language you have an instaceof keyword or something
similar. You'd get :

if (foo instanceof X) {
// You can use X insterface on foo.
}

vs

static if (foo instanceof X) {
// You can use X insterface on foo.
}

The whole runtime vs compile time is essentially an
implementation detail. The
idea is the very same.

The most intriguing part of this conversation is that the
unitests and complexity are the very same than for dynamic vs
strong typing (and
there is hard data that strong typing is better).

Yet, if someone would make the very same argument in the case
of dynamic typing,
both Walter and Andrei would not give it a second though (and
rightly so). Yet,
nowhere the reason why this differs in ay that make the
cost/benefit ratio shift
is mentioned. It is simply asserted as such.

I don't see how this addresses my point at all. This is very
frustrating.

I think it does. Your point is essentially an argument by
ignorance: this is new, we don't really know, and it has been
show in the past that what seems like a good idea (checked
exception for instance) turns out horribly wrong, against all
expectations.

My point is that it is not new, it is very much the same thing as
what we've been doing ll along for several decades, the
difference being mostly implementation details.

Also, argument from ignorance is hard to maintain when the thread
is an actual feedback from experience.

Jul 25 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 7/25/2015 3:28 PM, deadalnix wrote:
Also, argument from ignorance is hard to maintain when the thread is an actual
feedback from experience.

You say that interfaces do the same thing. So please show how it's done with
the
example I gave:

int foo(T: hasPrefix)(T t) {
t.prefix();    // ok
bar(t);        // error, hasColor was not specified for T
}

void bar(T: hasColor)(T t) {
t.color();
}

Jul 25 2015
On Sunday, 26 July 2015 at 03:42:22 UTC, Walter Bright wrote:
On 7/25/2015 3:28 PM, deadalnix wrote:
Also, argument from ignorance is hard to maintain when the
feedback from experience.

You say that interfaces do the same thing. So please show how
it's done with the example I gave:

int foo(T: hasPrefix)(T t) {
t.prefix();    // ok
bar(t);        // error, hasColor was not specified for T
}

void bar(T: hasColor)(T t) {
t.color();
}

I'm not sure what is the problem here.

Jul 26 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 7/26/2015 3:59 PM, deadalnix wrote:
On Sunday, 26 July 2015 at 03:42:22 UTC, Walter Bright wrote:
On 7/25/2015 3:28 PM, deadalnix wrote:
Also, argument from ignorance is hard to maintain when the thread is an actual
feedback from experience.

You say that interfaces do the same thing. So please show how it's done with
the example I gave:

int foo(T: hasPrefix)(T t) {
t.prefix();    // ok
bar(t);        // error, hasColor was not specified for T
}

void bar(T: hasColor)(T t) {
t.color();
}

I'm not sure what is the problem here.

I give up.

Jul 26 2015
"Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Sunday, 26 July 2015 at 22:59:09 UTC, deadalnix wrote:
On Sunday, 26 July 2015 at 03:42:22 UTC, Walter Bright wrote:
On 7/25/2015 3:28 PM, deadalnix wrote:
Also, argument from ignorance is hard to maintain when the
feedback from experience.

You say that interfaces do the same thing. So please show how
it's done with the example I gave:

int foo(T: hasPrefix)(T t) {
t.prefix();    // ok
bar(t);        // error, hasColor was not specified for
T
}

void bar(T: hasColor)(T t) {
t.color();
}

I'm not sure what is the problem here.

The problem here is that if you're dealing with traits or
concepts or whatever that are essentially interfaces, and foo
uses the Prefix interface/trait/concept and then wants to use bar
which has the Color interface/trait/concept, it has to then
require that what it's given implements both the Prefix and Color
interfaces/traits/concepts.

In the case of actual interfaces, this really doesn't work well,
because you're forced to basically have an interface that's
derived from both - e.g. PrefixAndColor. Otherwise, you'd be
forced to do nonsense like have foo take a Prefix and then cast
it to Color to pass to bar and throw an exception if the type it
was given didn't actually implement both interfaces. And it's
downright ugly. In reality, the code would likely simply end up
not being that generic and would require some specific type that
you were using in your code which implemented both Prefix and
Color, and foo just wouldn't work with anything else, making it
far less reusable. So, with actual interfaces, it becomes very
difficult to write generic code.

With traits or concepts, presumably, you could say that foo
required a type that implemented both Prefix and Color, which
fixes the problem of how you're able to accept something that
takes both generically without coming up with something like
PrefixAndColor (though if you can only list one trait/concept as
required, you're just as screwed as you are with interfaces). But
even if you can list multiple traits/concepts as required by a
function, you quickly end up with a proliferation of
traits/concepts that need to be covered by a higher level
function like foo, because not only would foo need to list all of
the traits/concepts that the functions it uses directly require,
but it has to list all of the traits/concepts that it even
indirectly requires (potentially from far down the call stack).
So, any change to a function that even gets called indirectly
could break foo, because it didn't have the right traits/concepts
listed in its requirements. And all of the functions in the call
chain would have to have their list of required traits/concepts
updated any time there was any tweak to any of the underlying
functions, even if those functions would have actually worked
fine with most of the code that was calling them, because the
that the functions higher up the stack didn't list the updated
requirements yet).

By having foo list all of the traits/concepts that would be
required anywhere in its call stack, you're doing something very
similar to what happens with checked exceptions where the
exceptions have to be listed clear up the chain. It's not quite
the same, and there's no equivalent to "throws Exception" (since
that would be equivalent to somehow having a trait/concept that
said that you didn't care what the type given to foo
implemented). Rather, you're basically being forced to list each
trait/concept individually up the chain - but it's still a
similar problem to checked exceptions. It doesn't scale well. And
if a function is required to list all of the traits/concepts that
are required - even indirectly - then changing the requirements
of a function - even slightly - results in code breakage similar
to that of checked exceptions when you change which exceptions a
function throws, and "throws Exception" wasn't being used. And
even if you're not worried about breaking other people's code,
it's a maintenance problem to maintain that list clear up the
chain.

Unfortunately, we _do_ have a similar problem with template
constraints if we insist on putting all of the function's
requirements in its template constraint rather than just its
immediate requirements. But at least with template constraints,
if the top-level constraint is missing something that a function
somewhere deeper in the stack requires, then you get a
compilation error only when the type being passed in doesn't pass
the constraint on the function deeper in the stack. So, if you
adjust a template constraint, it will only break code that
doesn't work with the new constraint - even code that uses that
function indirectly (possibly even quite deeply in a call stack,
far from their own code) won't break due to the change, unless
the type being used doesn't pass the new constraint. And when it
does fail, the errors may not be pretty, but they do tell you
exactly what's required to figure out what's wrong when you look
at the source code. Whereas the traits/concepts solution would
break _all_ code that used the function that was adjusted (even
indirectly), not just the code that wouldn't work with the new
requirements.

I discussed this quite a bit more elsewhere in this thread:
http://forum.dlang.org/post/lsxidsyweczhojoucnsw forum.dlang.org

- Jonathan M Davis

Jul 26 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 7/26/2015 6:59 PM, Jonathan M Davis wrote:
[...]

Thank you, Jonathan!

Jul 26 2015
On Monday, 27 July 2015 at 01:59:49 UTC, Jonathan M Davis wrote:
On Sunday, 26 July 2015 at 22:59:09 UTC, deadalnix wrote:
On Sunday, 26 July 2015 at 03:42:22 UTC, Walter Bright wrote:
On 7/25/2015 3:28 PM, deadalnix wrote:
Also, argument from ignorance is hard to maintain when the
feedback from experience.

You say that interfaces do the same thing. So please show how
it's done with the example I gave:

int foo(T: hasPrefix)(T t) {
t.prefix();    // ok
bar(t);        // error, hasColor was not specified
for T
}

void bar(T: hasColor)(T t) {
t.color();
}

I'm not sure what is the problem here.

The problem here is that if you're dealing with traits or
concepts or whatever that are essentially interfaces, and foo
uses the Prefix interface/trait/concept and then wants to use
bar which has the Color interface/trait/concept, it has to then
require that what it's given implements both the Prefix and
Color interfaces/traits/concepts.

So, if I translate to regular D, here is what I get :

int foo(T)(T t) if (hasPrefix!T) {
t.prefix();    // ok
bar(t);        // ok, nothign is checked
}

void bar(T)(T t) if (hasColor!T) {
t.color(); // error, color is not specified on an object of
type XXX
}

It changes nothing.

In the case of actual interfaces, this really doesn't work
well, because you're forced to basically have an interface
that's derived from both - e.g. PrefixAndColor. Otherwise,
you'd be forced to do nonsense like have foo take a Prefix and
then cast it to Color to pass to bar and throw an exception if
the type it was given didn't actually implement both
interfaces. And it's downright ugly. In reality, the code would
likely simply end up not being that generic and would require
some specific type that you were using in your code which
implemented both Prefix and Color, and foo just wouldn't work
with anything else, making it far less reusable. So, with
actual interfaces, it becomes very difficult to write generic
code.

We do not have to make the same limitation (especially if trait
are implicitly implemented).

Still, even with this limitation, using type is considered
superior and using unitests is not considered to be not
sufficient.

Also, your message is kind of weird as you seem to assume in that
part that what is discussed here is the same as passing argument,
when you get back to the checked exception position lower.

With traits or concepts, presumably, you could say that foo
required a type that implemented both Prefix and Color, which
fixes the problem of how you're able to accept something that
takes both generically without coming up with something like
PrefixAndColor (though if you can only list one trait/concept
as required, you're just as screwed as you are with
interfaces). But even if you can list multiple traits/concepts
as required by a function, you quickly end up with a
proliferation of traits/concepts that need to be covered by a
higher level function like foo, because not only would foo need
to list all of the traits/concepts that the functions it uses
directly require, but it has to list all of the traits/concepts
that it even indirectly requires (potentially from far down the
call stack). So, any change to a function that even gets called
indirectly could break foo, because it didn't have the right
traits/concepts listed in its requirements. And all of the
functions in the call chain would have to have their list of
required traits/concepts updated any time there was any tweak
to any of the underlying functions, even if those functions
would have actually worked fine with most of the code that was
calling them, because the types being passed in had the new
requirements already (it's just that the functions higher up
the stack didn't list the updated requirements yet).

By having foo list all of the traits/concepts that would be
required anywhere in its call stack, you're doing something
very similar to what happens with checked exceptions where the
exceptions have to be listed clear up the chain. It's not quite
the same, and there's no equivalent to "throws Exception"
(since that would be equivalent to somehow having a
trait/concept that said that you didn't care what the type
given to foo implemented). Rather, you're basically being
forced to list each trait/concept individually up the chain -
but it's still a similar problem to checked exceptions. It
doesn't scale well. And if a function is required to list all
of the traits/concepts that are required - even indirectly -
then changing the requirements of a function - even slightly -
results in code breakage similar to that of checked exceptions
when you change which exceptions a function throws, and "throws
Exception" wasn't being used. And even if you're not worried
about breaking other people's code, it's a maintenance problem
to maintain that list clear up the chain.

I'm doing something similar to checked exception. Yes. I'm
passing argument down. There are similar indeed, and this is why
people though checked exception were a good idea.

The main way in which the differs is that checked Exception
expose implementation, while typed argument provide a contract
for the caller.

Consider it that way. Template are a meta language within the
language. With template you write code that write code.

At this meta level, types are values. Instantiating a template is
the same as calling a function (function that will generate
code). That is the reason why static if works so well, and that
is the reason why static foreach is asked for. This is the reason
why Andrei's approach to present compile time arguments in TDPL
rather than templates works.

For some reason, unitests cannot replace types for code that
connect to database, database code itself, code that render
pages, code that do scientific computation, code that do
rendering, code that crunches numbers, code that do GUI, code for
command line utilities, code that do whatever, but for code that
wirte code, yeah, they trully do the trick !

Unfortunately, we _do_ have a similar problem with template
constraints if we insist on putting all of the function's
requirements in its template constraint rather than just its
immediate requirements. But at least with template constraints,
if the top-level constraint is missing something that a
function somewhere deeper in the stack requires, then you get a
compilation error only when the type being passed in doesn't
pass the constraint on the function deeper in the stack. So, if
you adjust a template constraint, it will only break code that
doesn't work with the new constraint - even code that uses that
function indirectly (possibly even quite deeply in a call
stack, far from their own code) won't break due to the change,
unless the type being used doesn't pass the new constraint. And
when it does fail, the errors may not be pretty, but they do
tell you exactly what's required to figure out what's wrong
when you look at the source code. Whereas the traits/concepts
solution would break _all_ code that used the function that was
adjusted (even indirectly), not just the code that wouldn't
work with the new requirements.

I discussed this quite a bit more elsewhere in this thread:
http://forum.dlang.org/post/lsxidsyweczhojoucnsw forum.dlang.org

- Jonathan M Davis


Jul 27 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 7/27/2015 12:53 PM, deadalnix wrote:
So, if I translate to regular D, here is what I get :

I asked how you'd solve the problem with interfaces.

Jul 27 2015
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/24/15 6:12 PM, deadalnix wrote:
The most intriguing part of this conversation is that the argument made
about unitests and complexity are the very same than for dynamic vs
strong typing (and there is hard data that strong typing is better).

No, that's not the case at all. There is a distinction: in dynamic
typing the error is deferred to run time, in this discussion the error
is only deferred to instantiation time. -- Andrei

Jul 25 2015
"Brendan Zabarauskas" <bjzaba yahoo.com.au> writes:
On Saturday, 25 July 2015 at 13:37:15 UTC, Andrei Alexandrescu
wrote:
On 7/24/15 6:12 PM, deadalnix wrote:
The most intriguing part of this conversation is that the
about unitests and complexity are the very same than for
dynamic vs
strong typing (and there is hard data that strong typing is
better).

No, that's not the case at all. There is a distinction: in
dynamic typing the error is deferred to run time, in this
discussion the error is only deferred to instantiation time. --
Andrei

Runtime errors are a usability problem for users and
maintianability problem for developers. Instatiation time errors
are a maintianability problem for library authors and a usability
problem for developers. I would argue that the latter is better
than the former, but the poor developer experience of using
Phobos is what made me move away from D a couple of years ago.

Jul 25 2015
On Saturday, 25 July 2015 at 13:37:15 UTC, Andrei Alexandrescu
wrote:
On 7/24/15 6:12 PM, deadalnix wrote:
The most intriguing part of this conversation is that the
about unitests and complexity are the very same than for
dynamic vs
strong typing (and there is hard data that strong typing is
better).

No, that's not the case at all. There is a distinction: in
dynamic typing the error is deferred to run time, in this
discussion the error is only deferred to instantiation time. --
Andrei

In case 1, it is argued that unitest check runtime, so we are
good, and in case 2, unitest check instantiation, so we are good.

That is the very same argument and it is equally bogus in both
cases.

Jul 25 2015
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/25/15 7:10 PM, deadalnix wrote:
On Saturday, 25 July 2015 at 13:37:15 UTC, Andrei Alexandrescu wrote:
On 7/24/15 6:12 PM, deadalnix wrote:
The most intriguing part of this conversation is that the argument made
about unitests and complexity are the very same than for dynamic vs
strong typing (and there is hard data that strong typing is better).

No, that's not the case at all. There is a distinction: in dynamic
typing the error is deferred to run time, in this discussion the error
is only deferred to instantiation time. -- Andrei

In case 1, it is argued that unitest check runtime, so we are good, and
in case 2, unitest check instantiation, so we are good.

That is the very same argument and it is equally bogus in both cases.

I don't see it as the same argument. I do agree that applied to runtime
it is specious. -- Andrei

Jul 26 2015
"Bruno Queiroga" <brunoqueiroga gmail.com> writes:
On Friday, 24 July 2015 at 04:42:59 UTC, Walter Bright wrote:
On 7/23/2015 3:12 PM, Dicebot wrote:
On Thursday, 23 July 2015 at 22:10:11 UTC, H. S. Teoh wrote:
OK, I jumped into the middle of this discussion so probably
I'm speaking
totally out of context...

This is exactly one major advantage of Rust traits I have been
trying to
explain, thanks for putting it up in much more understandable
way :)

Consider the following:

int foo(T: hasPrefix)(T t) {
t.prefix();    // ok
bar(t);        // error, hasColor was not specified for T
}

void bar(T: hasColor)(T t) {
t.color();
}

Now consider a deeply nested chain of function calls like this.
At the bottom, one adds a call to 'color', and now every
function in the chain has to add 'hasColor' even though it has
nothing to do with the logic in that function. This is the pit
that Exception Specifications fell into.

I can see these possibilities:

1. Require adding the constraint annotations all the way up the
call tree. I believe that this will not only become highly
annoying, it might make generic code impractical to write
(consider if bar was passed as an alias).

Could not the compiler just issue a warning of implicit use of
properties/functions like the C's implicit function declaration
warning? And some form of "cast" (or test) could be done to make
the use explicit:

int foo(T: hasPrefix)(T t) {
t.prefix();    // ok
t.color();     // Compiler warning: implicit.
bar(t);        // Compiler warning: implicit.
}

void bar(T: hasColor)(T t) {
t.color();
t.prefix();    // Compiler warning: implicit.
}

-------------

int foo(T: hasPrefix)(T t) {
t.prefix();                  // ok
(cast(hasColor) t).color();  // ok: explicit.
bar(cast(hasColor) t);       // ok: explicit.
}

void bar(T: hasColor)(T t) {
t.color();
}

This seems enough to avoid bugs.

Note: sorry for the bad english writing.

Best regards,
Bruno Queiroga.

Jul 24 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 7/24/2015 4:19 PM, Bruno Queiroga wrote:
Could not the compiler just issue a warning

Please, no half features. Code should be correct or not.

of implicit use of properties/functions like the C's implicit function
declaration warning?

C warnings are not part of Standard C. They're extensions only, and vary widely
from compiler to compiler.

Jul 24 2015
"Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Saturday, 25 July 2015 at 01:15:52 UTC, Walter Bright wrote:
On 7/24/2015 4:19 PM, Bruno Queiroga wrote:
Could not the compiler just issue a warning

Please, no half features. Code should be correct or not.

Yeah. I wish that no one had ever managed to convince you to add
-w and -wi to dmd. :)

-w is particularly bad, since it affects what code will and won't
compile, which affects stuff like is expressions, so -w can
actually affect the behavior of your program, even if it doesn't
actually result in any errors being printed out. We really should
just make it do the same thing as -wi IMHO.

In any case, I strongly concur with the idea that warnings are a
bad idea. A good developer is going to fix all warnings, which
ultimately makes them the same as errors anyway, and a bad
developer will just leave them there and make them useless,
because there will be too many of them to read.

- Jonathan M Davis

Jul 24 2015
"Bruno Queiroga" <brunoqueiroga gmail.com> writes:
On Saturday, 25 July 2015 at 01:15:52 UTC, Walter Bright wrote:
On 7/24/2015 4:19 PM, Bruno Queiroga wrote:
Could not the compiler just issue a warning

Please, no half features. Code should be correct or not.

...
... (consider if bar was passed as an alias)

trait S1 { void m1(); }
trait S2 : S1 { void m2(); }

void bar(S : S2)(S s) {
s.m1(); // ok...
s.m2(); // ok...
}

template foo(S : S1) {
static void foo(alias void f(S))(S s) {
s.m1(); // ok...
s.m2(); // ERROR: S1 is the base trait of S
f(s);   // Ignored: typeof(s) is S of f(S)
}
}

void main(string[] args) {
S2 s2;

alias foo!S2 fooS2;
alias bar!S2 barS2;

fooS2!barS2(s2);
}

??

Jul 24 2015
"Bruno Queiroga" <brunoqueiroga gmail.com> writes:
On Saturday, 25 July 2015 at 02:55:16 UTC, Bruno Queiroga wrote:
On Saturday, 25 July 2015 at 01:15:52 UTC, Walter Bright wrote:
On 7/24/2015 4:19 PM, Bruno Queiroga wrote:
Could not the compiler just issue a warning

Please, no half features. Code should be correct or not.

...
... (consider if bar was passed as an alias)

trait S1 { void m1(); }
trait S2 : S1 { void m2(); }

For completeness:

struct Struct1 { void m1(){}; void m2(){}; void m3(){}; void
m4(){}; }

trait S1 { void m1(); }
trait S2 : S1 { void m2(); }
trait S3 : S2 { void m3(); }
trait S4 { void m4(); } // no "inheritance"

void bar(S : S2)(S s) {
s.m1(); // ok...
s.m2(); // ok...
s.m3(); // ERROR!
(cast(S3) s).m3();    // OK!  (Struct1 has m3())
// (cast(S4) s).m4(); // OK!! (Struct1 has m4())
}

template foo(S : S1) {
static void foo(alias void f(S))(S s) {
s.m1(); // ok...
s.m2(); // ERROR: S1 is the base trait of S
f(s);   // OK! typeof(s) is (compatible with) S of f(S)
//                  (structurally or nominally)
}
}

void main(string[] args) {
Struct1 struct1;

alias foo!Struct1 fooSt1;
alias bar!Struct1 barSt1;

fooSt1!barSt1(struct1);
}

Is this reasonable?

Jul 24 2015
"Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Friday, 24 July 2015 at 04:42:59 UTC, Walter Bright wrote:
On 7/23/2015 3:12 PM, Dicebot wrote:
On Thursday, 23 July 2015 at 22:10:11 UTC, H. S. Teoh wrote:
OK, I jumped into the middle of this discussion so probably
I'm speaking
totally out of context...

This is exactly one major advantage of Rust traits I have been
trying to
explain, thanks for putting it up in much more understandable
way :)

Consider the following:

int foo(T: hasPrefix)(T t) {
t.prefix();    // ok
bar(t);        // error, hasColor was not specified for T
}

void bar(T: hasColor)(T t) {
t.color();
}

Now consider a deeply nested chain of function calls like this.
At the bottom, one adds a call to 'color', and now every
function in the chain has to add 'hasColor' even though it has
nothing to do with the logic in that function. This is the pit
that Exception Specifications fell into.

I can see these possibilities:

1. Require adding the constraint annotations all the way up the
call tree. I believe that this will not only become highly
annoying, it might make generic code impractical to write
(consider if bar was passed as an alias).

2. Do the checking only for 1 level, i.e. don't consider what
bar() requires. This winds up just pulling the teeth of the
point of the constraint annotations.

3. Do inference of the constraints. I think that is
indistinguishable from not having annotations as being
exclusive.

Anyone know how Rust traits and C++ concepts deal with this?

everything in the constraint, then the user is going to get an
error buried in your templated code somewhere rather than in
their code, which is _not_ user friendly and is why we usually
try and put everything required in the template constraint. On
the other hand, you're very much right in that this doesn't scale
if you have enough levels of template constraints, especially if
some of the constraints in the functions being called internally
change. And yet, the caller needs to know what the requirements
are of the template or templated function actually are when they
pass it something. So, it does kind of need to be at the top
level from that aspect of usability as well. So, this is just
plain ugly regardless.

One option which would work at least some of the time would be to
do something like

void foo(T)(T t)
if(hasPrefix!T && is(typeof(bar(t))))
{
t.prefix();
bar(t);
}

void bar(T)(T t)
if(hasColor!T)
{
t.color();
}

then you don't have to care what the current constraint for bar
is, and it still gets checked in foo's template constraint.

...

Actually, I just messed around with some of this to see what
error messages you get when foo doesn't check for bar's
constraints in its template constraint, and it's a _lot_ better
than it used to be. This code

void foo(T)(T t)
if(hasPrefix!T)
{
t.prefix();
bar(t);
}

void bar(T)(T t)
if(hasColor!T)
{
t.color();
}

struct Both { void prefix() { } void color() { } }

struct OneOnly { void prefix() { } }

enum hasPrefix(T) = __traits(hasMember, T, "prefix");
enum hasColor(T) = __traits(hasMember, T, "color");

void main()
{
foo(Both.init);
bar(Both.init);
foo(OneOnly.init);
}

results in these error messages:

q.d(5): Error: template q.bar cannot deduce function from
argument types !()(OneOnly), candidates are:
q.d(8):        q.bar(T)(T t) if (hasColor!T)
q.d(25): Error: template instance q.foo!(OneOnly) error
instantiating

It tells you exactly which line in your code is wrong (which it
didn't used to when the error was inside the template), and it
clearly gives you the template constraint which is failing,
whereas if you foo tests for bar in its template constraint, you
get this

q.d(25): Error: template q.foo cannot deduce function from
argument types !()(OneOnly), candidates are:
q.d(1):        q.foo(T)(T t) if (hasPrefix!T &&
is(typeof(bar(t))))

And that doesn't tell you anything about what bar requires.
Actually putting bar's template constraint in foo's template
constraint would fix that, but then you wouldn't necessarily know
which is failing, and you have the maintenance problem caused by
having to duplicate bar's constraint.

So, I actually think that how the current implementation reports
errors makes it so that maybe it's _not_ a good idea to put all
of the sub-constraints within the top-level constraint, because
it actually makes it harder to figure out what you've done wrong.
Unfortunately, it probably requires that you look at the source
code of the templated function that you're calling regardless,
since the error message doesn't actually make it clear that it's
the argument that you passed to foo that's being passed to bar
rather than an actual bug in foo (and to make matters more
complicated, it could actually be something that came from what
you passed to foo rather than actually being what you passed in).
So, maybe we could improve the error messages further to make it
clear that it was what you passed in or something about where it
came from so that you wouldn't necessarily have to look at the
source code, and if so, I think that that solves the problem
reasonably well. It would avoid the maintenance problem of having
to propagate the constraints, and it would actually give clearer
error messages than propagating the constraints. And having
overly complicated template constraints is one of the most
annoying aspects of dealing with template constraints, because it
makes it a lot harder to figure out why they're failing. So,
_not_ putting the sub-constraints in the top-level constraint
could make it easier to figure out what's gone wrong.

So, honestly, I think that we have the makings here of a far
better solution than trying to put everything in the top-level
template constraint. This could be a good part of the solution
that we've needed to improve error-reporting associated with
template constraints.

In any case, looking at this, I have to agree with you that this
is the same problem you get with checked exceptions / exceptions
specifications - only worse really, because you can't do "throws
Exception" and be done with it like you can in Java (as hideous
as that is). Rather, you're forced to do the equivalent of
listing all of the exception types being thrown and maintain that
list as the code changes - i.e. you have to make the top-level
template constraint list all of the sub-constraints and keep that
list up-to-date as the sub-constraints change, which is a mess,
especially with deep call stacks of templated functions.

- Jonathan M Davis

Jul 24 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 7/24/2015 10:59 PM, Jonathan M Davis wrote:
In any case, looking at this, I have to agree with you that this is the same
problem you get with checked exceptions / exceptions specifications - only
worse
really, because you can't do "throws Exception" and be done with it like you
can
in Java (as hideous as that is). Rather, you're forced to do the equivalent of
listing all of the exception types being thrown and maintain that list as the
code changes - i.e. you have to make the top-level template constraint list all
of the sub-constraints and keep that list up-to-date as the sub-constraints
change, which is a mess, especially with deep call stacks of templated
functions.

Phew, finally, someone understands what I'm talking about! I'm really bad at
explaining things to people that they aren't already familiar with.

I'm not sure, but I suspect this problem may cripple writing generic library
functions that do one operation and then forward to the next (unknown in

It also may completely torpedo Andrei's Design By Introspection technique.

Jul 25 2015
"Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Saturday, 25 July 2015 at 10:01:43 UTC, Walter Bright wrote:
On 7/24/2015 10:59 PM, Jonathan M Davis wrote:
In any case, looking at this, I have to agree with you that
this is the same
problem you get with checked exceptions / exceptions
specifications - only worse
really, because you can't do "throws Exception" and be done
with it like you can
in Java (as hideous as that is). Rather, you're forced to do
the equivalent of
listing all of the exception types being thrown and maintain
that list as the
code changes - i.e. you have to make the top-level template
constraint list all
of the sub-constraints and keep that list up-to-date as the
sub-constraints
change, which is a mess, especially with deep call stacks of
templated functions.

Phew, finally, someone understands what I'm talking about! I'm
really bad at explaining things to people that they aren't

I'm not sure, but I suspect this problem may cripple writing
generic library functions that do one operation and then
forward to the next (unknown in advance) operation in a chain.

Well, the caller then has to do deal with the fact that the
result doesn't work with the next call in the chain, and at least
in that case, the failures are at their level, not buried inside
of calls that they're making. And this is exactly the sort of
problem that we've had for quite a while where you try and do
something like

auto result = rndGen().map!(a % 10).take(10).sort();

The result of take isn't random-access, but sort requires
random-access, so you get a compilation failure - but it's
clearly on this line and not inside of one of the calls that
you're making, so it's pretty straightforward.

I guess that where the problem might come in (and maybe this is
what you meant) is when you try and do a chain like that inside
of a templated function, and you end up with a compilation
failure, because the argument to that function resulted in one of
the items in the chain failing. But I'm not sure that that's any
different really from a line with a single function call failing
due to the outer function's argument not working with it.

It also may completely torpedo Andrei's Design By Introspection
technique.

I'm not sure what you mean here. Design by Introspection seems to
be primarily for implementing optional functionality, and it
couldn't be in the outer template constraint regardless, because
it's optional. So, I don't really see how whether or not we put
all of the sub-constraints in the main template constraint really
affects DbI.

I do have to wonder about what Andrei means to do with DbI
though, since he keeps bringing it up like it solves everything
and should be used everywhere, whereas it seems like it would
only to apply to certain areas where you're dealing with optional
functionality, and a lot of code wouldn't benefit from it all.
We're essentially using it with ranges already when we're
implementing algorithms differently based on what type of range
we're given or what extra capabilities the range has, so it
obviously is showing its usefulness there, but the allocators is
the only other case that I can think of at the moment where it
would make sense to use it heavily. He sounds like he wants to
use it everywhere, which I don't get at all.

- Jonathan M Davis

Jul 25 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 7/25/2015 3:29 AM, Jonathan M Davis wrote:
We're essentially using it with ranges already when we're implementing
algorithms differently based on what type of range we're given or what extra
capabilities the range has, so it obviously is showing its usefulness there,

That's right. We've already been doing it in a haphazard manner, what Andrei is
doing is recognizing the technique, naming it, and thinking about how to
formalize it, organize it, and determine best practices.

It's like going from an ad-hoc table of function pointers to recognizing that
one is doing OOP.

but the allocators is the only other case that I can think of at the moment
where it
would make sense to use it heavily.

Containers are another fairly obvious use case.

Jul 25 2015
"Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Saturday, 25 July 2015 at 20:35:08 UTC, Walter Bright wrote:
On 7/25/2015 3:29 AM, Jonathan M Davis wrote:
We're essentially using it with ranges already when we're
implementing
algorithms differently based on what type of range we're given
or what extra
capabilities the range has, so it obviously is showing its
usefulness there,

That's right. We've already been doing it in a haphazard
manner, what Andrei is doing is recognizing the technique,
naming it, and thinking about how to formalize it, organize it,
and determine best practices.

It's like going from an ad-hoc table of function pointers to
recognizing that one is doing OOP.

Well, it'll be interesting to see what he comes up with.

but the allocators is the only other case that I can think of
at the moment where it
would make sense to use it heavily.

Containers are another fairly obvious use case.

Yes. There are definitely places that DbI is going to be huge. I
just have a hard time coming up with them. So, while I agree that
it's a fantastic tool, I'm just not convinced yet that it's going
to be one that's widely applicable. I guess that we'll just have
to wait and see what Andrei comes up with and where others take
it from there. But it's definitely something that D can do rather
easily and most other languages can't do at all, so it's a big
win for us in that regard, especially if it does turn out to be
widely applicable.

On a related note, while I'd noticed it on some level, I don't
think that it had ever clicked for me how restrictive interfaces
are before this discussion. The simple fact that you can't ask
for two of them at once really reduces how reusable your code can
be. So, templatizing those checks rather than using interfaces is
huge. And DbI is an extension of that. There's likely a lot of
unplumbed depth there.

- Jonathan M Davis

Jul 27 2015
=?UTF-8?Q?Tobias=20M=C3=BCller?= <troplin bluewin.ch> writes:
"Jonathan M Davis" <jmdavisProg gmx.com> wrote:
On a related note, while I'd noticed it on some level, I don't think that
it had ever clicked for me how restrictive interfaces are before this
discussion. The simple fact that you can't ask for two of them at once
really reduces how reusable your code can be. So, templatizing those
checks rather than using interfaces is huge. And DbI is an extension of
that. There's likely a lot of unplumbed depth there.

One big improvement of traits over interfaces is, that you can implement
traits for types after they are defined, even for types that you didn't
define yourself.

So in Rust, if your function needs two unrelated interfaces (trait objects
== dynamic polymorphism) A and B, you can easily define a new trait C that
depends on A and B and implement C for all types that also implement A and
B:

trait A {...}
trait B {...}

trait C : A,B { }

impl<T: A+B> C for T { }

fn myFunction(c: C) {...}

For generics you don't even need that:

fn myFunction<T: A+B>(t: T) {...}

Tobi

Jul 27 2015
"Enamex" <enamex+d outlook.com> writes:
On Monday, 27 July 2015 at 19:21:10 UTC, Tobias Müller wrote:
trait A {...}
trait B {...}

trait C : A,B { }

impl<T: A+B> C for T { }

fn myFunction(c: C) {...}

Tobi

Has to be:
fn my_function(c: &C) { ... }
actually, because trait objects can only be passed by
reference/borrowed-pointer.

Jul 27 2015
"Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Monday, 27 July 2015 at 19:21:10 UTC, Tobias Müller wrote:
"Jonathan M Davis" <jmdavisProg gmx.com> wrote:
On a related note, while I'd noticed it on some level, I don't
think that it had ever clicked for me how restrictive
interfaces are before this discussion. The simple fact that
you can't ask for two of them at once really reduces how
reusable your code can be. So, templatizing those checks
rather than using interfaces is huge. And DbI is an extension
of that. There's likely a lot of unplumbed depth there.

One big improvement of traits over interfaces is, that you can
implement traits for types after they are defined, even for
types that you didn't define yourself.

So in Rust, if your function needs two unrelated interfaces
(trait objects == dynamic polymorphism) A and B, you can easily
define a new trait C that depends on A and B and implement C
for all types that also implement A and B:

trait A {...}
trait B {...}

trait C : A,B { }

How is that any different from interfaces? You can do exactly the
same thing with them.

impl<T: A+B> C for T { }

fn myFunction(c: C) {...}

For generics you don't even need that:

fn myFunction<T: A+B>(t: T) {...}

As long as you can list the two interfaces/traits/concepts that
you require separately, then you're okay. But as soon as you have
to create a new one that combines two or more
interfaces/traits/concepts, then that doesn't scale. interfaces
force that. I wouldn't expect traits or concepts to, because
they're compile-time constructs, but that would depend on how the
language defines them.

It might make sense to create combined traits/concepts for the
cases where the operations in question are often going to be
required together, but in general, it's going to scale far better
to require them separately rather than require a combined
trait/concept. Otherwise, you get a combinatorial explosion of
traits/concepts as you combine them to create new traits/concepts
- either that, or you code doesn't end up being very generic,
because it's frequently using traits/concepts that require more
operations than it actually uses, meaning that it will work with
fewer types than it would otherwise.

In general, templates shouldn't be requiring more operations than
they actually use, or they won't be as reusable as they
could/should be. And that implies that the list of required
operations should be kept to what's actually required rather than
using traits/concepts that require those operations plus others.

- Jonathan M Davis

Jul 27 2015
=?UTF-8?Q?Tobias=20M=C3=BCller?= <troplin bluewin.ch> writes:
"Jonathan M Davis" <jmdavisProg gmx.com> wrote:
trait C : A,B { }

How is that any different from interfaces? You can do exactly the same thing
with them.

impl<T: A+B> C for T { }

^^^
That's the important line.
You can define *and implement* the trait C for *all types that already
implement A and B*.
That's definitely not possible with interfaces. The difference between
traits and interfaces is, that trait implementation is separate from the
type definition.

fn myFunction(c: C) {...}

[...]
But as soon as you have to create a new one that combines two or more
interfaces/traits/concepts, then that doesn't scale. interfaces force
that. I wouldn't expect traits or concepts to, because they're
compile-time constructs, but that would depend on how the language defines
them.

It might make sense to create combined traits/concepts for the cases
where the operations in question are often going to be required together,
but in general, it's going to scale far better to require them separately
rather than require a combined trait/concept. Otherwise, you get a
combinatorial explosion of traits/concepts as you combine them to create
new traits/concepts - either that, or you code doesn't end up being very
generic, because it's frequently using traits/concepts that require more
operations than it actually uses, meaning that it will work with fewer
types than it would otherwise.

Because you can implement the combined trait directly for all separate
subtraits, you can define that new trait just for the use of one function.

Tobi

Jul 28 2015
"Tofu Ninja" <emmons0 purdue.edu> writes:
On Saturday, 25 July 2015 at 10:01:43 UTC, Walter Bright wrote:
Phew, finally, someone understands what I'm talking about! I'm
really bad at explaining things to people that they aren't

I'm not sure, but I suspect this problem may cripple writing
generic library functions that do one operation and then
forward to the next (unknown in advance) operation in a chain.

It also may completely torpedo Andrei's Design By Introspection
technique.

Actually I don't think the problem you state is actually a
problem at all, even disregarding my previous argument(which is
still think is valid). The key point is that it would be opt-in
and it would trickle down, not up. Normal templates without the
concept/traits/interface things would still be able to call
functions with the extra constraints with out needing to add it
to them selves.

For instance, say the syntax to specialize on one of these
concept/traits/interface things was the same as specializing on a
class, eg:

void foo(T : inputRange)(T x){}

Calling foo from any where would still be the same, even calling
it from other templates with out the concept/traits/interface
things. eg the following would work:

void bar(T)(T x){ foo(x); }

Because bar is a normal template, it still has no choice but to
assume that T can do any thing we ask it to do, because that is
what we have always done with templates. So the template happily
assumes that passing x into foo will work. If for some reason you
pass a type in that is not an inputRange, then it will fail at
instantiation. So far it is the same as the constraints we have
now.

Ok, here is where it is different.

In side of foo, it would be illegal to do anything other than
inputRange stuff with x. For instance the following would be
illegal:

void foo(T : inputRange)(T x)
{
x.something(); // ERROR!
}

The real kicker here, is that THAT error can be detected without
ever instantiating foo. No need to rely on unittests, which may
or may not catch it depending on which types we use.

Ok now take it a step further. Say we have the following:

void foo(T : inputRange)(T x)
{
bar(x); // ERROR!
}

void bar(T : someOtherInterface)(T x){}

The previous would error! Why? Because foo only assumes x can do
inputRange things, and when you pass it into bar it asks it to do
someOtherInterface which it doesn't know it can do! This all
would still error with out every instantiating the template!

Also the following should also error:

void foo(T : inputRange)(T x)
{
bar(x); // ERROR!
}

void bar(T)(T x) if(isSomeOtherInterface!T) {}

Why? Because from inside foo, it is only assumed that x can do
inputRange things, when it gets passed into bar the constraint
will ask it to do non inputRange things and fail! Still with out
foo being instantiated! Even something like the following should
error:

void foo(T : inputRange)(T x)
{
bar(x); // ERROR!
}

void bar(T)(T x) { x.something_inputranges_dont_have(); }

This would error for the same reasons as before, bar asked x to
do non input range things. In contrast the following would be ok!

void foo(T : inputRange)(T x)
{
bar(x); // Ok
}

void bar(T)(T x) { foreach(i;x){} }

That still works because it is known that x can do inputRange
things, so its ok! Woo!

This is awesome right? All these errors being caught without ever
instantiating the templates. You should still instantiate them
and test them, but the value is that the errors were caught
sooner, with out even instantiating them.

The main difference here is that a normal template assumes that a
type can do anything until it actually gets instantiated. Add
some constraints(the normal ones we have now) and you can filter
for things that don't do X, but you still assume that the type
can do any thing else in addition to X. On the other hand, the
concept/traits/interface things would be as conservative as
possible and only assume a type can do what its
concept/traits/interface things say it can do.

In summery, the concept/traits/interface things would not require
them to applied to the whole tree. Doing so would break how
templates work now and really just does not make sense unless
things were being redone from scratch. They are opt-in! In
addition to that, they catch a bunch of bugs in templates before
they are ever instantiated! This is a good thing.

Jul 25 2015
On Saturday, 25 July 2015 at 10:01:43 UTC, Walter Bright wrote:
On 7/24/2015 10:59 PM, Jonathan M Davis wrote:
In any case, looking at this, I have to agree with you that
this is the same
problem you get with checked exceptions / exceptions
specifications - only worse
really, because you can't do "throws Exception" and be done
with it like you can
in Java (as hideous as that is). Rather, you're forced to do
the equivalent of
listing all of the exception types being thrown and maintain
that list as the
code changes - i.e. you have to make the top-level template
constraint list all
of the sub-constraints and keep that list up-to-date as the
sub-constraints
change, which is a mess, especially with deep call stacks of
templated functions.

Phew, finally, someone understands what I'm talking about! I'm
really bad at explaining things to people that they aren't

I'm not sure, but I suspect this problem may cripple writing
generic library functions that do one operation and then
forward to the next (unknown in advance) operation in a chain.

It also may completely torpedo Andrei's Design By Introspection
technique.

This only make sense under the premise that both technique are
mutually exclusive, which they aren't, and that no introspection
can be done, which nobody argues against (I'm not sure what Rust
provide here, but if they don't allow it, they'll regret it).

Jul 25 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 7/23/2015 3:06 PM, H. S. Teoh via Digitalmars-d wrote:
OK, I jumped into the middle of this discussion so probably I'm speaking
totally out of context... but anyway, with regards to template code, I
agree that it ought to be thoroughly tested by at least instantiating
the most typical use cases (as well as some not-so-typical use cases).

I argue that every code line of the template must at least have been
instantiated at some point by the test suite. Anything less is, frankly,
unprofessional.

A lot of Phobos bugs lurk in rarely-used template branches that are not
covered by the unittests.

Generally when I work on a Phobos template, I upgrade it to 100% unit test
coverage. This should be a minimum bar for all Phobos work. We ought to be
ashamed of anything less.

Instantiating all branches is only part of the solution, though. A lot
of Phobos bugs also arise from undetected dependencies of the template
code on the specifics of the concrete types used to test it in the
unittests.  The template passes the unittest but when you instantiate it
with a type not used in the unittests, it breaks. For instance, a lot of
range-based templates are tested with arrays in the unittests. Some of
these templates wrongly depend on array behaviour (as opposed to being
confined only to range API operations) while their signature constraints
indicate only the generic range API. As a result, when non-array ranges
are used, it breaks. Sometimes bugs like this can lurk undetected for a
long time before somebody one day happens to instantiate it with a range
type that violates the hidden assumption in the template code.

I agree that the constraint system is not checked against the actual body of
the
template. Dicebot brought that up as well. Some attention should be paid in the
unit tests to using types that are minimal implementations of the constraints.

That said, it is a pipe dream to believe that if something matches the function
signatures, that it is correct and will work without ever having been tested.

If we had a Concepts-like construct in D, where template code is
statically constrained to only use, e.g., range API when manipulating an
incoming type, a lot of these bugs would've been caught.

In fact, I'd argue that this should be done for *all* templates -- for
example, a function like this ought to be statically rejected:

auto myFunc(T)(T t) { return t + 1; }

because it assumes the validity of the + operation on T, but T is not
constrained in any way, so it can be *any* type, most of which,
arguably, do not support the + operation.

It's a valid point, but I'd counter that it'd be pretty tedious and burdensome.
D isn't meant to be a bondage & discipline language. The failed exception
specifications (Java and C++) comes to mind.

If the compiler outright rejected any operation on T that hasn't been
explicitly tested for, *then* we will have eliminated a whole class of
template bugs. Wrong code like the last example above would be caught as
soon as the compiler compiles the body of myFunc.

Yeah, but few would like programming in such a nagging, annoying language. Note
that if you do instantiate with a type that doesn't support those operations,
it
isn't the end of the world - you'll still get a compile time error message.

Jul 23 2015
"H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Thu, Jul 23, 2015 at 05:34:20PM -0700, Walter Bright via Digitalmars-d wrote:
On 7/23/2015 3:06 PM, H. S. Teoh via Digitalmars-d wrote:
OK, I jumped into the middle of this discussion so probably I'm
speaking totally out of context... but anyway, with regards to
template code, I agree that it ought to be thoroughly tested by at
least instantiating the most typical use cases (as well as some
not-so-typical use cases).

I argue that every code line of the template must at least have been
instantiated at some point by the test suite. Anything less is,
frankly, unprofessional.

I agree, and I didn't claim otherwise.

A lot of Phobos bugs lurk in rarely-used template branches that are
not covered by the unittests.

Generally when I work on a Phobos template, I upgrade it to 100% unit
test coverage. This should be a minimum bar for all Phobos work. We
ought to be ashamed of anything less.

Agreed.

Instantiating all branches is only part of the solution, though. A
lot of Phobos bugs also arise from undetected dependencies of the
template code on the specifics of the concrete types used to test it
in the unittests.  The template passes the unittest but when you
instantiate it with a type not used in the unittests, it breaks. For
instance, a lot of range-based templates are tested with arrays in
the unittests. Some of these templates wrongly depend on array
behaviour (as opposed to being confined only to range API operations)
while their signature constraints indicate only the generic range
API. As a result, when non-array ranges are used, it breaks.
Sometimes bugs like this can lurk undetected for a long time before
somebody one day happens to instantiate it with a range type that
violates the hidden assumption in the template code.

I agree that the constraint system is not checked against the actual
body of the template. Dicebot brought that up as well. Some attention
should be paid in the unit tests to using types that are minimal
implementations of the constraints.

That said, it is a pipe dream to believe that if something matches the
function signatures, that it is correct and will work without ever
having been tested.

I didn't say that this one thing alone will singlehandedly solve all of
our template testing woes. Obviously, it cannot catch semantic errors --
you use all the valid range API operations, but you use them in the
wrong order, say, or in a way that doesn't accomplish what the code is
supposed to do.  I think it's a given that you still need to adequately
unittest the code just like you would non-template code.

Nevertheless, this does help to eliminate an entire class of latent
template bugs -- hidden dependencies on the incoming type that are not
covered by the function's contract (i.e., signature constraints).
Relying on the programmer to always use types with minimal functionality
in the unittests is programming by convention, and you know very well
how effective that is. Without enforcement, we have no way of being sure
that our tests are actually adequate. An untested branch of template
code can be detected by using -cov, but performing an operation on an
incoming type without checking for it in the sig constraints cannot be
detected except by reading every line of code. The unittest may have
inadvertently used a type with a superset of functionality, but since
this is never enforced (and the current language provides no way to
actually enforce it) we can never be sure -- we're just taking it on
faith that the tests have covered all bases.

With actual language enforcement, we can actually provide some
guarantees. It doesn't solve *all* the problems, but it does solve a
significant subset of them.

If we had a Concepts-like construct in D, where template code is
statically constrained to only use, e.g., range API when manipulating
an incoming type, a lot of these bugs would've been caught.

In fact, I'd argue that this should be done for *all* templates --
for example, a function like this ought to be statically rejected:

auto myFunc(T)(T t) { return t + 1; }

because it assumes the validity of the + operation on T, but T is not
constrained in any way, so it can be *any* type, most of which,
arguably, do not support the + operation.

It's a valid point, but I'd counter that it'd be pretty tedious and
burdensome. D isn't meant to be a bondage & discipline language. The
failed exception specifications (Java and C++) comes to mind.

If the compiler outright rejected any operation on T that hasn't been
explicitly tested for, *then* we will have eliminated a whole class
of template bugs. Wrong code like the last example above would be
caught as soon as the compiler compiles the body of myFunc.

Yeah, but few would like programming in such a nagging, annoying
language.

I have trouble thinking of a template function that's actually *correct*
when its sig constraints doesn't specify what operations are valid on
the incoming type. Can you give an example?

If such code is wrong, I'd say the language *should* reject it.

If you think that's too "bondange and discipline", what about a generic
wildcard sig constraint clause that says basically "type T works with
any operation you imagine"? Then those programmers who are too lazy to
figure out what operations are required for the function can just slap
this on, and continue writing broken code to their heart's content.

Note that if you do instantiate with a type that doesn't support those
operations, it isn't the end of the world - you'll still get a compile
time error message.

Yes, but by then it's the user that is faced with an inscrutable
template error. If I'm a library author, I'd like to be able to find all
these bugs *before* shipping my code to the customers.

T

--
People tell me I'm stubborn, but I refuse to accept it!

Jul 23 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 7/23/2015 6:05 PM, H. S. Teoh via Digitalmars-d wrote:
It doesn't solve *all* the problems, but it does solve a
significant subset of them.

The worst case of not having this feature is a compile time error. Not a
runtime
error, undetectable error, or silent corruption. A compile time error.

I also believe that you underestimate the nuisance significance of requiring
the
constraints cover 100% of everything the template body does.

Experience with something similar is with exception specifications. Even
advocates of ES found themselves writing obviously crap code to work around the
issue, because ES was so damned annoying.

I know a lot of the programming community is sold on exclusive constraints (C++
concepts, Rust traits) rather than inclusive ones (D constraints). What I don't
see is a lot of experience actually using them long term. They may not turn out
so well, like ES.

Jul 23 2015
=?UTF-8?Q?Tobias=20M=C3=BCller?= <troplin bluewin.ch> writes:
Walter Bright <newshound2 digitalmars.com> wrote:
I know a lot of the programming community is sold on exclusive
constraints (C++ concepts, Rust traits) rather than inclusive ones (D
constraints). What I don't see is a lot of experience actually using them
long term. They may not turn out so well, like ES.

Haskell has type classes since ~1990.

Tobi

Jul 23 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 7/23/2015 10:49 PM, Tobias Müller wrote:
Walter Bright <newshound2 digitalmars.com> wrote:
I know a lot of the programming community is sold on exclusive
constraints (C++ concepts, Rust traits) rather than inclusive ones (D
constraints). What I don't see is a lot of experience actually using them
long term. They may not turn out so well, like ES.

Haskell has type classes since ~1990.

if you don't believe me :-) Such languages have their place and adherents, but
I
don't think D is directed that way.

Exception Specifications were proposed for Java and C++ by smart, experienced
programmers. It looked great on paper, and in the simple examples in the
proposals. The unfit nature of it only emerged years later. Concepts and traits
appear to me to suffer from the same fault.

Jul 23 2015
Timon Gehr <timon.gehr gmx.ch> writes:
On 07/24/2015 08:56 AM, Walter Bright wrote:
On 7/23/2015 10:49 PM, Tobias Müller wrote:
Walter Bright <newshound2 digitalmars.com> wrote:
I know a lot of the programming community is sold on exclusive
constraints (C++ concepts, Rust traits) rather than inclusive ones (D
constraints). What I don't see is a lot of experience actually using
them
long term. They may not turn out so well, like ES.

Haskell has type classes since ~1990.

Haskell is sometimes described as a bondage-and-discipline language.
Google it if you don't believe me :-) Such languages have their place
and adherents, but I don't think D is directed that way.

Also, if there are carrots in your meal, it is vegetarian.

Exception Specifications were proposed for Java and C++ by smart,
experienced programmers. It looked great on paper, and in the simple
examples in the proposals. The unfit nature of it only emerged years
later. Concepts and traits appear to me to suffer from the same fault.

They are not the same thing. Not even close.

Jul 24 2015
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/24/15 2:56 AM, Walter Bright wrote:
On 7/23/2015 10:49 PM, Tobias Müller wrote:
Walter Bright <newshound2 digitalmars.com> wrote:
I know a lot of the programming community is sold on exclusive
constraints (C++ concepts, Rust traits) rather than inclusive ones (D
constraints). What I don't see is a lot of experience actually using
them
long term. They may not turn out so well, like ES.

Haskell has type classes since ~1990.

Haskell is sometimes described as a bondage-and-discipline language.
Google it if you don't believe me :-) Such languages have their place
and adherents, but I don't think D is directed that way.

Exception Specifications were proposed for Java and C++ by smart,
experienced programmers. It looked great on paper, and in the simple
examples in the proposals. The unfit nature of it only emerged years
later. Concepts and traits appear to me to suffer from the same fault.

FWIW I think traits are better than concepts. -- Andrei

Jul 25 2015
On Saturday, 25 July 2015 at 12:53:37 UTC, Andrei Alexandrescu
wrote:
FWIW I think traits are better than concepts. -- Andrei

Can you explain this in more details ?

Jul 25 2015
=?UTF-8?Q?Tobias=20M=C3=BCller?= <troplin bluewin.ch> writes:
Walter Bright <newshound2 digitalmars.com> wrote:
On 7/23/2015 10:49 PM, Tobias Müller wrote:
Walter Bright <newshound2 digitalmars.com> wrote:
I know a lot of the programming community is sold on exclusive
constraints (C++ concepts, Rust traits) rather than inclusive ones (D
constraints). What I don't see is a lot of experience actually using them
long term. They may not turn out so well, like ES.

Haskell has type classes since ~1990.

Haskell is sometimes described as a bondage-and-discipline language.
Google it if you don't believe me :-) Such languages have their place and
adherents, but I don't think D is directed that way.

I just wanted to point out that there *is* long time experience. What

Tobi

Jul 25 2015
Timon Gehr <timon.gehr gmx.ch> writes:
On 07/24/2015 04:02 AM, Walter Bright wrote:
On 7/23/2015 6:05 PM, H. S. Teoh via Digitalmars-d wrote:
It doesn't solve *all* the problems, but it does solve a
significant subset of them.

The worst case of not having this feature is a compile time error. Not a
runtime error, undetectable error, or silent corruption. A compile time
error.

You got this wrong. In D, compile-time errors possibly influence runtime
semantics.

Jul 24 2015
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/23/15 9:05 PM, H. S. Teoh via Digitalmars-d wrote:
Yes, but by then it's the user that is faced with an inscrutable
template error. If I'm a library author, I'd like to be able to find all
these bugs*before*  shipping my code to the customers.

Then you need to understand that concepts are not helping you with that.
-- Andrei

Jul 25 2015
"Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Friday, 24 July 2015 at 01:09:19 UTC, H. S. Teoh wrote:
I have trouble thinking of a template function that's actually
*correct* when its sig constraints doesn't specify what
operations are valid on the incoming type. Can you give an
example?

If such code is wrong, I'd say the language *should* reject it.

I see two issues here, both of which relate to maintenance. The
first one is that if the language were actually able to check
that you missed a requirement in your template constraint (like
you're suggesting) and then give you an error, that makes it way
easier to break valid code. Take code like this, for example

auto foo(T)(T t)
if(cond1!T && cond2!T)
{
...
auto b = bar(t);
...
}

auto bar(T)(T t)
if(cond2!T)
{
...
}

foo calls bar, and it does have all of bar's constraints in its
own constraints so that you don't end up with a compilation error
when you pass foo something that doesn't work with bar. Now,
imagine if bar gets updated, and now its

auto bar(T)(T t)
if(cond2!T && cond3!T)
{
...
}

but foo's constraint isn't updated (e.g. because foo is in a
different library or program that depends no bar, so the person
who updates bar isn't necessarily the same person who maintains
foo). If the compiler then caught the fact that foo didn't check
all of bar's constraints and gave an error, that would alert
anyone using foo that foo needed to be updated, but it would also
mean that foo would no longer compile, when it's quite possible
that the argument passed to foo does indeed pass bar's template
constraint and will work just fine with foo. So, working code no
longer compiles when there's no technical reason why it couldn't
continue to work. Presumably, once the maintainer of foo finds
fixed, but it still means that every time that the template
constraint for bar is adjusted at all, every template that uses
it risks breaking if the compiler insists that those templates
check all of bar's constraints.

So, yes. it does help ensure that users of foo don't end up with
error messages inside of foo thanks to foo's template constraint
not listing everything that it actually requires, but it also
breaks a lot of code when template constraints change when the
code itself will often work just fine as-is (particularly since
the change to bar that required a change to its template
constraint would usually be a change to its implementation and
not what it did, since if you changed what it did, everyone that
used it would be broken anyway). Code that's actually broken by
the change to bar will fail bar's new template constraint even if
the compiler doesn't complain about foo (or any other function)
not having updated its constraint, and it'll still get caught.
The error might not be as nice, since it'll often be in someone
else's templated code, but it'll still be an error, and it'll
still tell you what's failing. So, with the current state of
affairs, only code that's actually broken by a change to bar's
template constraint would be broken and not everyone, whereas
what you're suggesting would break all code that used bar that
didn't happen to also check the same thing that bar was now
checking for.

The second issue that I see with your suggestion is basically
what Walter is saying the problem is. Even if we assume that we
_do_ want to put all of the requirements for foo - direct or
indirect - in its template constraint, this causes a maintenance
problem. For instance, if foo were updated to call another
function

auto foo(T)(T t)
if(cond1!T && cond2!T && cond3!T && cond4!T)
{
...
auto b = bar(t);
...
auto c = baz(t);
...
}

auto bar(T)(T t)
if(cond2!T && cond3!T)
{
...
}

auto baz(T)(T t)
if(cond1!T && cond4!T)
{
...
}

you now have to update foo. Okay. That's not a huge deal, but now
you have two functions that you're using within foo whose
template constraints need to be duplicated in foo's template
constraint. And ever function that _they_ call ends up affecting
_their_ template constraints and then foo in turn.

auto foo(T)(T t)
if(cond1!T && cond2!T && cond3!T && cond4!T && cond5!T &&
cond6!T && cond7!T)
{
...
auto b = bar(t);
...
auto c = baz(t);
...
}

auto bar(T)(T t)
if(cond2!T && cond3!T)
{
...
auto l = lark(t);
...
}

auto baz(T)(T t)
if(cond1!T && cond4!T)
{
...
auto s = stork(t);
...
}

auto lark(T)(T t)
if(cond5!T && cond6!T)
{
...
}

auto stork(T)(T)
if(cond2!T && cond3!T && cond7!T)
{
auto w = wolf(t);
}

auto wolf(T)(T)
if(cond7!T)
{
...
}

So, foo's template constraint potentially keeps getting nastier
and nastier thanks to indirect calls that it's making. Now, often
there's going to be a large overlap between these constraints
(e.g. because they're all range-based functions using
isInputRange, isForwardRange, hasLength, etc.), so maybe foo's
constraint doesn't get that nasty. But where you still have a
maintenance problem even if that's the case is if a function
that's being called indirectly adds something to its template
constraint, then everything up the chain has to add it if you
want to make sure that foo gets no compilation internally due to
it failing to pass a template constraint of something that it's
calling. So, if wolf ends up with a slightly more restrictive
constraint in the next release, then every templated function on
the planet which used it - directly or indirectly - would need to
be updated. And much of that code could be maintained by someone
other than the person who made the change to wolf, and much of it
could be code that they've don't even know exists. So, if we're
really trying to put everything that a function requires -
directly or indirectly - in its template constraint, we
potentially have a huge maintenance problem here once you start
having templated functions call other templated functions -
especially if any of these functions are part of a library that's
distributed to others. But even if it's just your own code base,
a slight adjustment to a template constraint could force you to
change a _lot_ of the other template constraints in your code.

So, while I definitely agree that it's nicer from the user's
standpoint when the template constraint checks everything that
the function requires - directly or indirectly - I think that we
have a major maintenance issue in the making here if that's what
we insist on. Putting all of the sub-constraints in the top-level
constraint - especially with multiple levels of templated
functions - simply doesn't scale well, even if it's desirable.
Maybe some kind of constraint inference would solve the problem.
I don't know. But I think that it is a problem, and it's one that
we haven't really recognized yet.

At this point, even if we're going to try and have top-level
template constraints explicitly contain all of the constraints of
the templates that they use - directly or indirectly - I think
that we really need to make sure that the error messages from
within templated code are as good as we can make them, because
there's no way that all template constraints are going to contain
all of their sub-constraints as code is changed over time, not
unless the constraints are fairly simple and looking for the same
stuff.

Fortunately, the error messages are a lot better than they used
to be, but if we can improve them sufficiently, then it becomes
less critical to make sure that all sub-constraints be in the
top-level constraint, and it makes it a lot more palatable when
sub-constraints are missed.

But as I said in the first part, I really don't think that
detecting missing constraints and giving errors is a good
solution. It'll just break more code that way. Rather, what we
need is to either find a way to infer the sub-constraints into
the top-level constraint and/or to provide really good error
messages when errors show up inside templated code, because a
constraint didn't check enough.

- Jonathan M Davis

Jul 25 2015
"Tofu Ninja" <emmons0 purdue.edu> writes:
On Sunday, 26 July 2015 at 00:31:48 UTC, Jonathan M Davis wrote:
I see two issues here, both of which relate to maintenance. The
first one is that if the language were actually able to check
that you missed a requirement in your template constraint (like
you're suggesting) and then give you an error, that makes it
way easier to break valid code. Take code like this, for example

auto foo(T)(T t)
if(cond1!T && cond2!T)
{
...
auto b = bar(t);
...
}

auto bar(T)(T t)
if(cond2!T)
{
...
}

foo calls bar, and it does have all of bar's constraints in its
own constraints so that you don't end up with a compilation
error when you pass foo something that doesn't work with bar.
Now, imagine if bar gets updated, and now its

auto bar(T)(T t)
if(cond2!T && cond3!T)
{
...
}

but foo's constraint isn't updated (e.g. because foo is in a
different library or program that depends no bar, so the person
who updates bar isn't necessarily the same person who maintains
foo). If the compiler then caught the fact that foo didn't
check all of bar's constraints and gave an error, that would
alert anyone using foo that foo needed to be updated, but it
would also mean that foo would no longer compile, when it's
quite possible that the argument passed to foo does indeed pass
bar's template constraint and will work just fine with foo. So,
working code no longer compiles when there's no technical
reason why it couldn't continue to work. Presumably, once the
the problem will be fixed, but it still means that every time
that the template constraint for bar is adjusted at all, every
template that uses it risks breaking if the compiler insists
that those templates check all of bar's constraints.

I think one of the key points, is that it would be opt-in.
Current constraints would continue to work as they do now. Only
templates that chose to use the new tighter constraints would
have this "problem". They would be separate from current
constraints (with a separate syntax). Code using current
constraints would not stop working if a template that used the
new constraints.

It makes sense when you consider how current constraints work
now, they basically say "If you can't do X then fail" but they
say nothing about if it can do extra things in addition to X.

So for example in the following code I will use the
specialization syntax to signify one of the NEW constraints and
the regular "if" syntax for the current constraints.

void foo(T)(T x) if(cond1!T) // OLD constraint
{
bar(x);
}

void bar(T : cond2)(T x) // NEW constraint
{
...
}

Would still work, and would only fail when foo gets instantiated
with a type that does not pass both cond1 and cond2. Which makes
perfect sense in the context of how the current constraints work.
They only test if you can do something, they don't care if you
can do extra things. There would be no need to update the
constraints on foo.

So, yes. it does help ensure that users of foo don't end up
with error messages inside of foo thanks to foo's template
constraint not listing everything that it actually requires,
but it also breaks a lot of code when template constraints
change when the code itself will often work just fine as-is
(particularly since the change to bar that required a change to
its template constraint would usually be a change to its
implementation and not what it did, since if you changed what
it did, everyone that used it would be broken anyway). Code
that's actually broken by the change to bar will fail bar's new
template constraint even if the compiler doesn't complain about
foo (or any other function) not having updated its constraint,
and it'll still get caught. The error might not be as nice,
since it'll often be in someone else's templated code, but
it'll still be an error, and it'll still tell you what's
failing. So, with the current state of affairs, only code
that's actually broken by a change to bar's template constraint
would be broken and not everyone, whereas what you're
suggesting would break all code that used bar that didn't
happen to also check the same thing that bar was now checking
for.

With what I said about opt-in I think every thing above is null.

The key advantage to the new constraints would be that it
constrains the type to only do what the constraints say. As
opposed to being able to do anything in addition to what the
constraints say.

The second issue that I see with your suggestion is basically
what Walter is saying the problem is. Even if we assume that we
_do_ want to put all of the requirements for foo - direct or
indirect - in its template constraint, this causes a
maintenance problem. For instance, if foo were updated to call
another function

auto foo(T)(T t)
if(cond1!T && cond2!T && cond3!T && cond4!T)
{
...
auto b = bar(t);
...
auto c = baz(t);
...
}

auto bar(T)(T t)
if(cond2!T && cond3!T)
{
...
}

auto baz(T)(T t)
if(cond1!T && cond4!T)
{
...
}

you now have to update foo. Okay. That's not a huge deal, but
now you have two functions that you're using within foo whose
template constraints need to be duplicated in foo's template
constraint. And ever function that _they_ call ends up
affecting _their_ template constraints and then foo in turn.

auto foo(T)(T t)
if(cond1!T && cond2!T && cond3!T && cond4!T && cond5!T &&
cond6!T && cond7!T)
{
...
auto b = bar(t);
...
auto c = baz(t);
...
}

auto bar(T)(T t)
if(cond2!T && cond3!T)
{
...
auto l = lark(t);
...
}

auto baz(T)(T t)
if(cond1!T && cond4!T)
{
...
auto s = stork(t);
...
}

auto lark(T)(T t)
if(cond5!T && cond6!T)
{
...
}

auto stork(T)(T)
if(cond2!T && cond3!T && cond7!T)
{
auto w = wolf(t);
}

auto wolf(T)(T)
if(cond7!T)
{
...
}

So, foo's template constraint potentially keeps getting nastier
and nastier thanks to indirect calls that it's making. Now,
often there's going to be a large overlap between these
constraints (e.g. because they're all range-based functions
using isInputRange, isForwardRange, hasLength, etc.), so maybe
foo's constraint doesn't get that nasty. But where you still
have a maintenance problem even if that's the case is if a
function that's being called indirectly adds something to its
template constraint, then everything up the chain has to add it
if you want to make sure that foo gets no compilation
internally due to it failing to pass a template constraint of
something that it's calling. So, if wolf ends up with a
slightly more restrictive constraint in the next release, then
every templated function on the planet which used it - directly
or indirectly - would need to be updated. And much of that code
could be maintained by someone other than the person who made
the change to wolf, and much of it could be code that they've
don't even know exists. So, if we're really trying to put
everything that a function requires - directly or indirectly -
in its template constraint, we potentially have a huge
maintenance problem here once you start having templated
functions call other templated functions - especially if any of
these functions are part of a library that's distributed to
others. But even if it's just your own code base, a slight
adjustment to a template constraint could force you to change a
_lot_ of the other template constraints in your code.

Again, with it being opt-in, this problem is not as bad as what
you say. Only templates that choose to use the new constraints
would need to do the sort of maintenance that you say.

Also this problem is the same as normal type systems experience!
If some low level function needs something new out of type T then
the same problem will arise. Its not a problem there so it should
not be a problem here.

So, while I definitely agree that it's nicer from the user's
standpoint when the template constraint checks everything that
the function requires - directly or indirectly - I think that
we have a major maintenance issue in the making here if that's
what we insist on. Putting all of the sub-constraints in the
top-level constraint - especially with multiple levels of
templated functions - simply doesn't scale well, even if it's
desirable. Maybe some kind of constraint inference would solve
the problem. I don't know. But I think that it is a problem,
and it's one that we haven't really recognized yet.

Again opt-in.

At this point, even if we're going to try and have top-level
template constraints explicitly contain all of the constraints
of the templates that they use - directly or indirectly - I
think that we really need to make sure that the error messages
from within templated code are as good as we can make them,
because there's no way that all template constraints are going
to contain all of their sub-constraints as code is changed over
time, not unless the constraints are fairly simple and looking
for the same stuff.

Fortunately, the error messages are a lot better than they used
to be, but if we can improve them sufficiently, then it becomes
less critical to make sure that all sub-constraints be in the
top-level constraint, and it makes it a lot more palatable when
sub-constraints are missed.

Better errors are of course better.

But as I said in the first part, I really don't think that
detecting missing constraints and giving errors is a good
solution. It'll just break more code that way. Rather, what we
need is to either find a way to infer the sub-constraints into
the top-level constraint and/or to provide really good error
messages when errors show up inside templated code, because a
constraint didn't check enough.

- Jonathan M Davis

Key point is opt-in.

Jul 25 2015
"Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Sunday, 26 July 2015 at 01:56:29 UTC, Tofu Ninja wrote:
Key point is opt-in.

Opt-in doesn't really fix the problem. It just allows you to
choose whether you're going to break more code by requiring that
all template constraints be updated because a function being
called inside somewhere had its constraint updated. So, you're
choosing whether to opt-in to that problem or not, but if you
opt-in, you're still screwed by it. That's not going to change
just because it's optional.

And my point about template constraint condition proliferation
holds even with the current implementation. Anyone choosing to
try and put all of the sub-constraints in the top-level
constraint has a maintenance problem. Sure, you can choose not to
do that and let the user see errors from within the template when
they use a type that fails the template constraint of a function
being called and thus avoid the constraint proliferation (which
then causes its own problems due to how that's more annoying to
deal with when you run into it), but the problem is still there.
If you opt-in to putting everything in the top-level template
constraints, you will have a maintenance issue.

The fact that you can choose what you do or don't put in your
template constraints (or  that you could choose whether to use
the new paradigm/feature that you're proposing) doesn't fix the
problem that going that route causes maintenance issues. That
fundamental problem still remains. All it means is that you can
choose whether you want to cause yourself problems by going that
route, not that they're necessarily a good idea.

- Jonathan M Davis

Jul 25 2015
"jmh530" <john.michael.hall gmail.com> writes:
On Sunday, 26 July 2015 at 00:31:48 UTC, Jonathan M Davis wrote:
On Friday, 24 July 2015 at 01:09:19 UTC, H. S. Teoh wrote:

The second issue that I see with your suggestion is basically
what Walter is saying the problem is. Even if we assume that we
_do_ want to put all of the requirements for foo - direct or
indirect - in its template constraint, this causes a
maintenance problem. For instance, if foo were updated to call
another function

I think from your post I finally understand what Walter was
getting at.

Not sure if this simplifies things, but what if instead you do
something like

void foo(T)(T t)
if (__traits(compiles, bar(t) && __traits(compiles, baz(t)))
{
...
auto b = bar(t);
...
auto c = baz(t);
...
}

This only really works in the case where it's obvious that you
are calling some bar(t). It might not work more generally...

some more condn!T constraints. If instead you just have more
__traits(compiles, x) for whatever templates they are calling,
then checking that bar compiles necessarily also tests whether
those other functions can compile as well. In this way, you're
testing all the constraints at lower levels. So I guess you would
still be checking that each constraint works, but at the highest
level you only have to specify that the templates you are calling
compile.

In my opinion this is superior (for this case) because if you
change bar and baz then you don't have to make changes to foo.

Am I wrong? Is this just another way of doing the same thing?

Jul 25 2015
"Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Sunday, 26 July 2015 at 02:15:20 UTC, jmh530 wrote:
On Sunday, 26 July 2015 at 00:31:48 UTC, Jonathan M Davis wrote:
On Friday, 24 July 2015 at 01:09:19 UTC, H. S. Teoh wrote:

The second issue that I see with your suggestion is basically
what Walter is saying the problem is. Even if we assume that
we _do_ want to put all of the requirements for foo - direct
or indirect - in its template constraint, this causes a
maintenance problem. For instance, if foo were updated to call
another function

I think from your post I finally understand what Walter was
getting at.

Not sure if this simplifies things, but what if instead you do
something like

void foo(T)(T t)
if (__traits(compiles, bar(t) && __traits(compiles, baz(t)))
{
...
auto b = bar(t);
...
auto c = baz(t);
...
}

This only really works in the case where it's obvious that you
are calling some bar(t). It might not work more generally...

some more condn!T constraints. If instead you just have more
__traits(compiles, x) for whatever templates they are calling,
then checking that bar compiles necessarily also tests whether
those other functions can compile as well. In this way, you're
testing all the constraints at lower levels. So I guess you
would still be checking that each constraint works, but at the
highest level you only have to specify that the templates you
are calling compile.

In my opinion this is superior (for this case) because if you
change bar and baz then you don't have to make changes to foo.

Am I wrong? Is this just another way of doing the same thing?

I suggested the same in response to Walter earlier. It is one way
to combat the problem. However, it's really only going to work in
basic cases (at least, without getting ugly). What if what you
passed to bar wasn't t but was a result from calling a function
on t? Or maybe it was a result of calling a function on the
return value of a function that was called on t? Or perhaps you
passed t through a chain of free functions and ended up with some
other type from that, and it doesn't pass bar's template
constraint? In order to deal with that sort of thing, pretty
soon, you have to put most of the function inside its own
constraint. It's _far_ cleaner in general to just be putting the
sub-constraints in the top-level constraint - e.g. maybe all that
it means is using isForwardRange and hasLength instead of just
isInputRange rather than putting a whole chain of function calls
inside of __traits(compiles, ...) test in the template
constraint. It just gets ugly quickly to try and get it to work
for you automatically by putting the calls you're making in the
constraint so that the actual constraint conditions are inferred.

The other problem is that if you're putting all of those
__traits(compiles, ...) tests in template constraints rather than
putting the sub-constraints in there, it makes it a lot more of a
pain for the user to figure out why they're failing the
constraint. The constraint for what's failing in
__traits(compiles, ...) isn't shown, whereas it would be if you
just let it get past the template constraint and fail the
sub-constraint at the point where that function is being called,
you'd see the actual condition that's failing. So, as annoying as
it would be, it would actually be easier to figure out what you
were doing wrong. Also, if you really didn't put the
sub-constraint in the top-level constraint at all, then the
constraint is split out so that when you get a failure at the
top-level, you see only the stuff that the function requires
directly, and when you get a failure internally, you see it the
condition that that function requires and can see that
separately. So, instead of having to figure out which part of
condition1 && condition2 is failing, you know which it is,
because the conditions are tested in separate places.

I suspect that the best way to go with this is that a template
constraint only require the stuff that a function uses directly
and let the constraints on any functions being called internally
report their own errors and then have the compiler provide really
good error messages to make that sane. Then it can be a lot
clearer what condition you're failing when you call the function
with a bad argument. But we need to improve the error messages
further if we want to go that way.

The other alternative would be to just make a best faith effort
to put all of the sub-constraints in the top-level constraint
initially and then have better error messages for when the
constraint is incomplete due to a change to a function being
called. But that would still require better error messages (which
is the main problem with the other suggestion), and it actually
has the problem that if a function being called has its
outer function could then accept more types of arguments, if you
put all of the sub-constraints at the top-level, then it won't
accept anything more until you realize that the sub-constraints
have changed and update the top-level constraint.

Right now, we're more or less living with the second option, but
if we can get the error messages to be good enough, I think that
the first option is actually better. But either way, we need to
find ways to improve the error messages inside of templates to
reduce the need to look at their source code when a template
constraint doesn't prevent an argument being used with it that
doesn't compile with it (particularly in the cases where it's due
to a function being called rather than that function itself
having a bug).

- Jonathan M Davis

Jul 25 2015
"jmh530" <john.michael.hall gmail.com> writes:
On Sunday, 26 July 2015 at 04:34:55 UTC, Jonathan M Davis wrote:
I suggested the same in response to Walter earlier. It is one
way to combat the problem. However, it's really only going to
work in basic cases (at least, without getting ugly). What if
what you passed to bar wasn't t but was a result from calling a
function on t? Or maybe it was a result of calling a function
on the return value of a function that was called on t? Or
perhaps you passed t through a chain of free functions and
ended up with some other type from that, and it doesn't pass
bar's template constraint? In order to deal with that sort of
thing, pretty soon, you have to put most of the function inside
its own constraint. It's _far_ cleaner in general to just be
putting the sub-constraints in the top-level constraint - e.g.
maybe all that it means is using isForwardRange and hasLength
instead of just isInputRange rather than putting a whole chain
of function calls inside of __traits(compiles, ...) test in the
template constraint. It just gets ugly quickly to try and get
it to work for you automatically by putting the calls you're
making in the constraint so that the actual constraint
conditions are inferred.

{snip}

I appreciate the thorough response. I think I agree with your
point about a best effort to putting constraints at the
top-level, there might be scope for making this easier for
people. For instance, if there were a way to include the
constraints from one template in another template. From your
example, maybe something like

auto foo(T)(T t)
if(template_constraints!bar && template_constraints!baz)
{
...
auto b = bar(t);
...
auto c = baz(t);
...
}

Ideally the template_constraints!bar would expand so that in an
error message the user sees what the actual constraints are
instead of the more nebulous template_constraints!bar. At least
something like this would avoid your point with respect to
__traits(compiles, x).

Jul 25 2015
"Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Sunday, 26 July 2015 at 06:12:55 UTC, jmh530 wrote:
I appreciate the thorough response. I think I agree with your
point about a best effort to putting constraints at the
top-level, there might be scope for making this easier for
people. For instance, if there were a way to include the
constraints from one template in another template. From your
example, maybe something like

auto foo(T)(T t)
if(template_constraints!bar && template_constraints!baz)
{
...
auto b = bar(t);
...
auto c = baz(t);
...
}

Ideally the template_constraints!bar would expand so that in an
error message the user sees what the actual constraints are
instead of the more nebulous template_constraints!bar. At least
something like this would avoid your point with respect to
__traits(compiles, x).

That's certainly an interesting idea, though if we're going that
route, it might be better to simply have the compiler do that
automatically for you, since it can see what functions are being
called and what they're constraints are. Still, part of the
problem is that the constraints for bar or baz may not be
directly related to the argument to foo but rather to a result of
operating on it. So, bar and baz's constraints can't necessarily
be moved up into foo's constraint like that in a meaningful way.
It would require the code that generates the arguments to bar and
baz as well in order to make that clear. For instance, it might
be that T needs to be an input range for the code that's directly
in foo. However, if you do something like

auto u = t.f1().f2().f3();
auto b = bar(u);

u then needs to be a forward range to work with bar. In order for
that to happen, t likely needs to have been a forward range, but
if you tried to move bar's template constraint into foo's
template constraint, it would be trying to require that u was a
forward range - because that's what bar needs - whereas that's r
really not what needs to be in foo's template constraint. What it
needs is to require that t be a forward range instead of just an
input range. The connection between the arguments the user is
passing to the function that they're calling and the arguments to
templated functions that are called within that function aren't
necessarily straightforward. So, moving those sub-constraints up
into the top-level constraint in a way that's clear to the caller
either requires that the writer of that function do it (because
they are able to understand how the function's argument relates
to the requirements of the functions being called within that
function and thus come up with the full requirements of for the
function's argument), or it requires that enough context be given
to the caller for them to be able to understand how what they're
passing in is related to the call inside the template that's
failing to compile because that function's argument doesn't pass
its constraint.

Without having the program who's writing this function
translating the sub-constraints into what the requirements then
are on the function's argument and need to go in the top-level
constraint, I don't see how we can avoid providing at least
_some_ of the source in error messages in order to make the
context of the failure clear. Simply shoving all of that into the
template constraint - even automatically - is just going to get
ugly outside of basic cases. And really, even then, what you're
trying to do is to take the context of the failure and put it in
the template constraint rather than just show that context along
with the failure. The more I think about it, the harder it seems
to be able to provide enough information to the caller without
pretty much just showing them the source code. The compiler
should be able to reduce how much of the source code would have
to be shown and thus avoid forcing the user to go look at the
templates full source, but in anything but the most basic cases,
that source code quickly becomes required to understand what's
going on.

If we're truly dealing with cases where the function's argument
is simply passed on to another function call, then the sort of
thing that you're suggesting is pretty straightforward and would
likely work well. But there are going to be a lot of cases where
the constraint failure isn't on the original function argument
but on the result of passing it through other functions or on
something that was obtained by calling one of that argument's
member functions. And as soon as that's what's going on,
attempting to push the sub-constraints into the top-level
constraint either by automatically inferring them or by having
something like template_constraints!baz is not going to work
well, if at all.

I don't know. I think that we can come up with solutions that fix
many of the simple cases, but I also think that a lot of the
simple cases are where it's easiest to maintain the top-level
template constraints with all of the sub-constraints translated
into top-level constraints. It's the complicated cases (which are
going to be common) where things get ugly. And I really don't see
a good solution.

Improved error messages obviously will help, but if the code is
complicated enough, it eventually reaches the point that the
caller is going to need to look at the full source code to see
what's going on and figure out what they're screwing up, so I
don't know how far we can go with the error messages. And it's
also those cases where it's probably going to be hardest to
maintain the constraints. It's a tough problem.

- Jonathan M Davis

Jul 25 2015
"jmh530" <john.michael.hall gmail.com> writes:
On Sunday, 26 July 2015 at 06:54:17 UTC, Jonathan M Davis wrote:
That's certainly an interesting idea, though if we're going
that route, it might be better to simply have the compiler do
that automatically for you, since it can see what functions are
being called and what they're constraints are. Still, part of
the problem is that the constraints for bar or baz may not be
directly related to the argument to foo but rather to a result
of operating on it. So, bar and baz's constraints can't
necessarily be moved up into foo's constraint like that in a
meaningful way. It would require the code that generates the
arguments to bar and baz as well in order to make that clear.
For instance, it might be that T needs to be an input range for
the code that's directly in foo. However, if you do something
like

auto u = t.f1().f2().f3();
auto b = bar(u);

Yeah, I can see how something like that's going to get
complicated. My best guess would be something like
auto foo(T)(T t)
{
static if ( template_constraints!f1(t) &&
template_constraints!f2(f1(t)) &&
template_constraints!f3(f2(f1(t))) )
auto u = t.f1().f2().f3();
static if (template_constraints!bar(u))
auto b = bar(u);
}

I guess the key would be that the template_constraints should be
able to take whatever inputs the function can take so that you
can use it with static if at any point in the function body if
needed. It may not work for everything, but I imagine it would
probably cover most cases, albeit awkwardly for the chained range
operations. Ideally, there could be a way so that only the last
condition in that first static if is required, but I'm not sure
how easy something like that would be.

Nevertheless, I honestly don't know how big of an issue something
like this is. I'm sort of talking out of ignorance at this point.
I haven't had the need to program anything like this.

the top-level is that if you make a change to the constraints in
the bottom level then you have to remember to make the changes
everywhere else. If you only allow one template constraint for
each function, then it might be easier to remember (b/c multiple
conditions would need to be defined in auxiliary functions), but
it would also be much less flexible.

Jul 26 2015
"Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Sunday, 26 July 2015 at 16:39:23 UTC, jmh530 wrote:
in the top-level is that if you make a change to the
constraints in the bottom level then you have to remember to
make the changes everywhere else.

Well, that's pretty much exactly why Walter is trying to say that
it's the same problem as checked exceptions. Changing something
lower in the chain forces you to change everything higher in the
chain - and often, stuff higher in the chain isn't written by the
same person or organization as the stuff lower in the chain. If
we did what some folks are suggesting and make it so that the
compiler gave an error if a template constraint failed to cover
all of its sub-constraints (which would probably require using
concepts), then changing the function lower in the chain would
outright break all of the code higher in the chain, making it
even more like checked exceptions. But even without that
enforcement, we have a maintenance problem if the lower level
constraints need to be propagated. If the constraints almost
never change, then it won't necessarily be a big deal, but as
code is updated and improved, then it could be a much bigger one.

For a publicly available library like Phobos, the solution may
simply be that the template constraints of functions in the
public API can only ever be made less strict rather than more
strict so that they don't stop working with any existing code or
cause other functions up the chain to have to tighten their
template constraints as well. But with all of the inference and
conditional compilation that you get in D code, maybe even
reducing the restrictions would cause problems in some cases -
especially if something like __traits(compiles, ...) is used in
template constraints, because that change could potentially make
it so that overloads start conflicting (or just change) in higher
level functions and thus break code. Though given how much you
can do with metaprogramming in D, pretty soon changing _anything_
risks breaking code, so I don't know how much we should really
worry about that. If you're doing stuff that involves that much
type introspection, the odds of your code breaking with changes
to the libraries you're using are high enough that it's probably
not reasonable to expect that it won't break anyway.

In any case, I suppose that we'll just have to wait and see how
much a problem this will really become, but I don't think
attempting to keep the higher level constraints in line with the
lower level ones is as bad as doing something like concepts where
it's forced. At least with what we have, the worst you normally
get is an error inside of a template instead of at the outer
template constraint. And if someone doesn't want to try and
propagate the template constraints, they don't have to - they
just then have to deal with error messages being inside of the
templated function (or inside of templated functions that gets
called by that function - either directly or indirectly) - and if
we improve the error messages enough, then that won't be so bad.
So, we have a potential maintenance problem here, but it's not
one that's generally going to be from broken code so much as
reporting the error at a point other than the one where folks
want to see it.

- Jonathan M Davis

Jul 26 2015
"Sebastiaan Koppe" <mail skoppe.eu> writes:
On Sunday, 26 July 2015 at 00:31:48 UTC, Jonathan M Davis wrote:
auto foo(T)(T t)
if(cond1!T && cond2!T && cond3!T && cond4!T && cond5!T &&
cond6!T && cond7!T)
{
...
auto b = bar(t);
...
auto c = baz(t);
...
}

auto bar(T)(T t)
if(cond2!T && cond3!T)
{
...
auto l = lark(t);
...
}

auto baz(T)(T t)
if(cond1!T && cond4!T)
{
...
auto s = stork(t);
...
}

auto lark(T)(T t)
if(cond5!T && cond6!T)
{
...
}

auto stork(T)(T)
if(cond2!T && cond3!T && cond7!T)
{
auto w = wolf(t);
}

auto wolf(T)(T)
if(cond7!T)
{
...
}

Regardless of this debate, it would be great if template
constraints could be inferred. It seems rather trivial.

Although I understand that a lot of times the compiler doesn't
have the function's body at hand.

Jul 25 2015
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/23/15 6:06 PM, H. S. Teoh via Digitalmars-d wrote:
An uninstantiated template path is worse than a branch that's never
you ship it to the customer.

-cov does help, although indeed in a suboptimal way because you need to
manually parse the listing. It would be nice if it marked lines that are
supposed to be code yet are not instantiated in a specific way.
(Currently it only marks lines that are compiled but not run.)

A lot of Phobos bugs lurk in rarely-used template branches that are not
covered by the unittests.

Hopefully not many are left. I consider that a historical problem caused
by lack of good testing discipline. My perception is we got a lot better
at that.

It's simple survival to not ship untested code, even if it does compile.
In any language. We should never do it.

If we had a Concepts-like construct in D, where template code is
statically constrained to only use, e.g., range API when manipulating an
incoming type, a lot of these bugs would've been caught.

We'd get to ship more untested code? No thanks. We need a different
angle on this. Concepts support a scenario that fails basic software
engineering quality assurance criteria.

In fact, I'd argue that this should be done for *all* templates -- for
example, a function like this ought to be statically rejected:

auto myFunc(T)(T t) { return t + 1; }

because it assumes the validity of the + operation on T, but T is not
constrained in any way, so it can be *any* type, most of which,
arguably, do not support the + operation.

That would be a bit much. myFunc is correct under static and dynamic
assumptions about T. Dynamic assumptions cannot be checked save for
documentation and unittesting. If the static assumptions fail, then well
we have a less-than-nice compile-time error message, but still a
compile-time error message. No disaster.

Someone could easily introduce a bug:

auto myFunc(T)(T t)
if (is(typeof(T.init + 1)))
{
/* Oops, we checked that +1 is a valid operation on T,
* but here we're doing -1 instead, which may or may not
* be valid: */
return t - 1;

Is that a bug or a suboptimal error message?

The compiler still accepts this code as long as the unittests use types
that support both + and -. So this dependency on the incidental
characteristics of T remains as a latent bug.

If the compiler outright rejected any operation on T that hasn't been
explicitly tested for, *then* we will have eliminated a whole class of
template bugs. Wrong code like the last example above would be caught as
soon as the compiler compiles the body of myFunc.

Yah, I think this is off.

Andrei

Jul 25 2015
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/23/15 3:50 PM, H. S. Teoh via Digitalmars-d wrote:
On Thu, Jul 23, 2015 at 12:49:29PM -0700, Walter Bright via Digitalmars-d
wrote:
On 7/23/2015 7:15 AM, Andrei Alexandrescu wrote:
I am a bit puzzled by the notion of shipping template code that has
never been instantiated as being a positive thing. This has also
turned up in the C++ static_if discussions.

This is easy to understand. Weeding out uncovered code during
compilation is a central feature of C++ concepts. Admitting you
actually never want to do that would be a major blow.

But if a unit test fails at instantiating it, it fails at compile
time.

That assumes the template author is diligent (foolhardy?) enough to
write unittests that cover all possible instantiations...

Well at least all paths must be compiled. You wouldn't ship templates
that were never instantiated just as much as you wouldn't ship any code
without compiling it. We've had a few cases in Phobos a while ago of
templates that were never instantiated, with simple compilation errors
when people tried to use them. -- Andrei

Jul 25 2015
On Saturday, 25 July 2015 at 12:05:12 UTC, Andrei Alexandrescu
wrote:
Well at least all paths must be compiled. You wouldn't ship
templates that were never instantiated just as much as you
wouldn't ship any code without compiling it. We've had a few
cases in Phobos a while ago of templates that were never
instantiated, with simple compilation errors when people tried
to use them. -- Andrei

That is an instance of happy case testing. You test that what you
expect to work work. You can't test that everything that is not
supposed to work do not, or that you don't rely on a specific
behavior of the thing you are testing.

Jul 25 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 7/25/2015 3:59 PM, deadalnix wrote:
On Saturday, 25 July 2015 at 12:05:12 UTC, Andrei Alexandrescu wrote:
Well at least all paths must be compiled. You wouldn't ship templates that
were never instantiated just as much as you wouldn't ship any code without
compiling it. We've had a few cases in Phobos a while ago of templates that
were never instantiated, with simple compilation errors when people tried to
use them. -- Andrei

That is an instance of happy case testing. You test that what you expect to
work
work. You can't test that everything that is not supposed to work do not, or
that you don't rely on a specific behavior of the thing you are testing.

Um, testing all paths is not happy case testing.

Jul 25 2015
On Sunday, 26 July 2015 at 00:18:14 UTC, Walter Bright wrote:
On 7/25/2015 3:59 PM, deadalnix wrote:
On Saturday, 25 July 2015 at 12:05:12 UTC, Andrei Alexandrescu
wrote:
Well at least all paths must be compiled. You wouldn't ship
templates that
were never instantiated just as much as you wouldn't ship any
code without
compiling it. We've had a few cases in Phobos a while ago of
templates that
were never instantiated, with simple compilation errors when
people tried to
use them. -- Andrei

That is an instance of happy case testing. You test that what
you expect to work
work. You can't test that everything that is not supposed to
work do not, or
that you don't rely on a specific behavior of the thing you
are testing.

Um, testing all paths is not happy case testing.

You test all execution path, not all "instantiation path".
Consider this, in a dynamically typed language, you can have a
function that accept a string and do something with it. You can
write unit tests to check it does the right thing with various
strings and make sure it execute all path.

Yet, what happen when it is passed an int ? a float ? an array ?
an object ? Probably random shit.

Same here, but at instantiation time.

Jul 26 2015
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/26/15 5:04 AM, deadalnix wrote:
On Sunday, 26 July 2015 at 00:18:14 UTC, Walter Bright wrote:
On 7/25/2015 3:59 PM, deadalnix wrote:
On Saturday, 25 July 2015 at 12:05:12 UTC, Andrei Alexandrescu wrote:
Well at least all paths must be compiled. You wouldn't ship
templates that
were never instantiated just as much as you wouldn't ship any code
without
compiling it. We've had a few cases in Phobos a while ago of
templates that
were never instantiated, with simple compilation errors when people
tried to
use them. -- Andrei

That is an instance of happy case testing. You test that what you
expect to work
work. You can't test that everything that is not supposed to work do
not, or
that you don't rely on a specific behavior of the thing you are testing.

Um, testing all paths is not happy case testing.

You test all execution path, not all "instantiation path". Consider
this, in a dynamically typed language, you can have a function that
accept a string and do something with it. You can write unit tests to
check it does the right thing with various strings and make sure it
execute all path.

Yet, what happen when it is passed an int ? a float ? an array ? an
object ? Probably random shit.

Same here, but at instantiation time.

No, you are very wrong here. I am sorry! Instantiation testing is making
sure that syntactic conformance is there. Semantic conformance cannot be
tested during compilation (big difference) and can be partially verified
dynamically.

This whole conflation with dynamic typing/unittesting is inappropriate
and smacks of https://en.wikipedia.org/wiki/Argument_from_analogy. If
you have a point, make it stand on its own.

Andrei

Jul 26 2015
On Sunday, 26 July 2015 at 15:33:59 UTC, Andrei Alexandrescu
wrote:
On 7/26/15 5:04 AM, deadalnix wrote:
On Sunday, 26 July 2015 at 00:18:14 UTC, Walter Bright wrote:
On 7/25/2015 3:59 PM, deadalnix wrote:
On Saturday, 25 July 2015 at 12:05:12 UTC, Andrei
Alexandrescu wrote:
Well at least all paths must be compiled. You wouldn't ship
templates that
were never instantiated just as much as you wouldn't ship
any code
without
compiling it. We've had a few cases in Phobos a while ago of
templates that
were never instantiated, with simple compilation errors
when people
tried to
use them. -- Andrei

That is an instance of happy case testing. You test that
what you
expect to work
work. You can't test that everything that is not supposed to
work do
not, or
that you don't rely on a specific behavior of the thing you
are testing.

Um, testing all paths is not happy case testing.

You test all execution path, not all "instantiation path".
Consider
this, in a dynamically typed language, you can have a function
that
accept a string and do something with it. You can write unit
tests to
check it does the right thing with various strings and make
sure it
execute all path.

Yet, what happen when it is passed an int ? a float ? an array
? an
object ? Probably random shit.

Same here, but at instantiation time.

No, you are very wrong here. I am sorry! Instantiation testing
is making sure that syntactic conformance is there. Semantic
conformance cannot be tested during compilation (big
difference) and can be partially verified dynamically.

This whole conflation with dynamic typing/unittesting is
inappropriate and smacks of
https://en.wikipedia.org/wiki/Argument_from_analogy. If you
have a point, make it stand on its own.

Andrei

It is not an analogy. The dynamic typing is not a problem that is
used as example or something. This is fundamentally the same
problem. I've made that point earlier, and I stand by it.

Claiming it is inappropriate do not make it so. Once again,
statements do not constitute good arguments. If you make a good
point that they differs in such a way that I missed then you
basically ends the argument.

Jul 26 2015
=?UTF-8?Q?Tobias=20M=C3=BCller?= <troplin bluewin.ch> writes:
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
On 7/23/15 5:07 AM, Walter Bright wrote:

Turns out many constraints in Phobos are of the form (A || B),
not just (A && B).

Agreed. And that's just scratching the surface.

Serious question: how do you express in Rust that a type implements one
trait or another, then figure out statically which?

You define a new trait and implement it differently for A and B.
That leads to a cleaner design IMO because you have to think about the
right abstraction for that trait.

TBH I'm very surprised about that argument, because boolean conditions with
version() were dimissed for exactly that reason.

Tobi

Jul 23 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 7/23/2015 12:50 PM, Tobias Müller wrote:
TBH I'm very surprised about that argument, because boolean conditions with
version() were dimissed for exactly that reason.

I knew someone would bring that up :-)

No, I do not believe it is the same thing. For one thing, you cannot test the
various versions on one system. On any one system, you have to take on faith
that you didn't break the version blocks on other systems.

This is quite unlike D's template constraints, where all the combinations can
be
tested reliably with a unittest{} block.

Jul 23 2015
Jacob Carlborg <doob me.com> writes:
On 2015-07-23 22:44, Walter Bright wrote:

I knew someone would bring that up :-)

No, I do not believe it is the same thing. For one thing, you cannot
test the various versions on one system. On any one system, you have to
take on faith that you didn't break the version blocks on other systems.

Perhaps it might be good idea to allow to set a predefined version
identifier, i.e. set "linux" on Windows just to see that it compiles.
Think of it like the "debug" statement can be used as an escape hatch
for pure functions.

--
/Jacob Carlborg

Jul 24 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 7/24/2015 11:39 AM, Jacob Carlborg wrote:
Perhaps it might be good idea to allow to set a predefined version identifier,
i.e. set "linux" on Windows just to see that it compiles. Think of it like the
"debug" statement can be used as an escape hatch for pure functions.

I don't want to encourage "if it compiles, ship it!" I've strongly disagreed
with the C++ concepts folks on that issue, and they've downvoted me to hell on
it, too :-)

I get the impression that I'm the only one who thinks exclusive traits is more
of a problem than a solution. It's deja vu all over again with Exception
Specifications. So, one of:

2. I fail to explain my argument properly (not the first time that's happened,
fer sure).
3. People strongly want to believe in traits.
4. Smart people say it tastes great and is less filling, so there's a bandwagon
effect.
5. The concepts/traits people have done a fantastic job convincing people that
the emperor is wearing the latest fashion :-)

It's also clear that traits work very well "in the small", i.e. in
specifications of the feature, presentation slide decks, tutorials, etc. Just
like Exception Specifications did. It's the complex hierarchies where it fell
apart.

Jul 24 2015
"jmh530" <john.michael.hall gmail.com> writes:
On Friday, 24 July 2015 at 19:26:33 UTC, Walter Bright wrote:
2. I fail to explain my argument properly (not the first time
that's happened, fer sure).

I wouldn't be surprised if you're right, contra one, and you've
explained it properly, contra two, but I don't understand anyway.
I don't have a problem deferring to people more knowledgeable
than I am.

Jul 24 2015
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/24/15 3:26 PM, Walter Bright wrote:
On 7/24/2015 11:39 AM, Jacob Carlborg wrote:
Perhaps it might be good idea to allow to set a predefined version
identifier,
i.e. set "linux" on Windows just to see that it compiles. Think of it
like the
"debug" statement can be used as an escape hatch for pure functions.

I don't want to encourage "if it compiles, ship it!" I've strongly
disagreed with the C++ concepts folks on that issue, and they've
downvoted me to hell on it, too :-)

I get the impression that I'm the only one who thinks exclusive traits
is more of a problem than a solution. It's deja vu all over again with
Exception Specifications. So, one of:

2. I fail to explain my argument properly (not the first time that's
happened, fer sure).
3. People strongly want to believe in traits.
4. Smart people say it tastes great and is less filling, so there's a
bandwagon effect.
5. The concepts/traits people have done a fantastic job convincing
people that the emperor is wearing the latest fashion :-)

It's also clear that traits work very well "in the small", i.e. in
specifications of the feature, presentation slide decks, tutorials, etc.
Just like Exception Specifications did. It's the complex hierarchies
where it fell apart.

It would be a mistake to put concepts and traits together. Traits have
been used at large scale in Scala to great results (my understanding is
they're similar to Rust's). Scala-style traits would marginally improve
D but we already have competing mechanisms in the form of template
constraints. I consider them more powerful; Odersky seems to think
they're about as powerful. -- Andrei

Jul 25 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 7/25/2015 6:20 AM, Andrei Alexandrescu wrote:
It would be a mistake to put concepts and traits together.

Then I'm misunderstanding one or the other.

Jul 25 2015
=?UTF-8?Q?Tobias=20M=C3=BCller?= <troplin bluewin.ch> writes:
Walter Bright <newshound2 digitalmars.com> wrote:
It's also clear that traits work very well "in the small", i.e. in
specifications of the feature, presentation slide decks, tutorials, etc.
Just like Exception Specifications did. It's the complex hierarchies where it
fell apart.

I'm not convinced at all that checked exceptions (as implemented in Java,
not C++) don't work.

My suspicion is that the usual Java code monkey is just too sloppy to care
and thus sees it more as a nuisance rather than the help that it is.

I think Rust attracts a different kind of programmer than Java.

Tobi

Jul 25 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 7/25/2015 11:40 AM, Tobias Müller wrote:
I'm not convinced at all that checked exceptions (as implemented in Java,
not C++) don't work.

My suspicion is that the usual Java code monkey is just too sloppy to care
and thus sees it more as a nuisance rather than the help that it is.

Unfortunately, Bruce Eckel's seminal article on it
http://www.mindview.net/Etc/Discussions/CheckedExceptions has disappeared.
Eckel
is not a Java code monkey, he wrote the book Thinking In Java
http://www.amazon.com/gp/product/0131002872/

Jul 25 2015
"Guillaume Chatelet" <chatelet.guillaume gmail.com> writes:
On Saturday, 25 July 2015 at 20:48:06 UTC, Walter Bright wrote:
On 7/25/2015 11:40 AM, Tobias Müller wrote:
I'm not convinced at all that checked exceptions (as
implemented in Java,
not C++) don't work.

My suspicion is that the usual Java code monkey is just too
sloppy to care
and thus sees it more as a nuisance rather than the help that
it is.

Unfortunately, Bruce Eckel's seminal article on it
http://www.mindview.net/Etc/Discussions/CheckedExceptions has
disappeared. Eckel is not a Java code monkey, he wrote the book
Thinking In Java
http://www.amazon.com/gp/product/0131002872/

This ?
http://www.artima.com/intv/handcuffs.html

Jul 25 2015
"Brandon Ragland" <brags callmemaybe.com> writes:
I'm not quite sure I understand why this thread is so hot...

Here's my case in point: Rust may as well be a system-language
however it appeals more to the high-level programmers of today.
Your folks from Java, C#, etc. are going to look at Rust with
awe. Technically speaking, Rust doesn't do anything that cannot
be done in D. The way you go about doing things may be different,
and frustrating at times, however that's the basis behind any
language.

D on the other hand is more of a systems language for C and C++
guys.

I'm personally of the opinion that D is wonderful, and it
my C++ days.

As for marketing, for novice and intermediate programmers, the
IDE is the language. D does not have very good IDE support. DDT
is by far the best, but is fairly easy to break, and still lacks
the feel of an IDE designed for D natively, as opposed to a Java
IDE with a plugin running in the background to manage DUB for us.

Jul 25 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 7/25/2015 3:19 PM, Guillaume Chatelet wrote:
On Saturday, 25 July 2015 at 20:48:06 UTC, Walter Bright wrote:
On 7/25/2015 11:40 AM, Tobias Müller wrote:
I'm not convinced at all that checked exceptions (as implemented in Java,
not C++) don't work.

My suspicion is that the usual Java code monkey is just too sloppy to care
and thus sees it more as a nuisance rather than the help that it is.

Unfortunately, Bruce Eckel's seminal article on it
http://www.mindview.net/Etc/Discussions/CheckedExceptions has disappeared.
Eckel is not a Java code monkey, he wrote the book Thinking In Java
http://www.amazon.com/gp/product/0131002872/

This ?
http://www.artima.com/intv/handcuffs.html

No, that's Anders.

Jul 25 2015
On Saturday, 25 July 2015 at 20:48:06 UTC, Walter Bright wrote:
On 7/25/2015 11:40 AM, Tobias Müller wrote:
I'm not convinced at all that checked exceptions (as
implemented in Java,
not C++) don't work.

My suspicion is that the usual Java code monkey is just too
sloppy to care
and thus sees it more as a nuisance rather than the help that
it is.

Unfortunately, Bruce Eckel's seminal article on it
http://www.mindview.net/Etc/Discussions/CheckedExceptions has
disappeared. Eckel is not a Java code monkey, he wrote the book
Thinking In Java
http://www.amazon.com/gp/product/0131002872/

Yes, checked exception is bankrupt at this point. It was not
clear at the time, now it is.

Jul 25 2015
Alix Pexton <alix.DOT.pexton gmail.DOT.com> writes:
On 25/07/2015 9:48 PM, Walter Bright wrote:

Unfortunately, Bruce Eckel's seminal article on it
http://www.mindview.net/Etc/Discussions/CheckedExceptions has
disappeared. Eckel is not a Java code monkey, he wrote the book Thinking
In Java
http://www.amazon.com/gp/product/0131002872/

https://web.archive.org/web/20150515072240/http://www.mindview.net/Etc/Discussions/CheckedExceptions

Jul 26 2015
=?UTF-8?Q?Tobias=20M=C3=BCller?= <troplin bluewin.ch> writes:
Alix Pexton <alix.DOT.pexton gmail.DOT.com> wrote:
On 25/07/2015 9:48 PM, Walter Bright wrote:

Unfortunately, Bruce Eckel's seminal article on it
http://www.mindview.net/Etc/Discussions/CheckedExceptions has
disappeared. Eckel is not a Java code monkey, he wrote the book Thinking
In Java
http://www.amazon.com/gp/product/0131002872/

https://web.archive.org/web/20150515072240/http://www.mindview.net/Etc/Discussions/CheckedExceptions

This is article not convincing at all. His argument is basically "Most
programmers are sloppy and tend to catch and ignore checked exceptions."

The same programmers that do this will just catch all RuntimeExceptions at
top level, write a log entry and proceed.
That's actually not much better and certainly not correct error handling.

Tobi

Jul 26 2015
On Sunday, 26 July 2015 at 18:13:30 UTC, Tobias Müller wrote:
Alix Pexton <alix.DOT.pexton gmail.DOT.com> wrote:
On 25/07/2015 9:48 PM, Walter Bright wrote:

Unfortunately, Bruce Eckel's seminal article on it
http://www.mindview.net/Etc/Discussions/CheckedExceptions has
disappeared. Eckel is not a Java code monkey, he wrote the
book Thinking
In Java
http://www.amazon.com/gp/product/0131002872/

https://web.archive.org/web/20150515072240/http://www.mindview.net/Etc/Discussions/CheckedExceptions

This is article not convincing at all. His argument is
basically "Most programmers are sloppy and tend to catch and
ignore checked exceptions."

No it is that checked Exception encourage this behavior.

Ultimately, checked exception are a failure as they completely
break encapsulation. Let's say you have a logger interface. Some
of its implementation will just send the log to Dave Null, some
write it in a file, some will send it over the network to some
tailor, and so on. The class of error that arise from each is
completely different and cannot be listed exhaustively at the
interface level in any meaningful way.

The same programmers that do this will just catch all
RuntimeExceptions at
top level, write a log entry and proceed.
That's actually not much better and certainly not correct error
handling.

Tobi

This is often the only meaningful thing you have to do with an
exception anyway.

Jul 26 2015
Bruno Medeiros <bruno.do.medeiros+dng gmail.com> writes:
On 26/07/2015 23:58, deadalnix wrote:
On Sunday, 26 July 2015 at 18:13:30 UTC, Tobias Müller wrote:
Alix Pexton <alix.DOT.pexton gmail.DOT.com> wrote:
On 25/07/2015 9:48 PM, Walter Bright wrote:

Unfortunately, Bruce Eckel's seminal article on it
http://www.mindview.net/Etc/Discussions/CheckedExceptions has
disappeared. Eckel is not a Java code monkey, he wrote the book
Thinking
In Java
http://www.amazon.com/gp/product/0131002872/

https://web.archive.org/web/20150515072240/http://www.mindview.net/Etc/Discussions/CheckedExceptions

This is article not convincing at all. His argument is basically "Most
programmers are sloppy and tend to catch and ignore checked exceptions."

No it is that checked Exception encourage this behavior.

Ultimately, checked exception are a failure as they completely break
encapsulation. Let's say you have a logger interface. Some of its
implementation will just send the log to Dave Null, some write it in a
file, some will send it over the network to some tailor, and so on. The
class of error that arise from each is completely different and cannot
be listed exhaustively at the interface level in any meaningful way.

Then define the logger interface as throwing a generic Exception class.
(a class that sits at the top of the hierarchy of the other Exceptions)

--
Bruno Medeiros

Oct 15 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 7/26/2015 12:51 AM, Alix Pexton wrote:
On 25/07/2015 9:48 PM, Walter Bright wrote:

Unfortunately, Bruce Eckel's seminal article on it
http://www.mindview.net/Etc/Discussions/CheckedExceptions has
disappeared. Eckel is not a Java code monkey, he wrote the book Thinking
In Java
http://www.amazon.com/gp/product/0131002872/

https://web.archive.org/web/20150515072240/http://www.mindview.net/Etc/Discussions/CheckedExceptions

That's it. Thanks for finding the link.

Jul 27 2015
=?UTF-8?Q?Tobias=20M=C3=BCller?= <troplin bluewin.ch> writes:
Walter Bright <newshound2 digitalmars.com> wrote:
On 7/23/2015 12:50 PM, Tobias Müller wrote:
TBH I'm very surprised about that argument, because boolean conditions with
version() were dimissed for exactly that reason.

I knew someone would bring that up :-)

No, I do not believe it is the same thing. For one thing, you cannot test
the various versions on one system. On any one system, you have to take
on faith that you didn't break the version blocks on other systems.

This is quite unlike D's template constraints, where all the combinations
can be tested reliably with a unittest{} block.

How is this related to testability? Using boolean conditions does not
introduce any new code paths compared to helper versions/ helper traits.
Testability is exactly the same.

My point was that you argued with cleaner design in the case of versions.
*I agree with that*, and I think the same is true for traits.
Even if you're right and testability is better, that doesn't contradict the
point that I'm trying to make.

Tobi

Jul 25 2015
Jacob Carlborg <doob me.com> writes:
On 2015-07-23 11:07, Walter Bright wrote:

Consider that template constraints can be arbitrarily complex, and can
even check behavior, not just a list of function signatures ANDed
together. Turns out many constraints in Phobos are of the form (A || B),
not just (A && B).

I know that is possible, but in most cases it's only a list of function
signatures that is needed.

--
/Jacob Carlborg

Jul 23 2015
"Chris" <wendlec tcd.ie> writes:
On Wednesday, 22 July 2015 at 18:47:33 UTC, simendsjo wrote:
Long rant ahead - a bit dipsy..

[snip]

It's one thing to list "nice features", and it's another thing to
use these features in production code. As a code base grows, the
limitations become more and more obvious. Thus, I would be wary
of jumping to conclusions or hailing new features as game
changers, before having tested them thoroughly in the real world.
Only time will tell, if something really scales. I've learned to
wait and see what people with experience report after a year or
two of using a given language.

Jul 23 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 7/23/2015 2:22 AM, Chris wrote:
It's one thing to list "nice features", and it's another thing to use these
features in production code. As a code base grows, the limitations become more
and more obvious. Thus, I would be wary of jumping to conclusions or hailing
new
features as game changers, before having tested them thoroughly in the real
world. Only time will tell, if something really scales. I've learned to wait
and
see what people with experience report after a year or two of using a given
language.

It is very true that many features look good on paper, and only time and
experience reveals the truth. There are a lot of programming features that fail
the second clause - like implicit declaration of variables.

Jul 23 2015
Justin Whear <justin economicmodeling.com> writes:
On Thu, 23 Jul 2015 13:46:16 -0700, Walter Bright wrote:
like implicit declaration of variables.

Trigger warning needed!

Jul 23 2015
"Chris" <wendlec tcd.ie> writes:
On Thursday, 23 July 2015 at 20:46:16 UTC, Walter Bright wrote:
On 7/23/2015 2:22 AM, Chris wrote:
It's one thing to list "nice features", and it's another thing
to use these
features in production code. As a code base grows, the
limitations become more
and more obvious. Thus, I would be wary of jumping to
conclusions or hailing new
features as game changers, before having tested them
thoroughly in the real
world. Only time will tell, if something really scales. I've
learned to wait and
see what people with experience report after a year or two of
using a given
language.

It is very true that many features look good on paper, and only
time and experience reveals the truth. There are a lot of
programming features that fail the second clause - like
implicit declaration of variables.

What happens next is that users demand that things be changed and
adapted to reality, which in turn compromises the original idea.
Then you have a feature soup with dodgy rules. In a way D avoids
this by providing only the ingredients and not the whole meal.

At the end of the day, it's up to the programmer to make the code
safe and stable. Time and again language designers try to avoid
bugs by making the language as rigid and prescriptive as
possible. "This error couldn't happen in X, because every
variable is a Y by default!" However, new features give rise to
new kinds of bugs. Finding a work around for a restriction is
bound to produce bugs.

Jul 24 2015
Timon Gehr <timon.gehr gmx.ch> writes:
On 07/23/2015 10:46 PM, Walter Bright wrote:
On 7/23/2015 2:22 AM, Chris wrote:
It's one thing to list "nice features", and it's another thing to use
these
features in production code. As a code base grows, the limitations
become more
and more obvious. Thus, I would be wary of jumping to conclusions or
hailing new
features as game changers, before having tested them thoroughly in the
real
world. Only time will tell, if something really scales. I've learned
to wait and
see what people with experience report after a year or two of using a
given
language.

It is very true that many features look good on paper, and only time and
experience reveals the truth. There are a lot of programming features
that fail the second clause - like implicit declaration of variables.

That also fails the first clause.

Jul 24 2015
Jacob Carlborg <doob me.com> writes:
On 2015-07-23 22:46, Walter Bright wrote:

like implicit declaration of variables.

I would say that Ruby is pretty far up the list of successful languages,
a lot higher than D ;)

--
/Jacob Carlborg

Jul 24 2015
Walter Bright <newshound2 digitalmars.com> writes:
On 7/24/2015 11:40 AM, Jacob Carlborg wrote:
On 2015-07-23 22:46, Walter Bright wrote:

like implicit declaration of variables.

I would say that Ruby is pretty far up the list of successful languages, a lot
higher than D ;)

I know you're a great fan of Ruby, so I'll bite my tongue :-)

Jul 24 2015
Timon Gehr <timon.gehr gmx.ch> writes:
On 07/23/2015 11:22 AM, Chris wrote:
On Wednesday, 22 July 2015 at 18:47:33 UTC, simendsjo wrote:
Long rant ahead - a bit dipsy..

[snip]

It's one thing to list "nice features", and it's another thing to use
these features in production code. As a code base grows, the limitations
become more and more obvious. Thus, I would be wary of jumping to
conclusions or hailing new features as game changers, before having
tested them thoroughly in the real world.  Only time will tell, if
something really scales. I've learned to wait and see what people with
experience report after a year or two of using a given language.

FWIW, most of the features in his list are very old.

Jul 24 2015
"Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
Thank you for sharing your impressions! I think posts like this
are really useful for the community, because it's important from
time to time to take a look outside of the D ecosystem.

I've been watching Rust with interest myself, though I've mostly
been a passive observer. Some of it's concept fascinate me. My
general impression is that it's a lot stricter than D (good for
correctness), but at the cost of expressiveness. I'm going to
comment on some of your points below:

On Wednesday, 22 July 2015 at 18:47:33 UTC, simendsjo wrote:

Cargo
-----

Very important. Fortunately, dub will be bundled with D in one of
the next versions.

Traits
------
I think the ability to express an interface without buying into
inheritance is the right move. The alternative in D is
specifying the behavior as a template and verifying the
contract in a unittest for the type.

Not only that, but Rust does so much more with traits, e.g.
closures. They seem to be a simple, yet powerful concept, that a
big part of the language is built on.

http://blog.rust-lang.org/2015/05/11/traits.html

On the other hand, it is extremely restrictive in contrast to
static if() and template constraints. D's approach is a lot
more expressive and - at least for me - intuitive.

Macros
------
I haven't written more than extremely simple macros in Rust,
but having macros that is possible for tools to understand is a
win.  Templates and string mixins is often used for this
purpose, but trying to build tools when string mixins exists is
probably extremely hard. If D had hygenic macros, I expect
several features could be expressed with this instead of string
mixins, making tooling easier to implement.

They're certainly cleaner than string mixins. On the other hand,
they are really complex (and probably costly to implement,
because they need to expose the AST). I don't know how
representative it is, but I mostly use them for accessing
aggregate members by name in meta-programming:

foreach(member; FieldNameTuple!T) {
static if(typeof(mixin("T.init." ~ member)) : int) {
// ...
}
}

Or sometimes in cases where using recursive templates to build a
list is too tedious.

I don't think AST macros would gain us much in D.

Safe by default
---------------
D is often said being safe by default, but D still has default
nullable references and mutable by default. I don't see it
being possible to change at this stage, but expressing when I
want to be unsafe rather than the opposite is very nice. I end
up typing a lot more in D than Rust because of this.

safe by default would be really nice. It's easy to add  safe:
at the beginning of your file, but large parts of e.g. vibe.d are
not annotated and need to be called from  system functions. Maybe
more inference would help here, too.

Pattern matching
----------------
Ooooh... I don't know what to say.. D should definitely look
into implementing some pattern matching! final switch is good
for making sure all values are handled, but deconstructing is
just so ergonomic.

Yes, and it goes hand in hand with a more handy tuple syntax.

Expressions
-----------
This probably also falls in the "too late" category, but
statements-as-expressions is really nice. auto a = if ... <-
why not?

Generally I find this an elegant concept. But in Rust, it leads
to the distinction between expressions terminated with ; and
those without, which in turn makes it necessary to use braces
even if you have only one statement or expression. This is
something that I dislike very much.

Borrowing
---------
This is probably the big thing that makes Rust really
different.  Everything is a resource, and resources have an
owner and a lifetime. As a part of this, you can either have
multiple aliases with read-only references, or a single
reference with a writeable reference. I won't say I have a lot
of experience with this, but it seems like it's not an
extremely unergonomic trade-off. I cannot even remotely imagine
the amount of possible compiler optimizations possible with
this feature.

Yes. I haven't given up hope that we can introduce a simplified
version of this in D. It doesn't need to be as strict and
pervasive as Rust's system. I'm still thinking about how to
reduce that into something less complex without breaking too many
of the guarantees it provides.

Jul 23 2015
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 7/23/15 6:56 AM, "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net>"
wrote:
Traits
------
I think the ability to express an interface without buying into
inheritance is the right move. The alternative in D is specifying the
behavior as a template and verifying the contract in a unittest for
the type.

Not only that, but Rust does so much more with traits, e.g. closures.
They seem to be a simple, yet powerful concept, that a big part of the
language is built on.

http://blog.rust-lang.org/2015/05/11/traits.html

On the other hand, it is extremely restrictive in contrast to static
if() and template constraints. D's approach is a lot more expressive
and - at least for me - intuitive.

Thanks for the link, good quick read to get the overview of Rust's
traits feature. It's ingenious because it integrates static and dynamic
dispatch.

For dynamic dispatch, traits are better than interfaces - more flexible,
better informed. For static dispatch, they don't hold a candle to D's
constraints. This is important because dynamic dispatch is more of a
cut-and-dried matter, whereas static dispatch is where it's at.

For static dispatch I think D's template constraints are quite a lot
better; they have a lot more power and offer a lot more to promise. They
are an out-of-the-box solution that's a bit unwieldy because it's new
enough to not yet have established idioms. In contrast, traits come from
straight within the box.

From a language perspective, it is my belief that Design by
Introspection (enabled collectively by template constraints,
introspection, compile-time function evaluation, and static if) is the
one crushing advantage that D has over any competitor. Things like
range-based algorithms are good but easy to copy and adapt.

The faster we manage to get creative with Design by Introspection and
describe it, systematize it, create compelling designs with it, the
quicker will D win the minds and hearts of people.

There's a lot at stake here. An question often asked by would-be users
is "What does D offer that's special?" or "What does D offer over
language Xyz?" As we all know there are many answers to that. Maybe too
many. Now I know what the answer is: Design by Introspection.

Andrei

Jul 23 2015
On Thursday, 23 July 2015 at 14:08:23 UTC, Andrei Alexandrescu
wrote:
Thanks for the link, good quick read to get the overview of
Rust's traits feature. It's ingenious because it integrates
static and dynamic dispatch.

For dynamic dispatch, traits are better than interfaces - more
flexible, better informed. For static dispatch, they don't hold
a candle to D's constraints. This is important because dynamic
dispatch is more of a cut-and-dried matter, whereas static
dispatch is where it's at.

On that note, I've mentioned scala's trait, which are kind of
similar and worth looking at. The thing being based on java's
object model, as D's object model, it is easier to think about
how this could get into D.

For static dispatch I think D's template constraints are quite
a lot better; they have a lot more power and offer a lot more
to promise. They are an out-of-the-box solution that's a bit
unwieldy because it's new enough to not yet have established
idioms. In contrast, traits come from straight within the box.

Certainly, but they suffer from the LISP effect. You can do
everything because the structure does not constrain you in any
way, while at the same time it become quickly very hard to
understand, for the very same reason.

I do think think the opposition between the 2, as seen in your
post, or Stroustrup's allergy to static if is wrong headed.

May be one can be expressed via the other ?

Jul 23 2015
Jacob Carlborg <doob me.com> writes:
On 2015-07-23 12:56, "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net>"
wrote:

Expressions
-----------
This probably also falls in the "too late" category, but
statements-as-expressions is really nice. auto a = if ... <- why not?

Generally I find this an elegant concept. But in Rust, it leads to the
distinction between expressions terminated with ; and those without,
which in turn makes it necessary to use braces even if you have only one
statement or expression. This is something that I dislike very much.

In Scala there's no problem. No semicolons are required a no braces.

--
/Jacob Carlborg

Jul 23 2015
Walter Bright <newshound2 digitalmars.com> writes:
C++ concepts for those interested:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3701.pdf

Jul 26 2015
Bruno Medeiros <bruno.do.medeiros+dng gmail.com> writes:
On 22/07/2015 19:47, simendsjo wrote:
Long rant ahead - a bit dipsy..

TL;DR: Rust has momentum, manpower and tooling. Tooling matters. Safe
defaults.  Ergonomics like expressions and deconstructing rocks.

Tooling doesn't just matters. Tooling trumps everything else.

I've skimmed through this discussion and noticed a lot of discussion
about Rust's traits vs. D's template constraints, as well as a few other
minor language features. And while there is value in such discussion -
as it may bring a better understanding or clarity about language design,
or perhaps even a few language or library changes - don't be under the
illusion that ultimately it will make any significant difference in
language adoption, if the tooling quality differs a lot (and it does).

Minor differences, shortcomings even, between languages will only have a
big impact for the kind of people that approach language preference with
an "art appreciator" kind of mentality. An almost platonic/voyeristic
approach. But for people building non-small, real-world projects (the
"engineering" approach), tooling will trump everything else.

Only if the language differences where massive (say between D and Go),
would perhaps tooling not trump language design... but even then, it
would still be a big fight between the two!

--
Bruno Medeiros

Jul 30 2015
"Alex Parrill" <initrd.gz gmail.com> writes:
On Thursday, 30 July 2015 at 11:46:02 UTC, Bruno Medeiros wrote:
Tooling doesn't just matters. Tooling trumps everything else.

I don't agree. IMO reducing the need for tools would be a better
solution.

For example, there's no need for a memory checker if you're
writing in Python, but if you're writing in C, you better start
learning how to use Valgrind, and that takes time.

Also there's Javascript's overabundance of tooling, with varying
levels of quality, way too many choices (grunt vs gulp vs ...,
hundreds of transpilers), and incompatibilities (want to use JSX
and TypeScript together? Good luck).

To take it to the extreme, no matter how much tooling you write
for BrainFuck, I doubt anyone will use it.

I think D goes in the right track by embedding things like unit
tests, function contracts, and annotations into the language
itself, even if the implementations could capitalize on them
better than they do now.

Jul 30 2015
On Thursday, 30 July 2015 at 14:23:34 UTC, Alex Parrill wrote:
On Thursday, 30 July 2015 at 11:46:02 UTC, Bruno Medeiros wrote:
Tooling doesn't just matters. Tooling trumps everything else.

I don't agree. IMO reducing the need for tools would be a
better solution.

For example, there's no need for a memory checker if you're
writing in Python, but if you're writing in C, you better start
learning how to use Valgrind, and that takes time.

Also there's Javascript's overabundance of tooling, with
varying levels of quality, way too many choices (grunt vs gulp
vs ..., hundreds of transpilers), and incompatibilities (want
to use JSX and TypeScript together? Good luck).

To take it to the extreme, no matter how much tooling you write
for BrainFuck, I doubt anyone will use it.

I think D goes in the right track by embedding things like unit
tests, function contracts, and annotations into the language
itself, even if the implementations could capitalize on them
better than they do now.

It is not matter of agreeing or not. It is matter of fact.
Language with good tooling work, language with poor tooling do
not.

C++ did lose traction compared to java for a while and only came
back up recently because Moore law is starting to not yield
expected results and tooling dramatically improved.

Tooling is king, and beat language solution most of the time.

Jul 30 2015
Bruno Medeiros <bruno.do.medeiros+dng gmail.com> writes:
On 30/07/2015 15:23, Alex Parrill wrote:
On Thursday, 30 July 2015 at 11:46:02 UTC, Bruno Medeiros wrote:
Tooling doesn't just matters. Tooling trumps everything else.

I don't agree. IMO reducing the need for tools would be a better solution.

For example, there's no need for a memory checker if you're writing in
Python, but if you're writing in C, you better start learning how to use
Valgrind, and that takes time.

That doesn't go against what I said. Not having the need for a tool can
be seen as identical to having the perfect tool (a tool you do not need).

Imagine I had said it this way: Tooling shortcomings trump all other
shortcomings (like language design shortcomings). Not having the need
for a particular tool, is like saying that tool has no shortcomings
whatsover.

--
Bruno Medeiros

Jul 30 2015
"Enamex" <enamex+d outlook.com> writes:
On Wednesday, 22 July 2015 at 18:47:33 UTC, simendsjo wrote:
Long rant ahead - a bit dipsy..

TL;DR: Rust has momentum, manpower and tooling. Tooling
matters.  Safe defaults.  Ergonomics like expressions and
deconstructing rocks.
[...]
But again... After playing a bit with Rust, I feel it lacks a
lot in expressive power. D has templates, template mixins,
alias this, string mixins, opDispatch etc. In my little time
with Rust, I've seen several pages of generic constrains that
is expressible in a couple of lines with D. I've seen
copy/pasted code that just isn't necessary when you code in D.

Anyways - my little ramblings after trying the Rust programming
language while I haven't used D in a long, long while (But I'm
still here now, as I'm not sure Rust is able to express
everything that is possible with D). Looking forward to
following D again :)

Mostly my experience, so far. If I have to choose the 'most
important' things that Rust has that I'd *definitely* want in D,
I'd pick (in order):

* A really nicely integrated package manager like Cargo that goes
seamlessly hand-in-hand with DMD.
* DDMD
* An attribute or something that makes it explicit to the
compiler that this type is a 'one time use' deal, and /exactly/
one-time-use (or, to be practical, zero-or-one times). Copying
instances of objects of that type is done by .dup. (Like a
non-Copy type in Rust currently.)
* Sum Data Types (enums in Rust and data D = VarA | VarB in
* Pattern matching. Algebraic! is okay but pattern-matching
that goes with it isn't PM.

But maybe that's for D 3.0 in 2030 or maybe another language...
(I just want SML with at least D's speed T_T. I already got
something /very roughly/ akin to ML's functors from D's template
and mixin magic...)

Jul 30 2015
"Enamex" <enamex+d outlook.com> writes:
On Friday, 31 July 2015 at 03:41:35 UTC, Enamex wrote:
[...]
Mostly my experience, so far. If I have to choose the 'most
important' things that Rust has that I'd *definitely* want in
D, I'd pick (in order):

* A really nicely integrated package manager like Cargo that
goes seamlessly hand-in-hand with DMD.
* DDMD
[...]

Ouch. Actually forgot second most important point (right before
DDMD): a ~99.99%  nogc Phobos and better documentation for GC
stuff. Right now docs say that delete is getting deprecated but
using it on DMD .067.1 gives no warnings.

Jul 30 2015
"Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Friday, 31 July 2015 at 04:47:20 UTC, Enamex wrote:
Right now docs say that delete is getting deprecated but
using it on DMD .067.1 gives no warnings.

There are no warnings because it hasn't actually been deprecated
yet. So, the docs are quite correct. They say that it's _going_
to be deprecated, not that it has been deprecated. Ideally, it
would have been deprecated quite some time ago, but without the
custom allocators, it's a lot harder to do something similar to
what delete does. So, if we'd actually deprecated it, we'd have
done so without a viable alternative (it's possible without
custom allocators, but it's hard to get right). Now, I think that
the reality of the matter that it hasn't been deprecated is
simply because no one has gotten around to it yet, but there are
problems with deprecating it prior getting customer allocators.
Fortunately, it looks like we will soon have that in
std.experimental, so it will become more reasonable to deprecate
delete, and maybe we can finally deprecate it and start moving it
out of the language. But it's been planned for ages that delete
would be removed from D at some point.

- Jonathan M Davis

Jul 31 2015
"Tofu Ninja" <emmons0 purdue.edu> writes:
On Friday, 31 July 2015 at 09:37:10 UTC, Jonathan M Davis wrote:
On Friday, 31 July 2015 at 04:47:20 UTC, Enamex wrote:
Right now docs say that delete is getting deprecated but
using it on DMD .067.1 gives no warnings.

There are no warnings because it hasn't actually been
deprecated yet. So, the docs are quite correct. They say that
it's _going_ to be deprecated, not that it has been deprecated.
Ideally, it would have been deprecated quite some time ago, but
without the custom allocators, it's a lot harder to do
something similar to what delete does. So, if we'd actually
deprecated it, we'd have done so without a viable alternative
(it's possible without custom allocators, but it's hard to get
right). Now, I think that the reality of the matter that it
hasn't been deprecated is simply because no one has gotten
around to it yet, but there are problems with deprecating it
prior getting customer allocators. Fortunately, it looks like
we will soon have that in std.experimental, so it will become
more reasonable to deprecate delete, and maybe we can finally
deprecate it and start moving it out of the language. But it's
been planned for ages that delete would be removed from D at
some point.

- Jonathan M Davis

I would much rather delete to stay and rig it up so new and
delete call the global allocator(which would be the GC by
default).

Jul 31 2015
"Enamex" <enamex+d outlook.com> writes:
On Friday, 31 July 2015 at 09:37:10 UTC, Jonathan M Davis wrote:
On Friday, 31 July 2015 at 04:47:20 UTC, Enamex wrote:
Right now docs say that delete is getting deprecated but
using it on DMD .067.1 gives no warnings.

There are no warnings because it hasn't actually been
deprecated yet.
[...]
- Jonathan M Davis

GC and memory management in general are inadequately documented.
There're doc-pages and answers on SO and discussions on the forum
about stuff that (coming from C++) should be so basic, like how
to allocate an instance of a struct on the heap (GC'ed or
otherwise) or how to allocate a class on non-managed heap (still
don't get how Unique! works; does it even register with the GC?
How to deep-copy/not-move its contents into another variable?),
or on the stack, for that matter (there's scoped! but docs
again are confusing. It's somehow stack-allocated but can't be
copied?).

Eventually deprecating it while leaving it now without any
warnings (though the docs warn; offer no replacement) seems like
it'd be more trouble than it's worth down the line, since it's
not a feature addition or even full deprecation but -AFAIU- a
replacement of semantics for identical syntax.

Aug 03 2015
"rsw0x" <anonymous anonymous.com> writes:
On Wednesday, 22 July 2015 at 18:47:33 UTC, simendsjo wrote:
...

One thing I didn't see mentioned at all is Rust's plugin system.

Rust plugin to embed C++ directly in Rust:
https://github.com/mystor/rust-cpp
Rust plugin to use Rust with whitespace instead of braces:
https://github.com/mystor/slag

Syntax extensions, lint plugins, etc are all possible via the
plugin interface.
https://doc.rust-lang.org/book/compiler-plugins.html

Honestly, this is pretty cool :/

This thread is really long so I didn't read all the posts. Sorry
if this has been mentioned.

Aug 04 2015
"Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= writes:
On Wednesday, 5 August 2015 at 06:03:14 UTC, rsw0x wrote:
Syntax extensions, lint plugins, etc are all possible via the
plugin interface.
https://doc.rust-lang.org/book/compiler-plugins.html

Honestly, this is pretty cool :/

Yes, it looks very cool. It lowers the threshold for
experimentation and testing new ideas.

Aug 04 2015
"jmh530" <john.michael.hall gmail.com> writes:
On Wednesday, 5 August 2015 at 06:03:14 UTC, rsw0x wrote:
This thread is really long so I didn't read all the posts.
Sorry if this has been mentioned.

Don't worry, I don't recall anybody talking about it.
Nevertheless, plugins aren't unique to Rust: GCC and Clang allow
plugins. Nevertheless, I think that the Rust C++ plugin you
posted was cool. Reminds me of Rcpp.

Aug 05 2015
"Enamex" <enamex+d outlook.com> writes:
On Wednesday, 5 August 2015 at 14:21:13 UTC, jmh530 wrote:
On Wednesday, 5 August 2015 at 06:03:14 UTC, rsw0x wrote:
This thread is really long so I didn't read all the posts.
Sorry if this has been mentioned.

Don't worry, I don't recall anybody talking about it.
Nevertheless, plugins aren't unique to Rust: GCC and Clang
allow plugins. Nevertheless, I think that the Rust C++ plugin
you posted was cool. Reminds me of Rcpp.

Oh, it _is_ talked about a lot. Just normally called 'syntax
extensions' (the most used aspect of the plugin system), so it
_is_ used. Though because it relies on compiler internals
everything released as a plugin is only usable on Nightly.

Aug 05 2015
"jmh530" <john.michael.hall gmail.com> writes:
On Wednesday, 5 August 2015 at 22:24:34 UTC, Enamex wrote:
Oh, it _is_ talked about a lot. Just normally called 'syntax
extensions' (the most used aspect of the plugin system), so it
_is_ used. Though because it relies on compiler internals
everything released as a plugin is only usable on Nightly.

a bunch of mentions on the forums (which I found after searching
the term you list), but only two in the past day (including
`