Anonymous Method constraints

Anonymous Method constraints
Due to the occasional shooting of foot due to how capture works, I wish there was a way of optionally constraining references for an anonymous method, so that I could enforce local references only.

type
  TFilterMethod = constrained reference to function (const Item:T):Boolean;

  TInstanceList = class
    property function Filtered(Filter: TFilterMethod):TInstanceList;
  end;

var
  SomeRef: TQualifier;
  List: TInstanceList;
  Element: TThing;
begin
  ...
  // compiles
  for Element in List.Filtered(function (Item: TThing)
    begin
      Result := Item.Qualifier in [This, That]);
    end)
  do begin
    ...

  // should not compile - but error on "reference outside method not allowed for constrained methods
  for Element in List.Filtered(function (Item: TThing)
    begin 
      Result := (Item.Qualifier = SomeRef);
    end)
  do begin
    ...

Comments

  1. Could you explain further please? Why would you want the second example to not compile - isn't a reference it an item in a list perfectly valid? (Or is it something to do with the property Filtered being a property not function?  I've never seen property syntax like that before - it's not an indexed property, it has normal braces like a method?)

    ReplyDelete
  2. If you don't like variable capturing then stick to plain old function pointers (sure you cannot write the code inline but have to pre-define the routine)

    ReplyDelete
  3. I think he means that the second example should fail because the anonymous method captures are variable (SomeRef). The first example dont need any variable capturing...
    But isnt that an advantage of anonymous method? Capturing variables from outside?

    ReplyDelete
  4. If you don't want anonymous methods to capture variables, don't refer to variables outside the anonymous method's scope.

    ReplyDelete
  5. David Millington My bad: Should be function, not property.

    Stefan Glienke - I could do that, but it is a lot less flexible, and it can take miniscule pieces of logic of it's context.

    David Heffernan - Duh, Captain Obvious! That advice has a Microsoft help quality about it: Accurate, but useless.

    Fabian S. Biehn - Capturing is an advantage - but it can also be a pitfall.  In some cases - you capture only the initial value of something that should have changed later.

    It would be nice to be able to constrain the abstract method definition by design.

    ReplyDelete
  6. "In some cases - you capture only the initial value of something that should have changed later."

    Actually it is the opposite - variable capturing means you always get the most recent value of this variable. Capturing means by reference and not by value. If you don't want that then write a function that returns the anonymous method or use the specification pattern (see Spring.Designpatterns.pas)

    ReplyDelete
  7. Lars Fosdal I don't think the advice is useless. I think it's important to know your tools and understand the code that you write. Certainly when I write anonymous methods, I think very carefully about what, if anything, is being captured. Do you not?

    It seems to me that you are responding to being caught out by your own incomplete understanding of how variable capture works and think that a change to the language is the fix.

    Furthermore, your proposed fix would be the absolute worst way to tackle this. You'd condemn any users of that type to be unable to use variable capture. If a change were to be made, and I see no reason to make one, but if one were to be made you would make it at the point at which the anonymous method was defined. So you'd have the programmer state in the definition of the anonymous method that the method did not capture any variables. And then the compiler could verify that.

    ReplyDelete
  8. Stefan Glienke "Actually it is the opposite - variable capturing means you always get the most recent value of this variable."

    Capturing a mutable variable by reference is a hell... I'd rather wish to always capture by value, not by reference.

    ReplyDelete
  9. Roman Yankovsky "I'd rather wish to always capture by value, not by reference."

    That would actually cripple this feature. If you want capture value then make a local variable, assign the mutable variable to it and you are done (as you have to do with certain types like records).

    ReplyDelete
  10. Wow, some pretty far out views of capture being expressed in this thread!!

    ReplyDelete
  11. Stefan Glienke this feature came from functional languages where all variables are immutable by default. Capturing by value cannot cripple this feature :)

    Disclaimer: I know, that some times is accessible only by a reference. That's why Emba didn't have a choice.

    ReplyDelete
  12. I guess the person that wrote this code also did not understand variable capturing: http://rosettacode.org/wiki/Closures/Value_capture#Delphi facepalm (Tip: look at the comment in this code: http://rosettacode.org/wiki/Closures/Value_capture#Using_delegates_only)

    ReplyDelete
  13. Stefan Glienke Slightly tricky to reproduce the C# code (the second example) in Delphi because Delphi can't readily create local variables in the same way that C family languages do by declaring them inside a loop body.

    ReplyDelete
  14. David Heffernan - It is not a "fix", it is a constraint - such as setting a field to be strict private.  It gives me, as the class designer, the option to constrain the use and enforce a design.

    If you design a class that uses anon methods, it is all well and fine that you do the required thinking, but you can't enforce this for those that will be using your designs.

    I really would like to have more granular control of the capture requirements - i.e. to, in the procedure in which the anon method is declared, discern the constraint between "simple" local variable captures and referenced object/property value or reference captures - but I can't think of an easy way to introduce the constraint.  

    If it needs to be done where you implement the anon method - as you suggest - then the type declaration needs a way to say that the compiler must require a capture constraint declaration at the implementation.

    ReplyDelete
  15. Lars Fosdal You are looking at it from a wrong perspective. A delegate type is an interface (even literally) or contract. If you are using variable capturing or not is an implementation detail of that anonymous method and should not be constrained by the interface.

    ReplyDelete
  16. Stefan Glienke - In principle, I agree.  Perhaps I just need a huge hint about the use of references outside the anon method?

    ReplyDelete
  17. +Lars What Stefan just said is my point about where the constraint would be specified. The consumer of an anon method has no grounds to care about its implementation. Which is why you'll never see the feature you asked for.

    What you actually looking for is a tool to help you find your mistakes.

    ReplyDelete
  18. David Heffernan What I would like is a means to help those that use the code of others, avoid making such mistakes.  

    I've made the mistakes, and learned from them - but if you use code with Generics, without the experience - you won't know the pitfalls until you spend time digging them out in the debugger.

    ReplyDelete
  19. +Lars Changing the language is quite drastic. To help novices avoid novice mistakes? Not enough justification. You'd be adding clutter for an ephemeral gain. That's a net loss.

    ReplyDelete
  20. Lars Fosdal We desperately need (better) static code analysis tools. It could tell you in a blink of an eye where you are referring something that you don't want.

    ReplyDelete
  21. Hints and warnings serve a similar purpose.
    Not initializing a variable is a typical novice mistake, right?  
    Changing the language is indeed drastic, and it may add clutter - but the challenge is not ephemeral.

    ReplyDelete
  22. +Lars Hints and warnings don't involve language changes. Static analysis, external to the language, is how to deal with such issues.

    ReplyDelete
  23. Stefan Glienke , Lars Fosdal "We desperately need (better) static code analysis tools."  So, hi, Roman Yankovsky .  I'm sure you're watching this thread ;)

    ReplyDelete
  24. Lars Fosdal I really don't understand why the Filtered function would care that SomeRef is being captured. What's wrong with that?

    ReplyDelete
  25. Asbjørn Heid It's perhaps not the best of examples. The original thought was to prevent "bad captures" (i.e. value instead of value reference).  Stefan Glienke / David Heffernan had compelling arguments for why my suggestion was the wrong solution.

    The concept can have potential as a way to ensure that the anon method has no side effects - but, yeah - not for the original thought.

    ReplyDelete
  26. Lars Fosdal Yeah ok. I could understand why the caller would consider it bad, but not Filtered.

    For what it's worth, in C++[1] you can specify exactly which and how variables are captured. Of course only when you declare the lambda, whatever is using the lambda can't, and shouldn't care.

    The syntax is a bit fugly, but at least you get absolute control. Want to make sure no variables are capture? No problem. Want to auto-capture variables by value, except "foo" by reference? Sure thing.

    [1]: http://en.cppreference.com/w/cpp/language/lambda

    ReplyDelete
  27. Asbjørn Heid Thanks for that info - I feel less bonkers now ;)

    ReplyDelete
  28. As is so often the case with C++, the flexibility is both powerful and confusing! Pity those poor people who have to write the compilers!

    ReplyDelete

Post a Comment