On 04/03/2013 19:16, Alexander Klenin wrote:
function NAME(PARAMS): RETURNTYPE as EXPRESSION
IMHO that is not a good idea. It adds an additional construct, that every
reader must know how to read. It does not add/save enough to be needed.
I agree, it is borderline, and needs to be additionally justified.
I believe that for each feature, there is a threshold for the baroqueness of
syntax required to use it which separates "obscure" from "simple and
understandable".
The main barrier for understanding code is not actually the number of
language constructs involved -- they are way too few compared to,
say, number of procedures in any non-trivial library.
The main barrier is the amount of effort required to see the programmers intent
while looking at a given piece of code.
For example (still using "lambda" syntax here to not muddle the issue,
I'll discuss your alternatives to "lambda" below):
// Task: Select five largest even numbers from the array:
// (0) Theoretically possible with type inference:
AIntArray.Filter(lambda X div 2 = 0).Sort(lambda X > Y).Take(5);
How is that possible? In "lambda X > Y " how does the compiler know, if
X or Y is the first param?
// (1) My proposal
AIntArray.Filter(lambda TIntFilter as X div 2 = 0).Sort(lambda TIntCmp
as X > Y).Take(5);
And that is a good example *against* anonymous functions. Even with some
c# background (and it does remind me of it / maybe general dotNet), I
had to read it several times.
Sure, if it was highlighted it might be easier to find the keywords
lambda/as and to know where the expression (the truncated "result :="
statement starts.
// (2) Without "as"
AIntArray.
Filter(lambda TIntFilter; begin Result := X div 2 = 0 end).
Sort(lambda TIntCmpl; begin Result := X > Y; end).Take(5);
You comparison is tainted. the begin/end are not needed. Though, I
actually would like them required. Well at least the end. The lambda
might serve as the opening.
AIntArray.
Filter(lambda TIntFilter; Result := X div 2 = 0 end).
Sort(lambda TIntCmpl; Result := X > Y; end).Take(5);
And that is of course because of your preference for keyword over typecast.
Though you are right, even in a typecast a keyword would be good to indicate
this code is not executed right away.
AIntArray.
Filter(TIntFilter(lambda Result := X div 2 = 0 end).
Sort(TIntCmpl(lambda Result := X > Y end).Take(5);
Then a none type casted lambda could return a "procedure();", no params, no
result.
// (4) Without anonymous functions:
function IsEven(X: Integer): Boolean; begin
Result := X div 2 = 0;
end;
function IsGreaterThan(X, Y: Integer): Boolean; begin
Result := X > Y;
end;
// ... possibly a lot of code here
AIntArray.Filter(@IsEven).Sort(@IsGreaterThan).Take(5);
And the actual fitter/sort line is so much easier to read. Really, so
much easier...
As you can see, there is a spectrum of expressive power vs language simplicity.
It is debatable at which position of the spectrum Pascal should be,
but my opinion is that (4) is way too weak, (0) is probably too strong,
and (1) is easier to read then (2), (3) or (4) for any reasonably
literate programmer,
actually from a point of readability
TIntCmpl(lambda Result := X > Y end
looks easiest to me. WIthin the typecast, you have only an opening
/closing keyword. The rest is the code. No distraction
We could also use the new, have a constructor for everything (TArray.Create)
style. Personally, the idea appals me, but
TVisitor.Create(Result := x + 5)
I actually dislike this syntax even for arrays.
As I said: the idea appals me
Another way to avoid the "as"
lambda(TVisitor) .... end
lambda acts as "begin" to the block, so there always must be an end.
Why not also omit "end" then? Just say that "lambda" must be followed
by a single statement,
No Pascal is verbose. It must be a proper block. TO easy to misread
otherwise.
It does not have to finish with end, there could be another keyword.
(Like in repeat until pair).
If there is no block
Foo(lambda TFunc x = a, lambda TFunc y<b)
versus
Foo(lambda TFunc x = a(lambda TFunc y<b))
And if (another of those I personally do not like it) tuples will come,
then
Foo(lambda TFunc x = (a, lambda TFunc y<b))
where x is a tuple, and the bracket is too.
I want a none return ticket to a different planet.
then for the case of many statements, "begin..end" should be used like
in normal blocks.
The only problem is with local declarations, but I guess it is
possible to disambiguate
based on "var", "const" and "type" keywords appearing after "lambda".
Argh yes, I hadn't thougt of that yet.
Well then that just makes it an even bigger monstrosity.
I am not going to think about that now. To much work for something that
I do not want in first....
See above. The omission "of Result :=" is IMHO not desirable.
Are you sure? Note that *any* useful single-statement function will
start exactly like this
(strictly speaking, it could be also be call to "exit", but obviously
it makes no difference).
And single-statement functions are the most important use case for
anonymous functions --
basically, if it takes several lines of code anyway, then in might as
well be a named function.
Yes. I used them plenty in perl. But this is pascal. Not perl.
IMHO the existing procedure/function keywords should be kept. But with the
requirement of using a defined type
Foo( function as TVisitor; Result := x+5 end; );
Foo( function as TVisitor; begin Result := x+5 end; );
Foo( function of TVisitor; begin Result := x+5 end; );
Foo( function TVisitor( Result := x+5 ) );
Yes, this could work too, and it does extend well to named functions/procedures:
procedure TForm1.Grid1OnDrawCell as TGridOnDrawCell;
we need to find a better substitute for "as"
using, like, of,
procedure TForm1.Grid1OnDrawCell prototype(TGridOnDrawCell); // brackets are
optional, but seem better
_______________________________________________
fpc-devel maillist - [email protected]
http://lists.freepascal.org/mailman/listinfo/fpc-devel