Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delphi anonymous methods - pro and cons. Good practices when using closures(anonymus methods) in Delphi

I have a colleague in my team which is extensively using closures in our projects developed in Delphi. Personal, I don't like this because is making code harder to read and I believe that closures should be used ONLY when you need them.

In the other hand I've read Can someone explain Anonymous methods to me? and other links related to this, and I'm taking into account that maybe I'm wrong, so I'm asking you to give me some examples when is better to use closures instead of a 'old-fashion' approach (not using closures).

like image 889
RBA Avatar asked Oct 19 '11 08:10

RBA


1 Answers

I believe that this question calls for a very subjective judgement. I am an old-school delphi developer, and inclined to agree with you. Not only do closures add certain risks (as David H points out in comments) they also reduce readability for all classically trained Delphi developers. So why were they added to the language at all? In Delphi XE, the syntax-formatting function and closures weren't working well together, for example, and this increased my mistrust of closures; How much stuff gets added to the Delphi compiler, that the IDE hasn't been fully upgraded to support? You know you're a cranky old timer when you admit publically that you would have been happy if the Delphi language was frozen at the Delphi 7 level and never improved again. But Delphi is a living, powerful, evolving syntax. And that's a good thing. Repeat that to yourself when you find the old-crank taking over. Give it a try.

I can think of at least ten places where Anonymous methods really make sense and thus, reasons why you should use them, notwithstanding my earlier comment that I mistrust them. I will only point out the two that I have decided to personally use, and the limits that I place on myself when I use them:

  1. Sort methods in container classes in the Generics.Collections accept an anonymous method so that you can easily provide a sorting bit of logic without having to write a regular (non-anonymous) function that matches the same signature that the sort method expects. The new generics syntax fits hand in hand with this style, and though it looks alien to you at first, it grows on you and becomes if not exactly really nice to use, at least more convenient than the alternatives.

  2. TThread methods like Synchronize are overloaded, and in addition to supporting a single TThreadMethod as a parameter Thread.Synchronize(aClassMethodWithoutParameters), it has always been a source of pain to me, to get the parameters into that synchronize method. now you can use a closure (anonymous method), and pass the parameters in.

Limits that I recommend in writing anonymous methods:

A. I have a personal rule of thumb of only ONE closure per function, and whenever there is more than one, refactor out that bit of code to its own method. This keeps the cyclomatic complexity of your "methods" from going insane.

B. Also, inside each closure, I prefer to have only a single method invocation, and its parameters, and if I end up writing giant blocks of code, I rewrite those to be methods. Closures are for variable capture, not a carte-blanche for writing endlessly-twisted spaghetti code.

Sample sort:

 var     
   aContainer:TList<TPair<String, Integer>>;  
 begin   
  aContainer.Sort(    
    TMyComparer.Construct(
      function (const L, R: TPair<String, Integer>): integer
      begin
        result := SysUtils.CompareStr(L.Key,R.Key);
      end ) {Construct end}   );  {aContainer.Sort end}  
 end;

Update: one comment points to "language uglification", I believe that the uglification refers to the difference between having to write:

  x.Sort(     
    TMyComparer.Construct(
      function (const L, R: TPair<String, Integer>): integer
      begin
        result := SysUtils.CompareStr(L.Key,R.Key);
      end )   ); 

Instead of, the following hypothetical duck-typed (or should I have said inferred types) syntax that I just invented here for comparison:

  x.Sort( lambda( [L,R], [ SysUtils.CompareStr(L.Key,R.Key) ] ) )

Some other languages like Smalltalk, and Python can write lambdas more compactly because they are dynamically typed. The need for an IComparer, for example, as the type passed to a Sort() method in a container, is an example of complexity caused by the interface-flavor that strongly typed languages with generics have to follow in order to implement traits like ordering, required for sortability. I don't think there was a nice way to do this. Personally I hate seeing procedure, begin and end keywords inside a function invocation parenthesis, but I don't see what else could reasonably have been done.

like image 86
Warren P Avatar answered Sep 28 '22 01:09

Warren P