Andrei Alexandrescu wrote:
Ary Borenszweig wrote:
Andrei Alexandrescu escribió:
Nice! :-)
I showed the Fibonacci example to a friend of mine and he said "that
string stuff scares me a little". The same happens to me.
What kind of error do you get if you mistype something in that string
expression?
Let me see. Say I made the mistake of using "b":
auto fib = series!("a[n-1] + b[n]")(1, 1);
std/functional.d(185): static assert "Bad binary function: a[n] +
b[n-1] for types Cycle!(int[]) and uint. You need to use an expression
using symbols a and n."
Heck, that's even better than compiler's own error messages.
Unfortunately, the line where the actual instantiation was invoked is
not shown. I think that's entirely doable though.
In BLADE, what I did was provide an error message as an alternate return
value from the inner templates. This is then passed down through the
higher-level functions. At the user level, instead of mixing in code to
invoke the function, it mixes in a static assert(0, errormsg) instead.
Then there is an error ONLY on the line of user code. You can only
achieve these perfect error messages if you have a mixin statement in
the user code, unfortunately.
Of course, using return values to indicate errors is so 1970's.
Compile-time assert exception catching would be so much better...
But a simple change to the static assert handling would get us most of
the way there.
Say I forget to close a bracket:
auto fib = series!("a[n] + a[n-1")(1, 1);
The compiler penalizes that by never finishing compiling. That's a bug.
There's a few cases in bugzilla about mismatched brackets. Some of them
cause segfaults.
Can you pass a delegate instead of a string? Something like:
auto fib = series!((Range a, int n) { return a[n-1] + a[n]; })(1, 1);
Not right now because delegate literals can't be templates. But I talked
about that with Walter and he'll make it work.
That seems much safe and will probably give a reasonable error if you
mistype something. I know, it's longer than the previous one. But it
would be nice if D had the following rule (or something similar to
it): "if a delegate doesn't return, the last statement is converted to
a return" (and you can ommit the semicolon). So now you can write:
auto fib = series!((Range a, int n) { a[n-1] + a[n] })(1, 1);
And if you could just ommit the types in the delegate...
auto fib = series!((a, n) { a[n-1] + a[n] })(1, 1);
Still a little longer than the original, but you get all the benefits
of not using a string.
Incidentally, I was talking to Walter about the shorter delegate form
and he convinced me that it's too risky to have semantics depend on only
one semicolon. But omitting the type names from a literal delegate is
something we're mulling over.
The truth is, the reasons against using strings for short functions are
shrinking. I mean, you don't want to not use strings just to not use
strings, right? I hope I convinced you that strings are unbeatable for
short functions that don't need access to local state, their efficiency
is exemplary, and their error messages are not half bad.
Andrei