Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-devel / 2013-01

Anonymous procedures (Was: Re: for-in-index loop)

13 messages · 5 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Alexander Klenin
Sven Barth wrote
One could also do an alternative (though currently not with arrays, but with type helper support even that would be possible...):

Yes, this is certainly the most interesting alternative. Actually, anonymous procedures/closures
is the "real" task I wanted Vasily to perform, after the "warm-up" of for-in-index.
I am still of the opinion that "for-in-index" would be a good addition nevertheless,
because it is a simple and neat solution for 90% of the cases, so even if there exists
a general and complex solution for 100%, IMHO simple one is still worth having.

Nevertheless, I separated the thread to discuss anonymous procedures, hopefully with less flame, or at least, with flame of different colours :)

When comparing anonymous procedures with built-in for-in loop, it is important to consider the code speed.
I think that good support for inlining "Iterate" procedure *together with its argument*
is essential for anonymous procedures to be a competitive solution for the problem
of container iteration.

Show quoted text 5 lines
map.Iterate(procedure(const aKey: String; const aValue: TObject) begin Writeln(aKey, ' => ', aValue.ClassName); end; );

As you noted, this solution is conceptually good, but rather cumbersome to write

You have also proposed lambda-expressions:

map.Iterate(lambda TFPGMapLongInt.TIteratorProc(aKey, aData) as Writeln(aKey, ' => ', aData.ClassName));

I think that they are not optimal in the proposed form. Note that the longest part of the expression is actually a restatement of the Iterate parameter type.
I propose to allow omitting that:

map.Iterate(lambda begin

  Writeln(aKey, ' => ', aData.ClassName);

end;)

That is, basically, "lambda" means "use procedure definition from the parameter type".

Independently, I propose a general shorthand for functions containing a single expression
(and perhaps for procedures containing a single statement, but I am less certain here):

function f(a, b: Integer): Integer;
begin Result := a + b; end;
=>
function f(a, b: Integer): Integer as a + b;

with both shortcuts combined, it becomes possible to write something like

sum := arr.Reduce(lambda as a + b;);
Even further, Ruby (or Objective C)-like extension may be introduced to allow
procedure arguments (or perhaps just the last one) to be separated by
some special character instead of parenthesis, for example:
procedure p(a, b: Integer);
p(3, 5); // Traditional syntax
p :3 :5; // Proposed extension
Then, then code will be
map.Iterate :lambda
  Writeln(aKey, ' => ', aData.ClassName);
end;
sum := arr.Reduce :lambda as a + b;

which is as good as it gets, IMHO.

And for a given array type and type helper support you can do the following:

While type helpers are a neat idea, I fear they introduce too much "action at a distance"
for so frequent usage.
A global generic functions (they are planned anyway, aren't they?) will be sufficient:

Iterate :map :lambda
  Writeln(aKey, ' => ', aData.ClassName);
end;
Alexander S. Klenin

Reply by email

Sven Barth
to Alexander Klenin
Alexander Klenin wrote 8 lines
On Sat, Jan 26, 2013 at 5:17 AM, Sven Barth wrote: > One could also do an alternative (though currently not with arrays, but with > type helper support even that would be possible...): Yes, this is certainly the most interesting alternative. Actually, anonymous procedures/closures is the "real" task I wanted Vasily to perform, after the "warm-up" of for-in-index.

Just in case: we already have a branch where someone started the development for anonymous functions some months ago. You can find it here:

http://svn.freepascal.org/cgi-bin/viewvc.cgi/branches/blaise/closures/

Please Note: the branch currently won't compile as the unit "pnameless" is missing. I've asked the author whether he wants to continue his work or if he could at least leave the branch in a compiling state. I'm still waiting for an answer (he confirmed me though that he got my mail and that he'll answer me when he finds the time).

Show quoted text 14 lines
You have also proposed lambda-expressions: > map.Iterate(lambda TFPGMapLongInt.TIteratorProc(aKey, aData) as Writeln(aKey, ' => ', aData.ClassName)); I think that they are not optimal in the proposed form. Note that the longest part of the expression is actually a restatement of the Iterate parameter type. I propose to allow omitting that: map.Iterate(lambda begin Writeln(aKey, ' => ', aData.ClassName); end;) That is, basically, "lambda" means "use procedure definition from the parameter type".

