[fpc-devel] Delphi anonymous methods

Alexander Klenin klenin at gmail.com
Mon Mar 4 20:16:51 CET 2013


On Tue, Mar 5, 2013 at 3:34 AM, Martin <lazarus at mfriebe.de> wrote:
> On 04/03/2013 16:05, Alexander Klenin wrote:
>>
>> Anonymous functions (with good syntax, of course) fall in this category.
>> The world recognized that fact -- rather slowly, to be sure, but
>> remember that "while"s and "for"s
>> also took decades to be accepted as standard constructs.
>
>  I am not going to ask for proof of this statement
I've made two statements here:
1) Anonymous functions are considered essential part of any modern
programming language.
  For a proof, look at any modern programming language :)
  Or, to save time, here is a list:
  http://en.wikipedia.org/wiki/Anonymous_function#List_of_languages
2) "while" loop took a long time to be invented and widely accepted.
  First high-level programming languages, like FORTRAN and COBOL,
created in 1950s,
  did not have it. First more-or-less widely known language to support
Algol, which came about 1960.
  However, Algol did not became used in practice, and the notion of
  structured programming was widely debated until the 1970s.
  For example, FORTRAN 77 still did not have "while" loop,
  it was considered an extension implemented by some compilers,
  and it was a standard recommendation to rewrite "while" using "if" and "goto"
  to increase code portability.
  So it took about 10 to 20 years for "while" loops to emerge and
become commonplace.
  I must note that the part of my statement about "for" is totally wrong --
  I just typed without thinking. Of course, FORTRAN had "DO" loops
right from the beginning.

>>    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);

// (1) My proposal
AIntArray.Filter(lambda TIntFilter as X div 2 = 0).Sort(lambda TIntCmp
as X > Y).Take(5);

// (2) Without "as"
AIntArray.
  Filter(lambda TIntFilter; begin Result := X div 2 = 0 end).
  Sort(lambda TIntCmpl; begin Result := X > Y; end).Take(5);

// (3) Without "lambda"
AIntArray.
  Filter(function (X: Integer): Boolean; begin Result := X div 2 = 0 end).
  Sort(function (X, Y: Integer): Boolean; begin Result := X > Y; end).Take(5);

// (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);

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,
regardless of whether or not he knows about details of anonymous
functions in Pascal.

> 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.
Nevertheless, I agree that this could be good idea, except it works only for
anonymous functions, thus losing the advantages for named ones.

> 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,
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".
My intention with the designated "as" keyword is to reduce the need
for look-ahead
in a grammar (which, btw, can also be considered one of "Pascal principles").
But certainly, is the "as" keyword can always be inferred from context,
then I am all for omitting it.

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

>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;
begin
  ...
end;

However, for shortened syntax, I'd prefer single-statement-body
interpretation I described above:
  Foo(function as TVisitor; Result := x+5); // one statement
  Foo(function as TVisitor; begin x *= 2; Result := x+5; end); //
several statements
or, with "Result := " omission
  Foo(function as TVisitor; x+5);

--
Alexander S. Klenin



More information about the fpc-devel mailing list