Rant after trying Rust a bit

Tofu Ninja via Digitalmars-d digitalmars-d at puremagic.com
Sat Jul 25 04:12:34 PDT 2015


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



More information about the Digitalmars-d mailing list