I have already thought about this as well, but this is simply not how Pascal (and especially the compiler) works. In Pascal the result of an expression is (at first) independantly of the result it is assigned to. For example:

double1 := 1.0/2.0;

here the expression on the right side will be evaluated with the smallest possible precision that does not cause data loss (let's assume for now that 1.0, 2.0 and 0.5 all can be represented by a Single) and only in the assignment the result is converted to a Double.

Also there's the problem of overloaded procedures. Let's assume we have

type
   TProc1 = reference of procedure(aArg: Integer);
   TProc2 = reference of procedure(aArg: String);
procedure SomeProc(aArg: TProc1);
procedure SomeProc(aArg: TProc2);
SomeProc(lambda as Writeln(aArg));

Which SomeProc to choose? Even if the lamda's code could derive the argument types directly (which must not be the case as you can see in the example, as Writeln can take an Integer as well as a String) you'd still need to find the correct procedure.

Also the parameter names MUST be declared somewhere. This is Pascal. There can't be a symbol name appearing suddenly in the middle of the code.

These three points are why I proposed my example as is: we need somehow the signature of the lambda. Maybe "lambda(aArg: Integer) as Writeln(aArg)" would be sufficient already (or "lambda(aArg: Integer): String as IntToStr(aArg)" for a function).

Otherwise - to borrow a statement from the for-in-index thread - we would be butchering Pascal.

Show quoted text 11 lines
Independently, I propose a general shorthand for functions containing a single expression (and perhaps for procedures containing a single statement, but I am less certain here): function f(a, b: Integer): Integer; begin Result := a + b; end; => function f(a, b: Integer): Integer as a + b;

I don't know whether I'd want to extend this to single line functions/procedures. This is where the language becomes less clear again.

Show quoted text 7 lines
with both shortcuts combined, it becomes possible to write something like sum := arr.Reduce(lambda as a + b;); Even further, Ruby (or Objective C)-like extension may be introduced to allow procedure arguments (or perhaps just the last one) to be separated by some special character instead of parenthesis, for example:

No, no, no. No "special characters". If you want to differentiate something, then use keywords.

Regards,
Sven

Reply by email

Alexander Klenin
to Paul Ishenin
Paul Ishenin wrote 5 lines
26.01.13, 6:57, Alexander Klenin пишет: Why to invent a new solution if Delphi already have one: http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/devcommon/anonymousmethods_xml.html

Of course, the Delphi-compatible implementation is a basis -- I did not even mention it, because I thought it is obvious. (Although I would still like to devise some solution for using closures for events --
"IDE does not recognize them" seems quite a weak obstacle to me).

My proposals were for syntax extensions to eliminate the ugliness of procedure-inside-extression construct.

Alexander S. Klenin

Reply by email

Sven Barth
to Alexander Klenin
Alexander Klenin wrote 13 lines
On Sun, Jan 27, 2013 at 12:26 AM, Paul Ishenin wrote: > 26.01.13, 6:57, Alexander Klenin пишет: > > Why to invent a new solution if Delphi already have one: > http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/devcommon/anonymousmethods_xml.html > Of course, the Delphi-compatible implementation is a basis -- I did not even mention it, because I thought it is obvious. (Although I would still like to devise some solution for using closures for events -- "IDE does not recognize them" seems quite a weak obstacle to me).

I assume this is because anonymous functions are not plain methods. Thus they are not compatible with TMethod (the type behind "procedure/function of object"). They are instead based on a different (internal) type.

My proposals were for syntax extensions to eliminate the ugliness of procedure-inside-extression construct.

Too which I principially agree (the elimination of the ugliness, not necesserily the syntax proposed by Alexander)

Regards,
Sven

Reply by email

kyan
to Sven Barth
Sven Barth wrote 22 lines
On 26.01.2013 15:52, Alexander Klenin wrote: > > On Sun, Jan 27, 2013 at 12:26 AM, Paul Ishenin > wrote: >> >> 26.01.13, 6:57, Alexander Klenin пишет: >> >> Why to invent a new solution if Delphi already have one: >> >> http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/devcommon/anonymousmethods_xml.html >> > > Of course, the Delphi-compatible implementation is a basis -- > I did not even mention it, because I thought it is obvious. > (Although I would still like to devise some solution for using > closures for events -- > "IDE does not recognize them" seems quite a weak obstacle to me). > I assume this is because anonymous functions are not plain methods. Thus they are not compatible with TMethod (the type behind "procedure/function of object"). They are instead based on a different (internal) type.

Please take a look at this:
http://blog.barrkel.com/2010/01/using-anonymous-methods-in-method.html

Reply by email

Alexander Klenin
to Sven Barth: Just in case: we already have a branch where someone started the development for…
Sven Barth wrote 45 lines
On 25.01.2013 23:57, Alexander Klenin wrote: > You have also proposed lambda-expressions: >> >> map.Iterate(lambda TFPGMapLongInt.TIteratorProc(aKey, aData) as >> Writeln(aKey, ' => ', aData.ClassName)); > > > I think that they are not optimal in the proposed form. > Note that the longest part of the expression is actually a restatement > of the Iterate parameter type. > I propose to allow omitting that: > > map.Iterate(lambda begin > Writeln(aKey, ' => ', aData.ClassName); > end;) > > That is, basically, "lambda" means "use procedure definition from the > parameter type". I have already thought about this as well, but this is simply not how Pascal (and especially the compiler) works. In Pascal the result of an expression is (at first) independantly of the result it is assigned to. For example: double1 := 1.0/2.0; here the expression on the right side will be evaluated with the smallest possible precision that does not cause data loss (let's assume for now that 1.0, 2.0 and 0.5 all can be represented by a Single) and only in the assignment the result is converted to a Double. Also there's the problem of overloaded procedures. Let's assume we have type TProc1 = reference of procedure(aArg: Integer); TProc2 = reference of procedure(aArg: String); procedure SomeProc(aArg: TProc1); procedure SomeProc(aArg: TProc2); SomeProc(lambda as Writeln(aArg)); Which SomeProc to choose? Even if the lamda's code could derive the argument types directly (which must not be the case as you can see in the example, as Writeln can take an Integer as well as a String) you'd still need to find the correct procedure.

Ok, then let's take just one step back:

SomeProc(lambda TProc1 as Writeln(aArg));

This way, but problems are solved -- procedure type is specified independently from the parameter type,
and overload resolution will work.

Also the parameter names MUST be declared somewhere. This is Pascal. There can't be a symbol name appearing suddenly in the middle of the code.

They are declared in the definition of TProc1 type. This is similar to records -- fields are declared in type definition.

These three points are why I proposed my example as is: we need somehow the signature of the lambda. Maybe "lambda(aArg: Integer) as Writeln(aArg)" would be sufficient already (or "lambda(aArg: Integer): String as IntToStr(aArg)" for a function).

This just replaces "procedure" with "lambda", so nothing is gained. The possibility to omit duplicated parameter lists is, in my opinion, quite important
for this whole feature to be useful.

Show quoted text 12 lines
> Independently, I propose a general shorthand for functions containing > a single expression > (and perhaps for procedures containing a single statement, but I am > less certain here): > > function f(a, b: Integer): Integer; > begin Result := a + b; end; > => > function f(a, b: Integer): Integer as a + b; I don't know whether I'd want to extend this to single line functions/procedures. This is where the language becomes less clear again.

While I do not feel too strongly about this, consider these arguments:
1) Orthogonality is one of the important first principles of language design, and of Pascal design in particular -- every feature should work in combination with every other.
So it is bad to allow shortcut syntax in procedures used as parameters, and disallow otherwise.
2) With the introduction of anonymous functions,
function f(a, b: Integer): Integer;
can be rewritten as
f := function (a, b: Integer): Integer;
which strengthens point (1) above.
3) Single-statement procedures/functions are moderately common -- including operator overloads, simple getters/setters, various utilities, nested helper procedures.
Quite a few times, I resented the fact that extracting a single-line nested procedure,
while clearing code at the point of calling, inconveniently increases total line count by five(!) lines,
thus significantly offsetting the readability gains. With shortened syntax, line count will be incremented only by two, which is much better.

