On 9/27/2014 4:20 PM, bearophile wrote:
Walter Bright:
It could be nice to have a syntax like lambdas for small struct/class methods:
https://issues.dlang.org/show_bug.cgi?id=7176
struct Foo {
int bar(int x) pure { return x * x; }
}
Becomes something like::
struct Foo {
int bar = (int x) pure => x * x;
}
I see no gain from that syntax.
Issue 7176 has currently seven votes, so someone seems to like it.
A feature without a solid rationale is no good in spite of how many votes it
has.
Now some D code is written in a functional style, this means many function
bodies are very small, and sometimes they contain just a UFCS chain.
Usually editors (and Phobos style guides) format that bar function like:
int bar(int x) pure
{
return x * x;
}
So now you have 4 lines instead of 1 line of the lambda-like syntax. Generally
the presence of a "return" is regarded as a "code smell" in functional-style
code.
D has a nice inline lamda syntax, which was needed. It is not needed for
separate functions.
I suggest using 'pure' and passing the globals you actually need as ref
parameters.
This is what I usually do in D, but it has some small disadvantages:
- It increases the number of function arguments (they are 8 bytes each),
increasing the size of the function, adding some stack management instructions.
This slows the function a little, if the function is performance-critical.
If the function is small enough that parameter setup time is significant, it is
likely to be inlined anyway which will erase the penalty.
Such arguments can also be passed as template alias parameters, which are zero
cost.
- Sometimes you can't use "pure", for various reasons, like Phobos functions not
yet pure, I/O action in your function, or other causes.
Then you really aren't encapsulating the globals the function uses, anyway, and
the feature is useless.
- If your pure function foo receives two global argument as out and ref, the
function bar that calls it needs to access to global variables or it too needs
those two ref/out arguments. The current design of @outer() is not transitive,
so only foo needs to state what global variables are in/out/inout.
Being global variables, they are accessible from everywhere :-) which is why
they're an encapsulation problem in the first place.
- @outer is more DRY, because you don't need to specify the type of the global
variable received by ref, you just need to know its name.
That's why gawd invented templates.
- With @outer you can tighten some old code, without changing the signature of a
function. If you have an old D module (or a C function converted to C) you often
can't (or you don't want) to change the function signature to add the global
arguments passed by ref. With @outer() the function signature doesn't change, so
you can improve your legacy code. It allows a simpler refactoring of code.
You can always wrap it with a template and pass the global by alias.
More notes:
- SPARK language has added a feature similar to @outer, but more verbose. See
comment 5 in issue 5007.
- @outer() is optional and it's fiddly because not it's not meant for small D
script-like programs, it's meant as a help for medium-integrity D programs
(where you may think about using Ada language instead).
Better encapsulation is intended for larger programs - I agree it is useless for
small ones.