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.
TFilterMethod = constrained reference to function (const Item:T):Boolean;
TInstanceList = class
property function Filtered(Filter: TFilterMethod):TInstanceList;
for Element in List.Filtered(function (Item: TThing)
Result := Item.Qualifier in [This, That]);
// should not compile – but error on “reference outside method not allowed for constrained methods
for Element in List.Filtered(function (Item: TThing)
Result := (Item.Qualifier = SomeRef);
30 thoughts on “Anonymous Method constraints”
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?)
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)
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?
If you don’t want anonymous methods to capture variables, don’t refer to variables outside the anonymous method’s scope.
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.
“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)
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.
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.
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).
Wow, some pretty far out views of capture being expressed in this thread!!
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.
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)
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.
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.
Fixed the code on rosettacode 🙂
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.
Stefan Glienke – In principle, I agree. Perhaps I just need a huge hint about the use of references outside the anon method?
+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.
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.
+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.
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.
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.
+Lars Hints and warnings don’t involve language changes. Static analysis, external to the language, is how to deal with such issues.
Stefan Glienke , Lars Fosdal “We desperately need (better) static code analysis tools.” So, hi, Roman Yankovsky . I’m sure you’re watching this thread 😉
Lars Fosdal I really don’t understand why the Filtered function would care that SomeRef is being captured. What’s wrong with that?
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.
Lars Fosdal Yeah ok. I could understand why the caller would consider it bad, but not Filtered.
For what it’s worth, in C++ 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.
Asbjørn Heid Thanks for that info – I feel less bonkers now 😉
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!
Lars Fosdal Don’t be hasty! 😉
You must log in to post a comment.