Show quoted text 11 lines
> with both shortcuts combined, it becomes possible to write something like > > sum := arr.Reduce(lambda as a + b;); > > Even further, Ruby (or Objective C)-like extension may be introduced to > allow > procedure arguments (or perhaps just the last one) to be separated by > some special character instead of parenthesis, for example: No, no, no. No "special characters". If you want to differentiate something, then use keywords.

There is a limit to "keywords instead of punctuation" rule -- once humanity invented the language where
"a := b + c" was spelled "add a to b giving c".
After trying to use that, the value of *some* punctuation was universally acknowledged :)
But in this case, perhaps you are right, how about (taking into account the discussion of lambda above):

sum := a.Reduce with lambda TAdderFunc as a + b;
or without type helpers:
sum := Reduce with a, lambda TAdderFunc as a + b;
Alexander S. Klenin

Reply by email

Alexander Klenin
to kyan: Please take a look at this: http://blog.barrkel.com/2010/01/using-anonymous-methods-in-met…
kyan wrote 6 lines
> I assume this is because anonymous functions are not plain methods. Thus > they are not compatible with TMethod (the type behind "procedure/function of > object"). They are instead based on a different (internal) type. Please take a look at this: http://blog.barrkel.com/2010/01/using-anonymous-methods-in-method.html

