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

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

July 24, 2015
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
July 24, 2015
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
July 24, 2015
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
July 24, 2015
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 your parameter types in Java.

-- 
/Jacob Carlborg