July 26, 2015
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.
July 26, 2015
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.
July 27, 2015
C++ concepts for those interested: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3701.pdf
July 27, 2015
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 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.

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 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.

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
July 27, 2015
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
>
> It's arguably a better trade-off.

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.
July 27, 2015
On 7/26/2015 6:59 PM, Jonathan M Davis wrote:
> [...]

Thank you, Jonathan!

July 27, 2015
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
July 27, 2015
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?
July 27, 2015
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:

https://github.com/CyberShadow/DFeed/issues/44
July 27, 2015
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