While this article confirms my understainding of them Delphi implementation, it does not offer a solution.
The solution must come at the language level -- for example, make TMethod managed type
and fully unify "procedure", "procedure of object" and "reference to procedure". This will of course incur some performance cost, but since event variables are in 99% of cases
created and destroyed together with components, and components are quite heavy anyway,
I suspect the cost will be negligible, especially with the help of clever enough optimizer.
Further, after that, since all events will become object instances anyway, C#-style event stacking may become feasible, replacing rather cumbersome hand-made chaining or "AddHandler/RemoveHandler" interfaces.

Alexander S. Klenin

Reply by email

Sven Barth
to Alexander Klenin: Ok, then let's take just one step back: SomeProc(lambda TProc1 as Writeln(aArg)); This…
Alexander Klenin wrote 54 lines
On Sat, Jan 26, 2013 at 8:31 PM, Sven Barth wrote: > On 25.01.2013 23:57, Alexander Klenin wrote: >> You have also proposed lambda-expressions: >>> >>> map.Iterate(lambda TFPGMapLongInt.TIteratorProc(aKey, aData) as >>> Writeln(aKey, ' => ', aData.ClassName)); >> >> >> I think that they are not optimal in the proposed form. >> Note that the longest part of the expression is actually a restatement >> of the Iterate parameter type. >> I propose to allow omitting that: >> >> map.Iterate(lambda begin >> Writeln(aKey, ' => ', aData.ClassName); >> end;) >> >> That is, basically, "lambda" means "use procedure definition from the >> parameter type". > > I have already thought about this as well, but this is simply not how Pascal > (and especially the compiler) works. In Pascal the result of an expression > is (at first) independantly of the result it is assigned to. For example: > > double1 := 1.0/2.0; > > here the expression on the right side will be evaluated with the smallest > possible precision that does not cause data loss (let's assume for now that > 1.0, 2.0 and 0.5 all can be represented by a Single) and only in the > assignment the result is converted to a Double. > Also there's the problem of overloaded procedures. Let's assume we have > > type > TProc1 = reference of procedure(aArg: Integer); > TProc2 = reference of procedure(aArg: String); > > procedure SomeProc(aArg: TProc1); > procedure SomeProc(aArg: TProc2); > > SomeProc(lambda as Writeln(aArg)); > > Which SomeProc to choose? Even if the lamda's code could derive the argument > types directly (which must not be the case as you can see in the example, as > Writeln can take an Integer as well as a String) you'd still need to find > the correct procedure. Ok, then let's take just one step back: SomeProc(lambda TProc1 as Writeln(aArg)); This way, but problems are solved -- procedure type is specified independently from the parameter type, and overload resolution will work.

While the compiler would be satisfied by this, this nevertheless leaves the problem that we (as in "the user of the language") don't really see where the aArg comes from...

I can already imagine many questions like the following appearing on the mailing list:

=== quote begin ===

type
   TMyProc = reference to function(aArg: Integer): String;
procedure SomeProc(aArg: Integer);
begin
   SomeProcTakingTMyProc(lambda TMyProc as IntToStr(aArg));
end;

Why doesn't the lambda expression return the value that's passed to SomeProc, but something completely different?!

=== quote end ===

Show quoted text 15 lines
> Also the parameter names MUST be declared somewhere. This is Pascal. There > can't be a symbol name appearing suddenly in the middle of the code. They are declared in the definition of TProc1 type. This is similar to records -- fields are declared in type definition. > These three points are why I proposed my example as is: we need somehow the > signature of the lambda. Maybe "lambda(aArg: Integer) as Writeln(aArg)" > would be sufficient already (or "lambda(aArg: Integer): String as > IntToStr(aArg)" for a function). This just replaces "procedure" with "lambda", so nothing is gained. The possibility to omit duplicated parameter lists is, in my opinion, quite important for this whole feature to be useful.

Yes, I'm aware of that...

Maybe the idea that I presented in the mail in the for-in-index thread:

lambda TProc(aArg) as ...

TProc is used to bind the correct type and the names in the parentheses are mapped to the arguments of TProc.

Show quoted text 35 lines
>> Independently, I propose a general shorthand for functions containing >> a single expression >> (and perhaps for procedures containing a single statement, but I am >> less certain here): >> >> function f(a, b: Integer): Integer; >> begin Result := a + b; end; >> => >> function f(a, b: Integer): Integer as a + b; > > I don't know whether I'd want to extend this to single line > functions/procedures. This is where the language becomes less clear again. While I do not feel too strongly about this, consider these arguments: 1) Orthogonality is one of the important first principles of language design, and of Pascal design in particular -- every feature should work in combination with every other. So it is bad to allow shortcut syntax in procedures used as parameters, and disallow otherwise. 2) With the introduction of anonymous functions, function f(a, b: Integer): Integer; can be rewritten as f := function (a, b: Integer): Integer; which strengthens point (1) above. 3) Single-statement procedures/functions are moderately common -- including operator overloads, simple getters/setters, various utilities, nested helper procedures. Quite a few times, I resented the fact that extracting a single-line nested procedure, while clearing code at the point of calling, inconveniently increases total line count by five(!) lines, thus significantly offsetting the readability gains. With shortened syntax, line count will be incremented only by two, which is much better.

