July 24, 2015
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.

July 24, 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.
July 24, 2015
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
>> 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.

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);
July 24, 2015
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.
July 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?
July 24, 2015
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 doesn't add much additional weight.
July 24, 2015
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
July 24, 2015
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
July 24, 2015
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
July 24, 2015
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
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19