Florian Klämpfl wrote > > >>> This is the prototypical way to run a function over each element in a >>> collection, returning the results. >> (map (lambda (x) (+ x 1)) '(1 2 3)) >> -> (2 3 4) > > I still don't see why this cannot be done by procedure variables: one > can easily pass a procedure reference to a compare function to any sort > library call. >
Sorry for resurrecting al thread, but p[erhaps better to keep things together. 1) "anonymous methods" in Delphi are made in very Pascalish way, a lot of wording, a lot of boilerplate. That adds to readability, but makes their use much harder. If you have relatively complex 4-5 lines function, then the overhead is slightly noticeable. But such functions are a borderline to me, having them anonymous hampers both ease of read and ease of debug. I know C++ fans how say that begin/end are awfully long. Well, Delphi style of anonymous functions would frighten them even more. Here we are at dilemma. Pascal was devised in 1949 to look like Classic monumental style building, or at least some "manufacturing plant" style building made of bricks industrial way. Functional style is like more like elegant houses made of paper and thin plywood, something mayeb of Gothic or Japanese style, easily constructible (composable) to any form you like. They really are hard to make together. You can read about Nemerle and Scala though, about the attempts. For example in C++ lambdas look total strangers by syntax, but that is inevitable. When you have to type 20 chars of boilerplate for 3 char function "x+y" - that is too much. http://www.cprogramming.com/c++11/c++11-lambda-closures.html However, other than that i like syntax for C++ lambda's. It is clearly distinct for compiler, it separates and thus clearly defines captured values, it is rather laconic. 2) however to treat "x+y" as a proper function definition one should have 2.1) either typeless (at compile type, in runtime that may be dynamical typing, duck typing, whatever) language like LISP or Erlang or Python even generic function 2.2) or strongly typed language with very sophisticated type infering like Nemerle and Scala "x + y" is a function, but the compiler should look at the context and make guess what would be the types for x, y and result (latter would usually be least common type for x and y); So compiler looks at the context and turns - where appropriate - "x + y" into a generic "function<TX, TY, TRes> (const x:TX; const y:TY): TRes; begin Result := x+y; end; " Quite a lot to generate of three symbols. Well, there are even more concise syntaxes, using jokers or omissions. For example in collection.filter operations the Predicate is expected (it is called collection.Where in Spring4Delphi and MS.Net). So then "_ > 2" or "> 2" would be correct function, implicated to a generic "function<SomeContextDependantNumericType> function( const value: SCDNT); begin Result := value>2; end; It would also be nice for compiler to make some merges. Surely all that cherries can not be gathered without heavy language and compiler reworking. Delphi tried to get at least some of those, but 1949 legacy feels there. The question is about possible compromise. (**** Side note: type inferring is also very useful in Generics. ***) Some very tiny part of it is even implemented in Delphi :-) Consider { Chu Jetcheng, 2008-07-24 } http://wiki.freepascal.org/Generics_proposals var Obj1: TGeneric of Integer, Real; begin Obj1 := TGeneric.Create(32, 3.14); This is quite un-orthodox proposal At the statement, a compiler know the type of Obj1, but does not know the type of TGeneric<X,Y>. Since usually rvalue is parsed with no look at lvalue, such inferring is hardly possible at all. More traditional way would be like var Obj1; begin Obj1 := TGeneric<String, Integer>.Create(32, 3.14); Where compiler "infers" type of Obj1 looking at the type of value used for assignment. It is even better if type would be something like TEvent<String, Record<Field1, Filed2, Array<Items>>>. You may say - make an alias. Type MyEventHere = TEvent<String, Record<Field1, Filed2, Array<Items>>>. Sometimes that works, for larger code blocks. But if you have to declare type for every procedure in your unit, that becomes a mess... However that is well in the spirit of Wirth. In 1949 AFAIR you could not have var x: array[0..10] of integer; You should have pre-defined named alias for that array. And "open arrays" functions parameters of Turbo Pascal 7 would be just a schism and herecy for original Pascal specs. But we now even have and enjoy dynamic arrays and generalyl do not consider them large enough to have their own dedicated explicitly declared name. (**** End of Side note. ***) 3) Why that matters ? You can refactor program with more ease. Imagine you just want to save some table to... XML file. Or write it to string. Or maybe pass it to some processor, that would check its sanity. What is the table ? Maybe it is dynamic "array of record ..... end;" ? Or maybe it is TDataSet ? Or maybe it is TStringList.Rows ? Or any of that and who knows what else ? // that is not that spectacular. TDataSet internals are scarcely documented spaghetti. When soem in-memory dataset no more compiles in newer Delphi you'd want to change it to dynamic array or TList, but that would ask to re-write all the units using it! You would end up using TClientDataSet and all its TField objects just to pass dynamic array to make a single iteration over it and dispose. Okay, in the world of many-GHz many-core CPUs that does not matter. But "while not q.eof do begin ... q.next; end;" loop is a bit fragile and redundant in wording. // Okay, you also want to filter it. Your user wants to see customers with most sales. So he enters a number and only wants to see those whose sales are above. AFAIR TDataSet had some filtering event (or was it TBDEDataSet), array does not and should be copied. The logic is very different for same purpose again, depending on source data type style. But what if there then be one more filtering wished ? Or what if consumer program needs to apply its internal filtering as well ? In hypothetical 100% inferring functional style it could look like procedure Button1OnClick(...); var Consumer; Data; begin Data := CurrentConsumersCollection; // array, file object, TDataSet - whatever Consumer := ConsumerArray[RadioGroup1.ItemIndex]; // Save to XML, save to network, show on screen - whatever if CheckBox1.checked then Data := Data.Filter( _.total > StrToInt(editSalesThreshhold.Text ); if CheckBox2.checked then Data := Data.Filter( _.CityCode = StrToInt(editCityCode.Text ); ..... Data.Map(Consumer.StepMethod); (* or Consumer.Process(Data) *) end; This function could potentially work with ANY kind of input data containers. 4) And here we come to "capturing" - why those so called "anonymous methods" are better called "closures", why that C++ \starts lambdas not with "()" par but with two pairs "[] ()". What is the argument type of " Data := Data.Filter( _.CityCode = StrToInt(editCityCode.Text ); " ? Obviously it only can be a predicate: function<our-data-record-type> (const value:ODRT): boolean; Otherwise collection should have some fore-knowledge how it would be used later. Alternatively, it can also have "pointer" second argument, sending all type checking to sewer. Just how non-Generic TList is practically done. Brr... Ahem, but how would it get city code to compare ? We cannot compile 1000 functions with all the reference values of CityCode. So the function should "capture" value of "StrToInt(editCityCode.Text" from calling place. In OOP language terms that means we create some object where "StrToInt(editCityCode.Text" is some private field. It is exactly how that is implemented in Delphi - via TInterfacedObject with refcounting. Compilers sees that the auto-generated function should have StrToInt(editCityCode.Text ) as a parameter from external context. It also knows it is not allowed to have due to argument type of Collection.Filter method being single-argument predicate. So it behind the hood creates that object with extra parameter like private field. Other possible approaches ? Well, that FPC wiki page mentions Extended Pascal / GNU Pascal "scheme types", tapes tagged with constant values. That maybe could be extended to be var-tagged. Then there can be hypothetical "function(code:integer) CompareCityCode(const r: record): boolean" type. If it can somehow be considered derived (okay, assignment-compatible) from mere "function CompareCityCode(...):..." then that would be the same idea mostly. Most hardcore feature would probably be "partial application", i mentioned Scala but even it has it with limitations. Imagine we have function CompareCityCode(const value: our-data-record; const referenceCode: integer): boolean; begin Result := value.CityCode = referenceCode; end; What would be result of "CompareCityCode(?, 10)" ? It could be a predicate, it could be new boolean function, having single argument "value" and always comparing with 10. In VM-based languages like Lisp or Java/.Net that can indeed be implemented as function generator. In native languages that probably can only be implemented as a hidden object with "10" stored to that private field again. Why bother ? Well, just imagining how much code and how many functions and names would have be written to implement that spectacular Button1Click with its adapting to different datasources, different consumers, different lists of filters on different conditions, etc. And then one future day this code - now split to many many dependent procedures and types - have to be reworked. 5) It is hard to implement all that laconic fluid features in "heavy industrial" style language of 1949. Probably would never happen to Pascal. But some parts, some ideas probably can be implemented still. For example in Delphi one can not do generic function "x+y" except is both "x" and "y" are classes. But if they are integers or floats - then compiler cannot make a difference, less so infer the types. Well, x+y is maybe nonsense, but what about Max(x,y), if it could be implemented generically for any type in single place... Well, again i am passing from lambdas to generics - but their practical use is entangled heavily... Surely 1949-styled Pascal can not make all that features 1st citizens. The question is how much and in what style of compromise can be implemented. Generics were also heresy for original Pascal. But they are implemented, some in Delphi and to some seemingly less extent in FPC as well. While i pity divergence of syntaxes between Delphi and FPC generics, i strangle would not pity if FPc finally implement lambdas/closures/anonymous-methods in non-Delphi style. Since Delphi style is so Pascalish, that it heavily limits their practical use. -- View this message in context: http://free-pascal-general.1045716.n5.nabble.com/Delphi-s-anonymous-functions-in-Free-Pascal-tp4911527p5711032.html Sent from the Free Pascal - General mailing list archive at Nabble.com. _______________________________________________ fpc-pascal maillist - fpc-pascal@lists.freepascal.org http://lists.freepascal.org/mailman/listinfo/fpc-pascal