Ok, if you come with orthogonality...

Just one point to ensure we understand this the same: if we use the abbreviated syntax ("as STMT/EXPR") then no variable/type/constant declarations are possible.

What still bothers me a bit is the handling of result values... if we require the code after the AS to be an EXPR we'd need to implicitely assign the result value. What does now happen if the lambda's type does require a return value, but we don't provide one? What the otherway round?

E.g.

=== example begin ===

type
   TProc = reference to procedure;
   TIntFunc = reference to function;
x := lambda TProc as fMyMemo.Lines.Add('FooBar'); // Add returns Integer
y := lambda TIntFunc as Writeln('Foobar');

=== example end ===

This would be solved if we allow the code to be a statement, but would
require the usage of "Result := ".
Show quoted text 17 lines
>> with both shortcuts combined, it becomes possible to write something like >> >> sum := arr.Reduce(lambda as a + b;); >> >> Even further, Ruby (or Objective C)-like extension may be introduced to >> allow >> procedure arguments (or perhaps just the last one) to be separated by >> some special character instead of parenthesis, for example: > > No, no, no. No "special characters". If you want to differentiate something, > then use keywords. There is a limit to "keywords instead of punctuation" rule -- once humanity invented the language where "a := b + c" was spelled "add a to b giving c". After trying to use that, the value of *some* punctuation was universally acknowledged :)

I do value punctuation. But this is Pascal, it's known to be based on keywords and to read more like English language unlike C....

Show quoted text 6 lines
But in this case, perhaps you are right, how about (taking into account the discussion of lambda above): sum := a.Reduce with lambda TAdderFunc as a + b; or without type helpers: sum := Reduce with a, lambda TAdderFunc as a + b;

While I still don't know why you'd want to replace parenthesis I would definitely prefer this syntax over any ":" you can throw at me ^^

Regards,
Sven

Reply by email

Marco van de Voort
to Alexander Klenin: While this article confirms my understainding of them Delphi implementation, it does not…

In our previous episode, Alexander Klenin said:

Show quoted text 11 lines
> > Please take a look at this: > http://blog.barrkel.com/2010/01/using-anonymous-methods-in-method.html While this article confirms my understainding of them Delphi implementation, it does not offer a solution. The solution must come at the language level -- for example, make TMethod managed type and fully unify "procedure", "procedure of object" and "reference to procedure". This will of course incur some performance cost, but since event variables are in 99% of cases

I sincerely hope you are kidding here. Do not slow existing constructs.

Reply by email

Alexander Klenin
to Sven Barth: While the compiler would be satisfied by this, this nevertheless leaves the problem that…
Sven Barth wrote 29 lines
On 26.01.2013 16:34, Alexander Klenin wrote: > Ok, then let's take just one step back: > SomeProc(lambda TProc1 as Writeln(aArg)); > > This way, but problems are solved -- procedure type is specified > independently from the parameter type, > and overload resolution will work. While the compiler would be satisfied by this, this nevertheless leaves the problem that we (as in "the user of the language") don't really see where the aArg comes from... I can already imagine many questions like the following appearing on the mailing list: === quote begin === type TMyProc = reference to function(aArg: Integer): String; procedure SomeProc(aArg: Integer); begin SomeProcTakingTMyProc(lambda TMyProc as IntToStr(aArg)); end; Why doesn't the lambda expression return the value that's passed to SomeProc, but something completely different?! === quote end ===

This is quite similar to using "with" statement -- yes, name clashes are possible.
Perhaps they should be disallowed, like currently it is impossible to declare local variable
overriding class field (except in Delphi mode).
In this case, user will have to revert to full syntax.

Show quoted text 6 lines
Maybe the idea that I presented in the mail in the for-in-index thread: lambda TProc(aArg) as ... TProc is used to bind the correct type and the names in the parentheses are mapped to the arguments of TProc.

Well, this would be yet another step backwards.
The break-even point where the gain is not enough to justify the feature would be quite near.
I'd prefer the previous variant -- with additional safety as described above.

Just one point to ensure we understand this the same: if we use the abbreviated syntax ("as STMT/EXPR") then no variable/type/constant declarations are possible.

Of course, that's the point of the shorthand.

What still bothers me a bit is the handling of result values... if we require the code after the AS to be an EXPR we'd need to implicitely assign the result value. What does now happen if the lambda's type does require a return value, but we don't provide one? What the otherway round?

I suggest the following:
Shortcut for functions *require* expression as body, since the only correct single statement would be assignment to Result (or call to Exit with an argument).
Shortcut for procedures *require* a statement.
Of course, the statement may be a function call, but that does not matter for procedure.

Show quoted text 7 lines
=== example begin === type TProc = reference to procedure; TIntFunc = reference to function; x := lambda TProc as fMyMemo.Lines.Add('FooBar'); // Add returns Integer

ok

y := lambda TIntFunc as Writeln('Foobar');

Syntax error: expression expected

Show quoted text 9 lines
> But in this case, perhaps you are right, how about (taking into > account the discussion of lambda above): > > sum := a.Reduce with lambda TAdderFunc as a + b; > or without type helpers: > sum := Reduce with a, lambda TAdderFunc as a + b; While I still don't know why you'd want to replace parenthesis I would definitely prefer this syntax over any ":" you can throw at me ^^

This is mostly to avoid JavaScript-style mixed closing brackets in nested calls:

a.forEach(function (aelem) {
aelem.forEach(function (v) {

    doSomething(v);
  })

});

with the no-parenthisis extension, the Pascal equivalent may be (suppose the actual code is long, so "as" shortcut does not apply):

a.forEach with lambda TAElemProc begin
  AElem.forEach with lambda TIntegerProc begin
    doSomething(v);
  end;
end;

without the extension, the Pascal equivalent may be:

a.forEach(lambda TAElemProc begin
  AElem.forEach(lambda TIntegerProc begin
    doSomething(v);
  end);
end);
Alexander S. Klenin

Reply by email

Alexander Klenin
to Marco van de Voort: In our previous episode, Alexander Klenin said: I sincerely hope you are kidding here. Do…
Marco van de Voort wrote 14 lines
In our previous episode, Alexander Klenin said: > > > > Please take a look at this: > > http://blog.barrkel.com/2010/01/using-anonymous-methods-in-method.html > > While this article confirms my understainding of them Delphi implementation, > it does not offer a solution. > The solution must come at the language level -- for example, make > TMethod managed type > and fully unify "procedure", "procedure of object" and "reference to procedure". > This will of course incur some performance cost, but since event > variables are in 99% of cases I sincerely hope you are kidding here. Do not slow existing constructs.

... for example, by suddenly converting all string handling to Unicode... :-))

I realize that there are downsides, and quite possibly they outweigh upsides -- although this is not quite certain to me.
Nevertheless, my point was that some solutions exist, and they are worth investigating.

Alexander S. Klenin

Reply by email

Sven Barth
to Alexander Klenin: This is quite similar to using "with" statement -- yes, name clashes are possible.…
Alexander Klenin wrote 37 lines
On Sun, Jan 27, 2013 at 3:10 AM, Sven Barth wrote: > On 26.01.2013 16:34, Alexander Klenin wrote: >> Ok, then let's take just one step back: >> SomeProc(lambda TProc1 as Writeln(aArg)); >> >> This way, but problems are solved -- procedure type is specified >> independently from the parameter type, >> and overload resolution will work. > > While the compiler would be satisfied by this, this nevertheless leaves the > problem that we (as in "the user of the language") don't really see where > the aArg comes from... > > I can already imagine many questions like the following appearing on the > mailing list: > > === quote begin === > > type > TMyProc = reference to function(aArg: Integer): String; > > procedure SomeProc(aArg: Integer); > begin > SomeProcTakingTMyProc(lambda TMyProc as IntToStr(aArg)); > end; > > Why doesn't the lambda expression return the value that's passed to > SomeProc, but something completely different?! > > === quote end === This is quite similar to using "with" statement -- yes, name clashes are possible. Perhaps they should be disallowed, like currently it is impossible to declare local variable overriding class field (except in Delphi mode). In this case, user will have to revert to full syntax.

Could indeed be a solution...

Show quoted text 12 lines
> What still bothers me a bit is the handling of result values... if we > require the code after the AS to be an EXPR we'd need to implicitely assign > the result value. What does now happen if the lambda's type does require a > return value, but we don't provide one? What the otherway round? I suggest the following: Shortcut for functions *require* expression as body, since the only correct single statement would be assignment to Result (or call to Exit with an argument). Shortcut for procedures *require* a statement. Of course, the statement may be a function call, but that does not matter for procedure.

That would work. It would need to be clearly documented though :)

Show quoted text 34 lines
>> But in this case, perhaps you are right, how about (taking into >> account the discussion of lambda above): >> >> sum := a.Reduce with lambda TAdderFunc as a + b; >> or without type helpers: >> sum := Reduce with a, lambda TAdderFunc as a + b; > While I still don't know why you'd want to replace parenthesis I would > definitely prefer this syntax over any ":" you can throw at me ^^ This is mostly to avoid JavaScript-style mixed closing brackets in nested calls: a.forEach(function (aelem) { aelem.forEach(function (v) { doSomething(v); }) }); with the no-parenthisis extension, the Pascal equivalent may be (suppose the actual code is long, so "as" shortcut does not apply): a.forEach with lambda TAElemProc begin AElem.forEach with lambda TIntegerProc begin doSomething(v); end; end; without the extension, the Pascal equivalent may be: a.forEach(lambda TAElemProc begin AElem.forEach(lambda TIntegerProc begin doSomething(v); end); end);

Hmm... you know what: i don't care. :) As long as you don't use "special characters" I'd make no problems here (others might though...)

Regards,
Sven

Reply by email