A FunctionalProgramming show-down, which originated in HowCanSomethingBeSuperGreatWithoutProducingExternalEvidence: [FixMe: Some code samples victim of TabMunging] * Quote: "If you know what those things are, then you don't need examples, they are '''clearly better''', lead to less code, more abstraction, more reuse. If you don't know what they are, it's your job to go learn them, follow the links, there's a wiki page about each one, no need to repeat their benefits here. But here's quick sample in javascript just to prove it." [Emph. added] Removing items from an array the procedural way, you can't modify a list while iterating it. [Is this really ''procedural''? Looks like there are objects in here.] var aList = getStuffFromWhereEver(); var toRemove = new Array(); for(var index=0;index0;index--) for(var index2=0;index2 delete endif endscan pack It is about the same number of tokens as your "improved" version. Thus, your "lead to less code" claim has been proven false, and I am confident the other claims will also (if you define them better). I don't claim FoxPro is the ideal language, but when it comes to "structure chomping", it can often take very little code. It is sometimes mistaken for pseudo-code. Under some conditions, the above can even be shortened to: use myTable delete for We don't need the "pack" command because if the config is set right, other commands automatically "respect" the built-in record deletion flag. However, eventually "pack" may be needed to clean up the space taken by deleted records. For temporary tables, such is usually not a concern, for the entire table eventually goes away (some dialects do it automatically if a table is declared temporary). Note that if I want to make my example handle any table, any function, and any condition, I could do this: procedure foo parameters tableName, condition, processFunc use '''&tableName''' scan do '''&processFunc''' if '''&condition''' delete endif endscan use return The "&" does run-time code substitution. In FoxPro, the called routine "inherits" the parent's scope, sort of closure-like behavior in a rough sense. (Ideally it would be nice if one could optionally switch this behavior off when not being used.) Pascal has a "static" version of this using "nested functions". * The above could be simplified if "processFunc" also is responsible for evaluating the condition. One could pass an entire function call, parameters and all. At this point, I see advantage in the separation between the condition and function. One of the advantages of using string substitution (eval()-like) for "higher order functions" is that the function name can be stored outside the program, such as in a database. String substitution is part of what makes languages such as TCL great "glue" languages. Function names and all kinds of "meta stuff" can be passed around between different programs in different languages (and databases). ''If you understood how closures maintain their environment, and how that is taken advantage of by using recursion, then you'd understand why eval() isn't a substitution for a closure and why HigherOrderFunctions can't simply be replaced with an eval call to a function. Your statement that "inherits the parent's scope is sort of closure like" just shows you don't understand a closure.'' It solves the problem at hand. If you are talking about other problems or mass generacy, then that is another sub-topic. ---- This entire digression about tables and FoxPro is moot. It misses the point badly. The idea here was to demonstrate that a simple task, like deletion from a list/table/structure/doohicky could be made smaller and easier to understand using HigherOrderFunctions. A showcase of their obvious strengths (and a case for why every modern language should include them). The FoxPro version of table deletion might have a slightly shorter definition, terrific. Yay for FoxPro. The issue at hand is that HOF constructs shorten code drastically and allow for reuse. Even without closures, they're powerful. For example, most structures in Ruby support a "collect" method. Collect takes a structure and returns another structure with all the entries that match a given criterion. The definition for such a method is simple, here is the trivial Array example: class Array def example_collect( &proc ) temp = [] self.each do |x| if proc.call( x ) temp << x end end temp end end This example is slightly longer, symbolwise, than your FoxPro definition, but it is a great proof-of-concept of how HOFs make powerful and generic code. We can use this (trivially longer) definition a million times over, for many different uses. For instance, we could define our array rejection in terms of collect and another HOF: class Array def example_reject( &proc ) self.example_collect { |item| !( proc.call( item ) ) } end end The magic of HOFs makes this possible. This is why we are ranting and raving about them. It's why we claim they're so obviously good as to be almost a no-brainer for modern language design. When we invoke them, we get such a huge savings in code (and brainspace, it's way easier to think of structure rejection atomically) that the feature speaks for itself. Nor are these concepts limited to simple structure control. New conditionals can be made. New looping constructs are trivial. Note that we used self.each in the example_collect code. It's iteration across an arbitrary structure, and to us it's free. It's easy to imagine making something more complex, like a framework for cryptographically secure network communications, which uses HigherOrderFunctions as a way to place your own protocol into the secure framework. Does this make the original statements and code clearer? ''I think we are going to need a specific UseCase to settle this. It is not hard to make something like this with eval:'' newStructure = filteredCopy(oldStructure, filterCriteria) Why do we need such a use case? HOFs are ''similar'' to eval in the sense that code gets conceptually moved around, but instead of moving strings you're moving actual functional units (which can entail a performance savings). HOFs are just a little cleaner (you don't need to worry as much about scope issues, either losing scope or gaining scope). In Lisp they play nicely together, and are used for different purposes. They really aren't in competition in most languages. Why exactly do we need to have some kind of showdown to compare them, since they're really meant for different things? Re: "HOFs are just a little cleaner". Note the word "little". This topic was motivated by claims such as "clearly better" and "great, not just good". If we are splitting hairs, then my original assertion that the bragging is way beyond the level of benefits is true. String-based meta-ing is simple because the language does not have to have HOF built-in, and string substitution can be used for ANYTHING, not just function substitution. Thus, it is more generic and wider reaching. It is a simpler concept that has more uses. Dedicated HOF may simplify a few things, but I find the need for them is not common enough to justify dedicated language features, especially when one uses tables instead of arrays. These array examples here are just reinventing queries from scratch. Rather than brag about making it easier to reinvent the wheel, reuse the goddam wheel instead. GreencoddsTenthRuleOfProgramming. -- top ''Well, it's really hard to say eval is "simpler" than anonymous functions, since anonymous functions will follow your language's scoping rules (which are very similar across most languages) and eval scoping rules vary based on implementation (Lisp loses context, you say FoxPro keeps context, Ruby lets you keep OR discard context). Text-based substitution has a spotty history and is actually quite a bit more difficult, since you can accidently create malformed programs (as in syntactically invalid). In a language with real HigherOrderFunctions, you don't have to worry about that.'' Sounds like this is degenerating into another compiler-versus-interpreter type-checking battle. No need to repeat these kind of arguments here. I don't need Eval-like stuff often enough to worry about those so far. The boundary of abstraction difference often does not fall on the function-level anyhow in practice. There are often orthogonal factors such that a simple list of functions is an insufficient characteristic of the repetition pattern that is an abstraction factoring candidate. See EmployeeTypes. ''Further, HigherOrderFunctions can be combined more safely. You saw my example with the Ruby reject. Doing that in eval would be trickier. Ruby has both a powerful Eval and simple HOFs, and people use HOFs for that sort of thing every time because it's less confusing and safer. Going even '''further''', higher-order functions allow you to compose logic at runtime. This is safer and easier than trying to nest code lexically and then calling eval. Eval is a kind of last-resort sort of thing in most languages, it is usually slow, inefficient, dangerous because of inadvertent text substitutions, and conceptually harder to deal with. HigherOrderFunctions offer nearly all the benefits (and then some) with none of those penalties.'' ''Hence, we say they're superior, obviously good, and highly desirable.'' Throw recursion into the mix, and HigherOrderFunctions and AnonymousFunction/Closures are clearly enormously superior, because they carry their execution environment with them, something string based substitution can't do at all. Evaling a string is really no match for executing a HigherOrderFunctions and passing it a closure. ''Again, I would like to see specific UseCases of "enormously superior", not just BrochureTalk.'' This isn't brochure talk. This is the real deal, Top. I gave you Ruby code showing a nice piece of code that used an previously defined, HOF-dependent function to do something different. Key to realizing its superiority to your own examples, notice that the original example_select was written without example_reject in mind. To properly make those in an eval scheme, you would need to adopt a specific style (in nearly every eval case I know of) and meticulously avoid name conflicts. It would be difficult. ''I am not fully sure what your Ruby code is doing, but can't one get a "reject" by simply putting a "NOT" before an "accept" criteria?'' Also, consider the extended uses I suggested. New conditionals and looping constructs. This isn't brochure talk either, a simple look at the Ruby standard library will give you a wealth of examples. The Lisp standard libraries are more difficult to read (for most people), but they're rife with it. ''TCL can make new block constructs using string substitution.'' Think about how you would make an extensible cryptographically secure networking service (two components, client and server) that merely create a secure corridor for another protocol. You could use anonymous functions (or named functions called by anonymous functions) to process the data according to an arbitrary protocol without ever even ''thinking'' about that protocol, or coding in a specific scope-aware style. Oh, and make it fast enough to be used for real-time, low-latency communications. ''I am not a systems programmer, and thus don't relate to network protocol examples very well. May I ask for custom business application examples? Thank You. Perhaps the characteristics of our domains are different enough to warrant different approaches. Network protocols and interfaces are less subject to the crazy whims of PointyHairedBosses, for example.'' The performance, syntax, and manipulation benefits of HOFs are obvious if you consider them, and we've given you several examples complete with reasoning. Past this point, you're on your own. We can't '''make''' you realize anything, Top. We can only give you the knowledge to make such realization possible. ''Your reasoning is often circular and your examples assume change needs which may be artificial. I cannot tell if they are artificial without looking at specific situations.'' If you're so dead set on a UseCase, please name a case in which your table-oriented-eval-coupled example is superior, because thus far you have not (your High Level language example vs. C-like example pseudocode isn't really valid or compelling). I'm pretty sure the HOF version of any such case will be easier, faster, and cleaner. There are VERY few situations in which the use of Eval is necessary, outside of runtime code generation (which is what eval is for, specifically, so it's not really fair for HOFs to have to meet on that ground). ''I never claimed they were objectively "superior". And, TOP is not the topic. Further, like I already stated, I don't use Eval that often in practice for reasons already given. It is more useful for debunking toy dispatching examples than it is for practical stuff. I already listed some toy examples I would like to see OO and FP people show up.'' [Give up dude... top's not listening at all.] Try something new, such as Realistic UseCases outside of SystemsSoftware . I'm listening, but you are only making "trust me" claims. Do show-me, not trust-me. [Those are realistic to any real programmer.] Always argument-by-intimidation instead of argument-by-use-case. Here is a quote from one of my webpages that describes you guys: * ''It comes across as, "Look how easy this tool allows you to fit your entire knee into your ear!" It indeed does a good job of allowing me to put my knee in my ear. But... um... I don't need to do that very often.'' Why not some use-cases outside of systems programming? What the hell are you '''afraid of'''? Are you some kind of realism-coward who wants to hide in a corner and perform MentalMasturbation all day? ''Who's mentally masturbating? Surely not HOF-users. You probably aren't used to using them because the languages you work with don't provide them, but lots of people use those patterns. How about sorting? I sort things all the live-long-freakin-day. I sort things all the time, because sorting this is a common problem (and a common way to reduce other problems). Instead of having to write a sort for each data type and set, I can write ONE sort that uses a HOF predicate (at least, in a dynamically typed language or a language with C++-style templates). This is exactly what many standard libraries do.'' ''Please don't pretend that HOFs are some kind of eggheaded conehead feature. They're not. They're everywhere, if you have the wherewithal to see them. '' [Wait wait wait, I can just see it now.... top's reply... "why not just say 'order by fieldName', what do I need to sort arrays for". Never mind the massive overhead of a db call, or the fact that all the data is already in memory, let's make the db do everything, shit, let's just throw out programming all together. LOL, Bwahahahahahaha.] Noooow you're gettin' it. You are solving problems that often I don't have because I avoid fricken arrays and use a real state and attribute management paradigm instead of 1950's Fortran leftovers known as Arrays. Higher abstractions, such as CollectionOrientedProgramming, often require more horsepower. Databases can use RAM cache too, BTW. Not every RDBMS has to be bloated and formal like Oracle. ''Err, people still need to solve the problem, Top. Just because '''you''' get to play hooky and get sorting as a free keyword doesn't mean sorting class problems don't exist anymore. We're not ignoring existing technology, we just can't leverage a database in most of our coding. '' I agree that one has to use more primitive techniques to build higher-power tools. [''You don't agree with me. That's not what I said. Twisting other people's words is a child's game, Top. If you'd like to be treated a such, instead of as a peer, then please continue.''] {I swear to God, Allah, and Dr. Codd that I have no intention of "twisting words". If there was a misunderstanding, it was purely accidental. Why am I always guilty-until-proven-innocent?} * Let me clarify: your techniques may indeed be good for building the base SystemsSoftware tool (languages, RDBMS engines, etc.). I don't dispute that. But, that is not a selling point to a custom application programmer who is not building a database engine from scratch. I don't dispute the power of your techniques for '''certain''' domains. Assembly and C also have places where they shine for hardware-friendly reasons, but a custom app developer may not be as affective under the hardware-friendly tradeoffs. If HOF's make it easier and cheaper to HAVE high-level tools ''available'' for domain developers, that's wonderful! However, it's off-topic. -t '' '''Nor''' have you shown that such an abstraction is truly desirable or universal (or even really workable). '' Didn't you agree that you spend a lot of time worrying about sorting? [ ''No, actually I don't, since with HOFs in my language of choice, all I have to do is define my sort criterion. Just like your paradigm of choice, incidentally. What's funny about your resistance to HOFs is that they're really quite useful in the context of table-oriented programming. If you'd stop trying to evangelize and just admit that maybe someone knows a neat trick that you don't, you might realize some of the uses with your chosen style of programming.'' ] Show me. I'm all eyes. ''Already did, but because the word "Array" was found within 60m of my example, you dismissed it out-of-hand. Adapt some of our examples. Try applying the idea to something you're working on right now. Take your favorite language, and pretend it has HigherOrderFunctions too, then solve something. I gave you an example from my line of work, easing calculation of weather data. That's about as real life and application oriented as you're gonna get, and a very practical use-case.'' ''Remember the BlubParadox. We all have to work hard to think beyond the language we work in and consider things in a more conceptual way, otherwise we can't adapt to new (potentially timesaving) tools.'' Maybe you guys are Blubbed by being stuck with arrays and only BigIron databases all your life. Until I see scenarios relevant to my domain, I have little choice but to conclude you are blowing smoke. The consistent pattern to you past examples is that you use your gizmos to work around the limitations of primitive data datastructures. You have called Wolf too many times. ''It's not our job to do your homework, Top. We've said, "This is good. We do lots of neat things with it." We've listened to your TableOrientedProgramming evangelism for a long time (most of us with a surprisingly temperate degree of patience). Some of us have even gleaned insight from what you say because-keep with me, Top-we try and apply this to our domain. You could at least '''try''' to extend that professional courtesy back. Take a moment and try and take it forward yourself.'' I always look for better ways to do something. But apparently I can't apply your claims with significant improvement to my domain and neither can you it seems. Thus, their application to my domain is still an unknown. If you want to sell your GoldenHammer to everybody and their dog, not just systems programmers, you need to find better scenarios. ''Let's see if I can summarize the disagreement here properly: One is saying that the array deletion examples are generic enough to apply to many typical problems, but the other side is saying that they are not really tested until a sampling of real-world situations are "plugged into" them, such as train scheduling or accounts payable scenarios. One is arguing that a template-like solution is a sufficient demonstration, while the other is saying that the devil is in the details and it needs to be tested against such details.'' I do indeed believe in RaceTheDamnedCar. "Lab" examples are often not good enough because they often make assumptions that don't pan out in the real world. I've seen a lot of nifty stuff in toy/lab examples that either can't bend to the real world, or nobody has shown how to bend them. WaterbedTheory is probably the most common reason the lab samples fail to scale, but not the only. -t ---- Let's head that off then. Someone needs to write the code that lets you sort. HOFs in these languages make it easy. Incidentally, for a more application-level use of HOF-objects: I have an application that needs to process long blocks of weather data and calculate things like wind shear, atmospheric pressure, and interpolations between points. Instead of writing one function per task, I wrote one function that iterated across the data (two points at a time) and called a function object on each one. Now, I can write new operations (like calculating the Rise Rate of the weather balloon, a brand new requirement) in just a few simple lines of code without thinking about the iteration. That's the kind of savings we're talking about. [EditHint: Perhaps the weather example needs to be moved to its own section or topic. It is scattered about.] ---- ''I would like to see a sample schema and some pseudo-code, if you don't mind. I don't see where weather balloons relate to interpolation. And, what are your "tasks"?'' Okay, I'll be super clear, but this isn't a problem we used tables to solve (nor do I believe it would benefit from such a solution), so no schema, and the pseudo-code would not be helpful, so none for you. A Weather Balloon sends back data every 2 seconds (or so). This is a lot of data, and the client doesn't want to edit in this space. We interpolate between points that span a 100' boundary (dividing the data into 5000' boundaries). That's the first interpolation link. We are playing with a scheme, but right now it's linear. I might switch it to a Hermite curve if that's proven inadequate. Next, the data the balloon sends back is sometimes bogus. These things are a product of the NAFTA treaty, they fail a bit all the time. So the guy who is going to say if the rocket-launch is okay needs a meteorologist to "correct" the data. They fill in holes (via linear interpolation across gaps) and an linearly or bi-linearly interpolate across arbitrary ranges. Also, we need to calculate Wind Shear, Rise Rate, and Atmospheric Pressure from this data. I wrote one iteration function that takes a functor (HOF simulated in C++, common stuff found in the STL) and iterates across the set two points at a time, calling the functor. The functor can choose to alter the data in any way it desires as it iterates, or not. Then I wrote Functor Objects for each operation I've described. The longest was was 10 lines, because I could program without worrying about any kind of iteration state (the iteration function provided most of it) or anything else. Just the task at hand. And I've been able to non-trivially extend the system to calculate new data as I go, without cluttering the code. Is that detailed enough? ''I am still not fully relating to the example, but it seems like something that EVAL can handle:'' function Iterate(funcName) { .... while (complexIteration) { Eval(functName . stdParams); } .... } ''The only complaint I can see is perhaps speed for super-large datasets.'' That is something like it. It looks more like (please forgive the C++, it's what I get paid to work in, and this has been altered so I don't get sued for giving away LMCO code): void DataBlock::with_range( double start, double stop, PermuteFunctor& functor ) { map::iterator point = _bData.lower_bound( start ); map::iterator end = _bData.upper_bound( stop ); if( point == _bData.end() ) { throw new no_such_record_exception(); } // One argument version of functor invocation, for the first point. Record prev = functor( point->second ); ++point; // Two argument version, for all subsequent points. while( point != end ) { prev = functor( point->second, prev ); ++point; } } Functors are just objects with the operator() method overloaded for one and two argument versions. This version is actually pretty fast too. These datasets involve several million records (two seconds, for 150,000ft, it adds up fast). With this framework, I can write (and have written) diverse operations on the data without worrying about the ranges, the iteration, or even what's going on with the dataset. ---- A faster-executing solution is perhaps to use a case statement: function Iterate(funcName) { .... while (complexIteration) { process(funcName, otherParams) } .... } function process(funcName, otherParams) { select on funcName { case 'foo': {........} case 'bar': {........} case 'blah': {........} etc.... } } Some people balk at long case statements, but frankly if they are well organized, I see no real problem with them. Blocks is blocks, but that is another battle. Personally, I would probably first try to create a "clean" data set that has all the interpolation and cleaning applied, and then process it. Otherwise the process of interpolating and cleaning the data is mixed together with processing, which I would rather separate. See SeparateIoFromCalculation. ---- Well, at least you're trying now. The processing and the "cleaning" are separate processes. They occur in separate places, as different functors. I get the side benefit of using the same iteration function for both, shrinking my code. As for the rest, I thought of that too. I didn't do it because of composition. The user decides what calculations are done and which are not, this is a series of preferences. When they decide, I compose several functors into one larger functor. I sort the functors (sorting again!) based on a simple priority system (generate new points, interpolate old points, perform data calculations) so that I do things in the right order. Since this composition can change on a whim (and it will evolve even more once our automatic data correction algorithms are complete) are more flexible solution than a case statement has to be put in to allow the code to grow. Already I'd have 13+ items in that caste statement, which is 130 lines of code for one case statement (roughly). That's bad. Further, since these compositions are nearly arbitrary (it's a bunch of checkboxes), I didn't want to have to write one of the following: * A series of booleans in One Big Function. Even if I broke it up into a dispatch function to many small functions, that would have raised many questions about the architecture that were superfluous (where do these functions go? Like it or not, you need to address this concern in C++) * One function for every permutation, this is just bad. The anonymity of the functions works strongly to my advantage here. ''At this point I could not make a specific recommendation without knowing your users and requirements and change patterns. Eval may perhaps be too slow for some domains. I won't dispute that. Not everything is good everywhere. There are 3 approaches I have at my disposal:'' * Eval * Case statement * Pre-clean the data * OOP (if the language supports sufficiently powerful OOP) ''The chances of none of them working satisfactorily is small. (See "perfect storm" below)'' It is not small. It's 100%. * Eval isn't an option in C++, but the composition part of it would be suitably nasty if it was, that I would still dismiss it. I do use Eval sometimes in Ruby and TCL, and I know how the code would drape in this situations. It would be a lot more trouble than a HOF solution. ** ''Please clarify "a lot more trouble".'' * Case statements would be VERY long, and grow. It would be like this page, TooLongToEdit. A single function's caste statement extending over at least 130 lines (it's actually a bit more, 10 lines per op is the average) would be a maintenance nightmare * Pre-clean the data. Impossible, since the human is the one deciding how to clean the data. This application IS the pre-cleaning application. The human expert (or the expert system) operates in this application to "QC" the data so that it reflects the weather. Then, another expert will use the data to calculate rocket thrust vectors. This will lean a launching rocket into wind (and alerts them of conditions that would render the launch too dangerous). I'm the one who has to give a meteorologist a palette of tools to fix the data. Think of it like photo touch-up in PhotoShop. Have I done it, have I actually stumped Top? :D [Nah, you said photoshop, so he'll call it a plugin... then a device driver, therefore not applicable to his domain, thus you've proved nothing, then he'll want to AgreeToDisagree because he doesn't have any real argument, since he doesn't really understand everything you're saying. But I give you an A+ for effort!] A+? If I had an underling that gave me round-a-bout requirements documents like this, I would send them back to school before they got their first paycheck. ------------- Another approach that I have not not seen explored is to combine them before compiling. For example, building a big case statement from snippets stored in files. Then, put it into the compile script. --------- PageAnchor "perfect_storm" Further, I don't think it is that likely to have several dozen functions in that Weather Example that ''all'' happen to be mutually-exclusive. After a bunch it would more likely be that some need/want execution for the same loop, requiring something more sophisticated or at least change the very nature of the problem altogether. Thus, four conditions have to be in place '''at the same time''' for my suggestions not the work: * "Eval" too slow for situation * Case-statements too unwieldy (however "unwieldy" is defined) * Can't simplify the loop setup code to reduce it to one or two lines (so that we can have loops inside the functions instead of the other way around). * All functions are mutually exclusive per iteration * Can't combine them before compiling I will agree that there may be an occasional PerfectStorm where my suggestions don't work sufficiently, but the claim seems to be everyday improvements from your suggestions, not just for perfect storms. No language or paradigm does everything well. A perfect storm can probably be found for any approach. (But not being there, I cannot verify that it really is a perfect storm in the weather case, no pun intended.) I believe the original author of the weather example somewhere admitted that more details could not be provided due to a Non-Disclosure Agreement (a "shut-up" clause in an employment or project contract). Thus, it is insufficient for detailed probing and should thus not be used for further exploration or debate beyond those benefits normally allowed for a toy example. A '''Non-Disclosure Agreement''' is another way of saying, "I can't provide thorough evidence here because I'd get sued if I did." Good, it's now clear that you CANNOT give thorough evidence here. You are essentially admitting you cannot present a full case and complete the job. QED. -- top -------- Other than possible speed complaints, I don't see what the problem is with Eval. As far as case statements being too long, split each case block off into a separate function. ''Ahh top. Speed IS a consideration (even after reducing the record space to 100' boundaries, 120000 ft of data reduces to 1200 large records, and so far we've only talked about Wind. I also have to do stuff for the temperature domain, which is even more complex!'' ''But let's ignore speed considerations. Eval would mean mode code! In order to use Eval, we would either have to take our constants and regex them into contextless code or (in an eval system that preserves context, an oddity) we would have to munge together these contexts. Each new context would have to be aware of and avoid the previous ones. The functional semantic (passing in args) implicitly provides this for us, and makes composition as simple as a list of functions which are executed in series, without caring about string manipulation or context management. Less code is good, right?'' I need a far more detailed description of your problem space here. I cannot make recommendations for code organization without knowing what is going on. If your context is that different (a new requirement not mentioned above), then perhaps an entirely different approach is needed. Maybe associative arrays or tables would server context management better. It hints that you are doing everything backasswards, but I cannot identify where without having more specifics in front of me. You are using code to define requirements instead of words, and that is just wrong if clean communication is desired. [ I already use associative arrays heavily. I can look up any record by height and time. You can see that in my sample code's iteration above (note the _bData.find() call, which is a dictionary lookup). Basically, all data is already tabled! I'm already leveraging that aspect pretty heavily. As for the "Everything Bassackwards", all I can say is that my peers can read my code and say it is the cleanest design they've ever seen for such a "murderous" problem. This coming from someone who worked for years exclusively with databases. I rather like the design, since it leads me to a very declarative approach to coding the system (and the entire app is currently under 4k lines of code! This includes the Qt gui, the graphing code, and code for managing multiple flights at once. And it runs pretty snappy even on a P41ghz (which is the lowest quality machine we have). That's the best I can offer. Don't you think maybe you're just being stubborn now? ] {No. You are asking me to accept AuthoritativeEvidence. Top does not accept authoritative evidence. That is a universal constant.} ''As for your case idea, remember that I need to compose these options! A case statement wouldn't let me do this (unless I had an enum for every possible combination of operations). If I had something like Lisp's "cond" construct I could do this, but it would still involve keeping a bool for each. Instead of that, I can just construct and save objects which represent user-defined operations. They manage themselves cleanly and allow for extensibility.'' What do you mean by "compose"? Not mutually exclusive? [ Correct, they may be combined in any arbitrary grouping as defined by the operator. They might want to recalculate shear while interpolating across data gaps. They might want to mark a region of data bad while setting all wind values to 0. They might want to generate missing points, interpolate all points in a range, recompute shear and rise rate, then recompute pressure. ] ''You might also suggest this domain is overly complex. Sadly, it's the requirements of the US Air Force. We said we'd do it, so we need to meet their requirements. Your tools do not address such a problem in any reasonable way. May I offer you this neat new tool, HigherOrderFunctions? They solve this problem neatly and cleanly, with a minimal of code (and fuss) and with a great deal of extensibility. They model this kind of variable, complex BusinessLogic beautifully. '''Significantly better than any solution you have offered.''' Could I be anymore real-world, practical, and firmly rooted in the domain you work in?'' ''I don't know, you keep flip flopping your requirements. I have '''not seen a specific requirement that flunks''' Eval nor case statements (from a code perspective).'' [ The composition requirement throws case statements out the window. Please read above where I address you directly. As for Eval, both speed '''and''' code-bloat make it impractical (although possible) at best. This is Extreme Business Application Programming, Top. No more minor leagues. We work on huge chunks of data, we have to work fast and our code needs to sparkle. ] * [I'm gonna jump in here with old stuff and give an example where you couldn't replace a functor with an eval, although it's specific to C++ functors and not so much to HOFs in general. Because functors are objects, they can store state and data. Say you wanted to get the average temperature over the data range - a functor can carry the running total with it as it iterates, without the need to re-create the iteration, modify the iterating function in any way, or step on the global namespace.] ** Note that because ClosuresAndObjectsAreEquivalent, you can do exactly the same thing with regular anonymous functions in any language that implements closures properly. It's trivial to write an averaging function of this sort in JavaScript, for example. -DavidMcLean ** C++ is generally not used in custom biz applications, at least I don't see it much. But if ClosuresAndObjectsAreEquivalent, then the claim that existing "business-oriented" languages have big deficiencies because they lack formal HOF's doesn't add up because they already have OOP built in. -t ** All object-oriented languages can fake higher-order functions, by passing around objects instead; this is essentially the entire idea behind DependencyInjection. Unfortunately, common business-oriented languages have crippled higher-order function support. For example, the anonymous inner classes of JavaLanguage are almost exactly like anonymous functions, except they're painfully verbose. However, Java's checked exceptions weaken a lot of common higher-order patterns such as the InternalIterator (anonymous inner classes also have semantics which seem to be specifically chosen so that true closures don't happen in Java), so higher-order patterns are used much more rarely in these languages. -DavidMcLean * That depends on the language. Many dynamic languages let one dynamically declare variables anywhere within a function and they keep their scope until the routine terminates. If there is a lot of averaging, it would probably be better to simplify the loop and put it on the inside (which the author alleges cannot be done for unstated reasons) rather than put it on the outside of the individual functions/units. * This solution means that adding a new behavior, like averaging, requires modification of the loop. Using functors allows all sorts of behavior (the averaging is actually a really basic example) to be added without modification of the core loop. I have never seen an eval language that allowed the eval-ed code to maintain it's own namespace, if such a language exists, I suspect that it actually implements that kind of eval on top of HOFs, by compiling the evaled code as a closure. You can do this in JavaScript, for example - new Function("blah blah blah a string that becomes a function object here"). There's no "simplification of the loop", it's already as simple as it can get. Every operation that you would perform on the data is implemented as a single functor. It's a very clean solution, because the code for the individual operations is compartmentalized and doesn't involve any extra boilerplate code for iteration. It's very efficient. The advantage of not stepping on the global namespace is huge, as the complexity of the code grows and especially as other programmers get involved. It's an all round superior solution to using eval. * ''Without looking at specific examples, I couldn't say whether the eval's I know would work. As far as "polluting the global namespace", I tend to break programs down into relatively independent modules such that there is not a big global namespace to pollute. Further, I only need to use eval about once every 2,000 LOC on the average, so it is not like a bunch are bumping into each other. Beside, it may not be global. I've seen two approaches to getting the caller's scope: 1. the option of inheriting the caller's scope for a given routine, and 2. the stack-level scope indicators in Tcl's eval-like functions. -- top'' * Jesus. You're claiming that because you don't do this, his example isn't valid? You only write small applications, so approaches to reducing complexity in large ones are invalid? I'm not sure you even know what the scope and namespace problems involved in this case are. Every function that gets executed (remember, we're composing operations from sets of functions here, not hardcoding everything) needs to operate inside it's own scope so that it's locals don't end up stepping on the locals of other functions. But remember that it has to be a persistent scope, so you can store things like running totals, don't want a new scope every loop iteration. You cannot do this with any eval system I have ever seen, even TCLs. The only eval system I can think of that can do this is one that allows you compose new HOFs via eval (which I have seen), which is sort of missing the point. You don't want to execute in the *callers* scope, you want to execute in a unique, new scope. Modularity is *good*. Every operation is a unique module. * ''Put the running totals into a map array. Until the detailed requirements are public, this is moot anyhow. I am not going to chase your ghost unicorn example anymore ("hidden" via your org's NDA). Your requirements are suspicious, as already described. And, every interactive application I have ever seen can be broken up into smaller "tasks", regardless of size of the project. The "size" problem is possibly of your own creation, not out of necessity.'' [Are you kidding... the guy just gave you a great description of his domain, the problems faced, and how he solved them.] The "context" thing is brand new. Just whip up some sample data and show Eval choking, okay? '' It's not new. We touched on it earlier, but tabled it for a bit. I mentioned that Eval-ing code requires that the code be placed in the "Context" of the executing program. Languages handle this differently. Most remove ALL context, like Lisp and Ruby. You suggest FoxPro keeps existing context (which is both good and bad, I think). Either way, you need to be concerned about:'' * ''How multiple, concatenated blocks of eval'd code interact.'' ** Please clarify. Is this string concatenation you refer to? There may be another way to do it, but that depends on the problem at hand, which is hidden via a NDA here. * ''How you place your values into the code (which is a bunch of string manipulation code or symbol manipulation code, increasing code size and complexity)'' * ''How they interface with the point where they are called.'' I am sorry, but I have not seen any problems or "bloat" with an Eval solution outside of speed (which I cannot test). I will give you credit for speed just to move beyond that annoying topic. But Moore's law is on my side in the long run. I am not sure what you mean by the second point of placing values in the eval string. Generally one just uses variables. The eval's and exec's I am familiar with are very similar to a run-time "include" statement. You can stick them just about anywhere. Maybe even the repeated loop statements can be executed that way rather than try to put the function inside the loop (I have never tried that, though). They "see" the context of where they are stuck just like the code actually stuck there. (FoxPro does not have an Exec-like statement, but gets around that by having functions inherit caller scope. TCL has more powerful such tools, but FoxPro's is usually good-enough.) If Lisp and Ruby lack contexted-based Eval's or Exec's, then they are crippled in that respect. At least make it an option. If we continue with this example, I would have to play "20 questions" to ascertain the specific requirements and relationships, which would probably end up violating your NDA, getting us both in the slammer. ---- You are so bound up in FoxPro that you can't see outside it. That's too bad. Hopefully you can grow a bit as a programmer and see what we're talking about at some later date. Until you're willing to take your head out of the sand, this discussion will continue to SpinLock. Your argument about case statements lies broken at your feet. Your eval reasoning is so hopelessly coiled around an outdated single-paradigm language that you can barely have a rational conversation about it. You're unwilling to even consider new tools, merely because of mental inertia. ''Yeah, that's right. I am closed-minded and evil, and you are open-minded and good. You did NOT identify any specific, documented problems with eval(). In fact, it appears you don't even understand how eval's outside of Lisp and Ruby work. How do you know they won't work if you have never used that kind??????'' You're not concerned with making great software. You're concerned with making TopMind right. And that is a profound waste of my time, because all I wanted to do was share a design that used HigherOrderFunctions to a significant degree with great success. Great software. You're so busy trying to attack non-table-oriented programming that you can't even extend the professional courtesy of thinking about something critically on your own. We have to walk you through it like a truculent college freshman, or you start screaming about how unrealistic and academic it all is. The only people you're hurting with such an attitude is the people who have to use or maintain your software. And me, because you wasted my time, and a fair amount of it too. ''If you can identify SPECIFIC problems with my code, I will mend my ways. Vagueness is Evil!'' [ How about this? You're refusing to examine a new tool. You have a small number of tools you evaluate as a GoldenHammer, and you ruthlessly push square pegs into round holes with them. While doing this, you loudly decry the nobility of this action. You're doing this for our own damn good, right? Until we all use the paradigm you understand, we're not really working. Never mind the fact that none of your proffered techniques can even scratch the surface of my problem (and all but eval was considered, because eval is not available in C++). Now that you've run into this problem, your first step-before even considering the tool I used to solve the problem cleanly-is to try and redefine the problem into an easier form. Because, well, these requirements sound too exacting and complex! Barring that, you say they're unclear. Guess what? They are, but I'm not giving you any of the unclear ones. Unclear requirements are part of Business Applications, your code has to be flexible so that you can adapt to what your customer wants, even before they're done deciding about it. You didn't even seem to really read what I wrote about the order of the operations (nor did you reread it carefully, I told you the order outright!). I'm done with this, TopMind. I did my best, and I've been a hell of a lot more patient and detailed than anyone should expect me to be. but your hardheadedness has defeated even my optimism. You're not trying to learn, you're trying to fight. I don't have enough to gain by continuing to hammer the same points into your skull until I finally break through. Good luck TopMind. You need it. ] ''You did NOT "do [your] best" if you exclude specific scenarios and requirements that bust Eval. You are expecting me to accept anecdotal evidence at face value. If that is "hardheadedness", then I am fricken guilty as charged. --top'' -------- Questions: * Is the order of the operations always the same, or does the user chose the order? ** ''I told you already.'' ** {Sorry, I did not find it clear. You imply the order is static one place, but dynamic in another.} * What is the relationship between iteration algorithms and the operations on the data? If they are independent, who or what chooses the iteration algorithms? ''I gave you the code for the only iteration algorithm I need to use.'' * Is a record marked "bad" as a whole, or are individual values (columns) marked? ''A whole, but Temperature records complicate this.'' * Can different users mark different records/columns "bad"? How are these kept separate? ''They are not, but badness propagates in a way not germane to this subject.'' * What is done with the results after the user selects the options and processing takes place? Does it generate new data sets, or just some kind of summary report or plot? ''I updates existing data sets to reflect the operation, and keeps a MementoPattern style snapshot for undo steps.'' * Why are only pairs used for interpolation? Don't some interpolation algorithms use more than pairs? ''They could, but the pairing was a convenient number. I could have only done one at a time and had my functors accumulate needed state if I wanted. For one algorithm that uses 4 points, I do just that, but it's an exception. '' * I don't understand why the looping is so complicated? Having to put the functions "inside" a single loop instead of having each function have it's own loop complicates the design. I suspect there are ways to simplify the loop, but need to know more about your domain. I have never encountered a similar situation where I could not find ways to simplify the loop. Perhaps the problem is with C language in particular. ---- Now let's try to bring this discussion to some closures. This discussion was supposed to prove what? That higher order functions is a language feature pretty much unrelated to relational model? Or was it intended to prove that adding function values to FoxPro would be an improvement to FoxPro? I don't understand your objections at all, TOP. Why not have function values in your FoxPro or whatever? Would it ruin FoxPro''''''s purity? ''It was claimed that such features were a *significant* improvement. I don't see anything significant.'' [Of course you don't, because you've never worked with them, you have no experience in the matter, and don't appreciate the advantages offered over eval, due to that complete lack of experience. It apparently never occurs to you that sometimes you have to use something for a while to truly grok it.] ''Compile-time checking and perhaps speed advantages are about the only thing I see, and those are incremental things. Eval is simpler conceptually and applies to more than just functions. Why clutter a language with gimmicks unless a strong justification is shown?'' [That statement just proves you don't know what you're talking about, how about not offering up opinions on things that you've never used. How about listening to people who actually have experience in the matter and taking their word that it's a huge advantage?] I am taking your word for nothing. Show us code and CLEAR requirements. Why have this discussion if you are not going to make a case by demonstrating something concrete rather than "you would JUST agree with me if you had my experience". That's anti-scientific. You don't need discussions to make claims about your grand experience. That's a '''resume''', not a discussion nor a demonstration. -t ---- Iteration can be improved upon. Here's a Common Lisp example using a brief form of iteration. (coerce (loop for i across #(1 2 3 4 5) when (oddp i) collect i) 'simple-vector) answer> #(1 3 5) Here, ''#(1 2 3 4 5)'' is syntax which denotes an array containing 1...5. It says that when an element is odd, collect it into some unnamed list, which will then be returned when ''loop'' is done. That list is then coerced into a "simple vector". (A kind of array without wizzy features like fill pointers.) I personally think this means two things. * Iteration needs to be improved in most languages to take care of common cases. * Functional programming is vital as a guide to improving iteration. ''--which was the entire point of the example, not that top noticed.--'' A ''meta'' lesson is that macros are good, since ''loop'' is just a macro some guy worked on. This means a language which supports macros well (with debugging features, etc) offers a way to extend the language, kind of like a patchless opensource. But of course that is a controversial conclusion. (Some people are worried what their coworkers will do with the power of macros. And I don't have the experience to know whether this is a true concern.) ''[I admit I wasn't too clear about the purpose of this example either. If it's just about how HigherOrderFunctions are better than conventional functions, nothing about iteration, then please feel free to delete my above CL example. -- TayssirJohnGabbour]'' ------------ For thoroughness, here is an SQL-based example. I am assuming that complex processing or analysis of the "nodes" needs to take place before we know what is to be deleted. Otherwise we could just use: DELETE FROM foo WHERE blah blah" Complex processing version in PHP-like syntax: $toBeDeleted = ''; // empty string list $rs = query("select * from Foo"); while (getNext($rs)) { $someCondition = [complex processing here].... if ($someCondition) { listAppend($toBeDeleted, $rs['primaryID']); } } query("delete from Foo where primaryID in ($toBeDeleted)"); This assumes numeric ID's, but some dialects can handle quotes around numbers also if we wish to make it more generic. The default string delimiter is a comma. If you want a different delimiter, then use a call such as: listAppend($toBeDeleted, $rs['primaryID'],'|'); (I am not necessarily using PHP's list function conventions here.) -- AnonymousDonor And then consider what happens when you need to delete based on some other condition: $toBeDeleted = ''; // empty string list $rs = query("select * from Foo"); while (getNext($rs)) { $someOtherCondition = [more complex processing here].... if ($someCondition) { listAppend($toBeDeleted, $rs['primaryID']); } } query("delete from Foo where primaryID in ($toBeDeleted)"); And then if you have to delete on another table on a different condition: $toBeDeleted = ''; // empty string list $rs = query("select * from Bar"); while (getNext($rs)) { $someCondition = [more complex processing here].... if ($someCondition) { listAppend($toBeDeleted, $rs['primaryID']); } } query("delete from Bar where primaryID in ($toBeDeleted)"); You can see where this is going. DuplicatedCode up the wazoo. CutAndPasteProgramming. Massive OnceAndOnlyOnce violations. Now compare the version using PHP's approximation of HigherOrderFunction''''''s (it's really more like eval, it doesn't close over the lexical environment, but it's usually GoodEnough): function deleteWhere($function, $tableName) { $toBeDeleted = ''; // empty string list $rs = query("select * from $tableName"); while (getNext($rs)) { $someCondition = $function($row); if ($someCondition) { listAppend($toBeDeleted, $rs['primaryID']); } } query("delete from $tableName where primaryID in ($toBeDeleted)"); } function someCondition() { [complex processing here].... } function someOtherCondition() { [more complex processing here].... } function someThirdCondition() { [still more complex processing here].... } deleteWhere('someCondition', 'Foo'); deleteWhere('someOtherCondition', 'Foo'); deleteWhere('someThirdCondition', 'Bar'); Each new usage is only a function definition and a function call. Compare that to writing out a half dozen lines of QueryAndLoop code each time. And if you change the database (PHP requires different API calls for each database vendor), you only need to change one function instead of every single query. -- JonathanTang ''That is not really much different from an Eval()'' Wrong, that's a lot different, and a lot cleaner. '', except maybe runs faster. However, note that the function setup takes just about as much code as the loop setup, so there is not really much, if any, code savings either way. '' Wrong again, it's a huge difference in the amount of duplicated code, and function setup is no where near as much code as that loop setup, plus it's much simpler. ''Like I mentioned in the above weather example, usually there are ways to simplify commonly used loops. If I had more domain details, I would bet money I could do it there too.'' Apparently you just don't know what good code looks like at all, ''Knock it off with the jabs, will ya? It ain't helping and I am twitching to fight back. I can say mean things about your fad domga shit also.'' even when it's shown to you. Jonathan's php above is far far better than yours. deleteWhere used as a HigherOrderFunction makes much more sense than repeating that same query loop code every time you need it. ''Either you repeat function setup code or you repeat loop setup code. I don't see any way around it. I suppose you are assuming functions are always created no matter what out of dogma (see LongFunctions). But I am assuming that something will be in-lined if it is not used more than once. Otherwise, we may have to create a complex parameter list. One-size-fits-all parameter signatures are not that common in my experience.'' [As I've said before, your experience is quite limited, because having common functions signature for all this stuff is trivial, if you have No arg, single arg, and double arg, Predicates, Functions, and Procedures, that covers any scenario you can dream up, and allows functional composition and complete easy reuse of HigherOrderFunction''''''s like that one above. There's never a need for a complex parameter list, parameters are passed via lexical scope, i.e. closures, and don't need to be named, another one of those fancy features that you don't think we need. And the lack of lexical scope is why eval simply isn't sufficient for a replacement to closures, because without lexical scope, you'd have to pass a bunch of parameters.] The loop code is written once. Post that, predicate functions are the only thing required. All of these will be short and simple, and typically less code than even the tightest of the tightly written loops. ''Not by much.'' [By tons!] * Well-oiled loop setup code should not take more than about 10 tokens. A function call will average around roughly 7. Thus, you have a net savings of about 3 tokens. Hardly revolutionary. (alleged) Tops version of 10 loops $toBeDeleted = ''; // empty string list $rs = query("select * from Bar"); while (getNext($rs)) { $someCondition = [more complex processing here].... if ($someCondition) { listAppend($toBeDeleted, $rs['primaryID']); } } query("delete from Bar where primaryID in ($toBeDeleted)"); $toBeDeleted = ''; // empty string list $rs = query("select * from Bar"); while (getNext($rs)) { $someCondition = [more complex processing here].... if ($someCondition) { listAppend($toBeDeleted, $rs['primaryID']); } } query("delete from Bar where primaryID in ($toBeDeleted)"); $toBeDeleted = ''; // empty string list $rs = query("select * from Bar"); while (getNext($rs)) { $someCondition = [more complex processing here].... if ($someCondition) { listAppend($toBeDeleted, $rs['primaryID']); } } query("delete from Bar where primaryID in ($toBeDeleted)"); $toBeDeleted = ''; // empty string list $rs = query("select * from Bar"); while (getNext($rs)) { $someCondition = [more complex processing here].... if ($someCondition) { listAppend($toBeDeleted, $rs['primaryID']); } } query("delete from Bar where primaryID in ($toBeDeleted)"); $toBeDeleted = ''; // empty string list $rs = query("select * from Bar"); while (getNext($rs)) { $someCondition = [more complex processing here].... if ($someCondition) { listAppend($toBeDeleted, $rs['primaryID']); } } query("delete from Bar where primaryID in ($toBeDeleted)"); $toBeDeleted = ''; // empty string list $rs = query("select * from Bar"); while (getNext($rs)) { $someCondition = [more complex processing here].... if ($someCondition) { listAppend($toBeDeleted, $rs['primaryID']); } } query("delete from Bar where primaryID in ($toBeDeleted)"); $toBeDeleted = ''; // empty string list $rs = query("select * from Bar"); while (getNext($rs)) { $someCondition = [more complex processing here].... if ($someCondition) { listAppend($toBeDeleted, $rs['primaryID']); } } query("delete from Bar where primaryID in ($toBeDeleted)"); $toBeDeleted = ''; // empty string list $rs = query("select * from Bar"); while (getNext($rs)) { $someCondition = [more complex processing here].... if ($someCondition) { listAppend($toBeDeleted, $rs['primaryID']); } } query("delete from Bar where primaryID in ($toBeDeleted)"); $toBeDeleted = ''; // empty string list $rs = query("select * from Bar"); while (getNext($rs)) { $someCondition = [more complex processing here].... if ($someCondition) { listAppend($toBeDeleted, $rs['primaryID']); } } query("delete from Bar where primaryID in ($toBeDeleted)"); $toBeDeleted = ''; // empty string list $rs = query("select * from Bar"); while (getNext($rs)) { $someCondition = [more complex processing here].... if ($someCondition) { listAppend($toBeDeleted, $rs['primaryID']); } } query("delete from Bar where primaryID in ($toBeDeleted)"); Jonathan's same 10 loops assuming anonymous functions function deleteWhere($function, $tableName) { $toBeDeleted = ''; // empty string list $rs = query("select * from $tableName"); while (getNext($rs)) { $someCondition = $function($row); if ($someCondition) { listAppend($toBeDeleted, $rs['primaryID']); } } query("delete from $tableName where primaryID in ($toBeDeleted)"); } deleteWhere(function($each){[more complex processing here]....}, 'Foo'); deleteWhere(function($each){[more complex processing here]....}, 'Foo'); deleteWhere(function($each){[more complex processing here]....}, 'Foo'); deleteWhere(function($each){[more complex processing here]....}, 'Foo'); deleteWhere(function($each){[more complex processing here]....}, 'Foo'); deleteWhere(function($each){[more complex processing here]....}, 'Foo'); deleteWhere(function($each){[more complex processing here]....}, 'Foo'); deleteWhere(function($each){[more complex processing here]....}, 'Foo'); deleteWhere(function($each){[more complex processing here]....}, 'Foo'); deleteWhere(function($each){[more complex processing here]....}, 'Foo'); It's quite obvious that Jonathan's is far better. Top, if you can't see that, you have problems. If you think that's a 3 token savings, then you better recount buddy. ''RIGGED! You excluded parameter management. '' I told you, we don't need to pass parameters, the lexical environment does that for us. ''Those things most likely will not work in isolation. They will interact with their code environment. And, you made yours look smaller by putting it all on the same line. Further, likely one will not need every piece of the loop versions. Some may not have complex processing, some may not have deletion, some will have different WHERE clauses etc. '''You assume far too much uniformity to be realistic.''' MutuallyExclusiveCategoriesDontScale is true of "lists" of function alternatives also. I see such M.E. lists break down in both classifications/taxonomies *and* function decomposition. I call it as I see it in the real world. If you observe more regularity (M.E. lists), then we will just have to AgreeToDisagree. --top'' No, you assume that I can't make it that uniform, I'm telling you you're wrong, I can. ''Don't "tell" me, show me with a realistic scenario. I used to try those kinds of things with Eval, and they don't really help when real world variations come it. When you start dealing with all those variations, and the parameters, yours will be just as large. You will possibly have AttributesInNameSmell or massive parameter overloading also.'' Again, wrong, don't assume because you can't figure out how to factor out those variations, that someone else can't. I do it daily. Those examples are quite realistic and aren't rigged. You just don't understand functional programming and implicit param passing via closures using lexical scope, if you did, you'd realize it's quite easy to rid yourself of passing parameters manually for those kinds of operations.'' * If VariationsTendTowardCartesianProduct, and variations can split in the fractal nature described there, then blocks and HOFs are too big a granularity of variation management. -t * Re: "I do it daily" - Then show a realistic scenario that applies to biz apps. I'm looking for something beyond anecdotes; I want to see it happen in semi-realistic code that I can actually see. Claims about code that I cannot see are not sufficient. I want to touch the unicorn myself. Maybe I can use my favorite approaches to rework it even simpler than yours. We'll never know until I examine the code. -t ''Okay, I'll give you the scope. But, using a language that allows a routine to inherit parent's scope, such as FoxPro (see examples above), one can '''do the same thing without explicit HOF''':'' =deleteWhere("myFunc1", "complex processing 1", "foo") =deleteWhere("myFunc2", "complex processing 2", "foo") =deleteWhere("myFunc3", "complex processing 3", "foo") ..... ''But even with such ability, my claim that sufficient uniformity probably lacks in the real world stands. (I should point out that FoxPro can only execute one statement in an Eval clause. However, one can still execute a function that still inherits caller scope if they need more than one statement.) '' FoxPro is a very limited language, you're still missing the fact that lexical scope is key. Inheriting parents scope doesn't cut it, because many HigherOrderFunctions after being called, curry your function around to other HigherOrderFunctions, or make recursive calls to themselves, and each call creates a brand new scope to prevent conflicts with variables from previous scopes, there is no shortcut, you must have lexical scope to realize the full power, simply inheriting the parents scope won't work. Your claim that sufficient uniformity lacks in the real world, is from your lack of experience with functional programming, and is flat out wrong. Functional programming makes these things trivial, see the other dudes lisp example below, and give him some time to fix it up a bit, it was a first cut. * First you say I cannot do it, and then when I do, you move the goalposts into a foggy fictional world where you need more nesting of such techniques for undemonstrated reasons. But here's where the major reuse comes in. Those predicate functions could be reused for a ''different'' predicate-taking function on a structure, like one that collects. The parameter list for these is always the same, they take one argument which is the object to act upon, returning a boolean. --AnonymousDonor If you can show it significantly simplifying typical biz-app code, I will give you credence. Until that, it is only anecdotal claims, of which I am highly skeptical. You claim that I am doing it all wrong and dumb, then show me the right and smart way with your actual shorter code. Let's see it shorten: http://www.geocities.com/tablizer/chal06.htm [NoteAboutGeocities] (ChallengeSixVersusFpDiscussion) That's a report writer, not much of a program, has no real behavior to begin with. It just presents the results of sql queries, strawman. * Why do my enemies always talk about "real behavior"? It has about 350 lines of code (which you can download). If you cannot reduce it with your allegedly massively powerful techniques, why not? Exceptions can often teach us a lot. * ''[I hope you realize how humorous this statement is, Top]'' * Sorry, I don't seem the humor. It is probably false patronizing if I know you guys. You are not showing realistic business examples of less code (or any other clear metric). The complex nesting and multi-recursion you talk about may be an example of mixing IO with calculation which may be a design smell, as described in ChallengeSixVersusFpDiscussion. I cannot tell if you are committing such a sin or not without seeing your actual code. --top ''Actually, I see a way with HigherOrderFunctions to make that process quite trivial. If I have some time later, I'll sketch out a lisp version that does it. Whenever we talk about moving and collecting logic, HOFs usually provide an elegant solution.'' One has to see the entire thing to see if there is a net reduction. Simplifying one part may complicate the unshown parts. There is a reporting example in SeparateIoFromCalculation that may relate to the issue of HOF's and loops. See also VariationsTendTowardCartesianProduct. ---------------- ''Here is a first-cut at the link above in lisp leveraging HOFs. This was a 30 min attempt, does not meet all the requirements, and in retrospect I shouldn't make a list of functions, I should build a new one that does both. But, it is a start. On Monday I'll revisit and refine, I am on vacation to San Francisco now!'' (defvar *queries* nil) (defvar *criterion* nil) (defvar *db* (list '(:reporter "Jango" :title "I rule!") '(:reporter "Mace" :title "No you don't!") '(:reporter "Boba" :title "Daddy! No! Why??"))) (defun run-queries (queries) (loop for (string func) in queries do (format t "~%~a: " string) collect (funcall func (read-line)))) (defun passes-criterion? (item) (loop for func in *criterion* unless (funcall func item) return nil finally (return 't))) (defun get-matches () (loop for record in *db* when (passes-criterion? record) collect record)) (defmacro make-query (name-str &rest body) `(progn (push (list ,name-str (lambda (value) ,@body)) *queries*))) ; Example query. Keep in mind we could shorten this further. (make-query "Reporter's name?" (lambda (x) (equal (getf x :reporter) value))) ''Please read the whole thing before working up a bunch of code.'' Please read my comment above before getting worked up. This is a trivial start to what could be a very real implementation. It uses HigherOrderFunctions. It doesn't have to be perfect, it took 30 minutes. Looping over Eval's done on lists/tables could achieve something similar, but by itself is only a small portion of the sample app. It's also missing the UI. Your code appears to also be reinventing a very limited competitor to SQL. The sample is not really about how to roll-your-own query language, but rather more about ''gluing together'' different services and "interface" languages, such as HTML and SQL. In biz apps that's often what one does: glue together existing tools, services, DSL's, and API's into a usable unit for a specific purpose. -t ---- '''Pending Summary''': Powerful tools available that can do most of what HOF's do: ''No they can't, please wait till the discussion is over before summarizing it wrongly.'' * Eval() and/or Execute() with scope inheritance * Functions that (optionally) inherit caller's scope The only thing I have not seen them do is create custom control structures. However, languages like TCL seem to do it by treating blocks as strings to be executed by the command call. The upLevel command allows it to execute a string (block) in the caller's scope. Yes, they are probably slower than HOF. I will give HOF the speed benefit. However, those kinds of things are not usually a bottleneck in my experience and domain. If they are too slow for massive weather data processing, so be it. Don't spoil the simplicity for the rest of us. You win on speed, I win on simplicity. ''Wrong, you lose on both counts. The HigherOrderFunction''''''s are both simpler, more elegant, faster, and safer. You don't seem to know what simplicity is.'' * And you don't seem to have a snowball's clue in hell how to demonstrate such. You should become a PHB marketer because you are all talk and no walk. (I already gave you "faster", by the way. I concede that argument.) ''How is: calling a function that reads in a string, parses it, establishes appropriate name bindings to the current environment, then finally compiles/interprets and executes the resulting code. [eval/execute] 'simpler and more elegant' than: calling a function. [higher-order-functions] ???'' * Who said anything about manual parsing? 50 years from I doubt they will give a fudge about it being slower. The bottlenecks are more and more in the I/O, not processing. -- top ----------------- I have suggested that functional "tricks" often don't save that much code because the vast majority of repeated loop setup code can be reduced to 2 lines or less, and my code averages about one loop every 15 lines or so. Thus, any savings in code from functional-based loop simplification will only be a few percent's worth most of the time, and this is only if there is a lot of regularity. Again, this is "good", but not "great", as some have claimed. The general pattern for repeated loop setup is: ''( And for comparison, how much work you can do with HOF lines. )'' myMap1 = loopSetupFoo([local vars as parameters]) | ''# Compared, three HOF lines can do many times more'' while (myMap2 = getNextFoo(myMap1)) { | ''myMap.each { |x, y| puts "#{x} => #{y}" } # print'' .... | ''myMap.sort { |x, y| x > y } # sort'' } | ''myMap.reject! { |x,y| x.isNotSpecial } # filter'' ("Eval" techniques may reduce it further, but are not considered here.) ''{ And there goes your "Loop code is short" suggestion out the window. Of course, it totally missed the point to begin with... }'' This pattern follows the observation that we need one "initialization" call and one repeated call for loop iteration. Sometimes they can be combined, but in many interpreters/compilers this results in unnecessarily repeated execution per loop iteration. I have not seen many situations where this two-line technique cannot reduce repeatedly-used looping patterns from several lines per instance to 2. In all likelihood, the above weather example can also be likewise reduced. ''This solution is a common external iterator pattern. We see these all the time in the C++ StandardTemplateLibrary. They are falling out of style because, even with shortened loop setups, they are a pain for anything more complex than simple iteration. Your method would provide no savings, in the long run, with operations like collection or filtration. It is far easier to make "verb" methods like "collect", "filter", "reject", "sort" that accept logic and handle the gritty details themselves. Since these operations are called commonly, the net code savings over time is substantial.'' ''Your other eval solutions suggests it/'s safe to modify the local environment to set things up. This is really a rather scary operation to do. What if someone changes your environment? Implicit reassignments happen, things that someone else may not realize. It's all well and good to say, "Eval gets access to the enclosing scope, so I can create variables as needed." but it's dangerous. Lispers can do this too, with macros, but it's dangerous and people usually try to avoid such "VariableCapture" because implicit behavior is unsafe and confusing in the long run.'' ''HOFs can do a lot of things. We've been focusing on common uses, such as simplifying and abstracting iterative tasks (like doing something for each item in a collection), for providing plug-and-play logic for common tasks (criterion for sorting, methods for printing), for providing dynamic callbacks for more complex systems (secure network protocol wrapper), or for providing a generic framework for arbitrary operations (such as the weather example above).'' ''While it is true that Eval can do many of these things, eval does it slower, with less overall safety, and with somewhat more complexity when it comes time to compose or swap data. Using Eval for many of these tasks is the application of a GoldenHammer, because you can get exactly the same end result faster, with less code, with more genericity, and with more safety. In programs that do static type checking (such as C++ or Haskell), HigherOrderFunctions provide this as well.'' ''Eval '''can''' be a better solution, but its "SweetSpot" of ideal applications is much narrower due to speed, syntax and safety. In languages that provide both (and differentiate between them), HigherOrderFunctions are usually the preferred method because of this (see Ruby's and Python's standard libraries). Still, people use them when they need to perform some operations or dynamically load code. That's what they're there for, and thusly they excel. A HigherOrderFunction doesn't even address that issue. Common idioms in many languages eval code which defines (and if possible, compiles to native/bytecode representation) functions for further use.'' ''TCL, which has come up several times now, is somewhat of a degenerate case for this debate-and is rather unsuitable for this debate-because in TCL programs are strings which are programs (you can check out HomoiconicLanguages for more detail on this). As such, in TCL Eval is much closer to a HigherOrderFunction-like-meme than in other languages like Lisp, Ruby or Python.'' ''When we talk about loops and functional tricks, this is often done as much for reusability as it is for explicit code savings. Examine the following Ruby code:'' myarray = ["Header1", "Header2", "Header3", "Header4"] myfile = File.new( "somefile.txt" ) myresult = [] # Blank array my_hof = lambda { |x| myresult << x } # A HOF to append items to an array. myarray.each( &my_hof ) # Append each array item to myresult via the HOF myfile.each( &my_hof ) # Append each file line to the array # print out every item in the array on its own line myresult.each { |x| puts x } ''Note how the interface for the File iteration is identical to the array iteration, we use the same HOF for both too. Pretty cool, huh? If we were defining functions (or classes, or what-have-you) dynamically, at run time, based on unknown input (trust your input? I hope so) then Eval would be the way you go.'' {First of all, I tend to use RDBMS, or at least MinimalTable''''''s for such "structures" and thus don't care much about added array power. You are essentially bragging about putting DatabaseVerbs on arrays. Second, "filtering" is often not very generic because we cannot filter in just one spot. Rough example:} PageAnchor Mandy (just to give it a unique name) ''Functional version of that, exactly half the lines of code, same logic.'' while (foo = getNext(bar)) { | bar.applyMatches(function(each){ filterThis = false | if(!condition1)return true; if (condition1) { | doSomething(); doSomething() | if(!condition2)return false; filterThis = true | doSomethingElse(); if (condition2) { | return glob();}, doSomethingElse() | function(foo){process(foo)}); filterThis = glob() | } | ''I would however, consider learning CommandQuerySeparation, decision making and processing'' } | ''do not have to be intermixed, there's always a cleaner way, and this sample is nasty.'' if (! filterThis) { | process(foo) | } | } // end-while | (indentation messed-up by TabMunging) [It might be half as much code, but using embedded returns can be problematic at times, and considered a "smell" by some (although I am not adamant about it). See below (PageAnchor Mandy2) for a shortened example.] Those aren't embedded returns, they're GuardClause''''''s and they're a common pattern to clean up that's rat's nest of nested conditionals you used. They return from the anonymous function, and most consider it far cleaner than nested conditionals (for small functions) because they clearly signal, "done", without forcing me to continue reading code. [I could do the same (embedded returns) to reduce code size, and keep the scope using Pascal-style nested functions or FoxPro-style caller inheritance. For example, if we wanted to print out a list of rejected ones for debugging or logging, the multi-return points would have to be reworked. I would have less rework. And, your "half" count is an exaggeration because your example is a bit wider code-wise. I copied this example to LinesOfCode where it is shown with alternative metrics that raw line-counts is misleading. Here I reworked it using your "return" technique. This assumes that function scope inheritance is the default:] PageAnchor Mandy2 while (foo = getNext(bar)) {if (can()) process();} function can() { if(!condition1) return true; doSomething(); if(!condition2) return false; doSomethingElse(); return glob(); } This appears to me to be within about 5% of the right-hand version above code-size-wise. {Decision making and processing is often intermixed in the real world. We cannot know all filtering conditions up front, and thus often must make them in the course of doing something.} Actually Top, I'm bragging about being able to put database verbs on '''ANYTHING''', even things that it might not normally make sense to do so on (each_line for a network socket, for instance). By building up such verbs, we can treat low-level structures at a higher level, without losing any control when we want it. It gives us more than that, but it's a start. As for your attempt at an example, it seems to be very poorly formed, like you're trying to do more than one thing at a time. Maybe you should think it through? Your need to reduce work so that one loop does more than one thing at a time. I assume you'd like to filter across a precomputed range. We could do this more elegantly if I had a bit more time, but here's a version does the same thing: shouldFilter = false my_map.reject! do |key,val| # ... do something to shouldFilter here, based on key or val, conditionally. shouldFilter # Returns shouldFilter end You tried to make it look very complex, but all you're really doing is some additional logic to determine when to filter (and possibly take other actions, which is bad style in any paradigm, but is not ruled out here). Do you have anymore spurious examples for us to debunk? ''I am just trying to point out that in my observation, it is often tough to separate things because they tend to be inherently coupled to each other. It would indeed make life simpler if we knew all the filter conditions before entering a loop or block, but often we don't. (Often if the filtering is simple, the WHERE clause does the filtering.) And, often they interact such that there may be filter A and filter B, each of which is finally applied based on other information we collect in the process. If you disagree and claim there is more regularity than I see, then so be it. It is anecdote-versus-anecdote. -- top'' Can you give a real-world example of this? -- dlf ''I had to parse a funky log file once and rejected lines as I parsed further down and progressively obtained more information. For example, it might reject a line because it is missing a date field(s), but later reject lines that have an invalid date range.'' ---- What's really interesting is that HigherOrderFunction''''''s allow programming in a more declarative fashion using verbs like foreach, select, reject, detect, remove, inject, and collect, exactly the kind of thing you'd think top would actually prefer over hand rolled loops. Odd that he's arguing so much for the code heavy hand written loops. ''Indeed. And since he's such a huge fan of declarative programming, it's an odd position to take. It reinforces my perception that TopMind is rejecting these ideas mostly on the grounds of inertia, not by any rational reasoning.'' Like I keep saying, most of those are trying to sell reinventing-the-database using arrays. Rational thinking is not reinventing something that already exists and is more scalable than arrays. [Get off the array thing top, it's a straw man, HigherOrderFunction''''''s are used for tons of things besides looping over stuff, see BlocksInRuby for a few examples of common use.] Tell me it is more logical to reinvent non-scalable wheels. Vulcan that! Nor am I "rejecting" them. I am only suggesting their benefits are overrated by some FP zealots, who apparently never mastered imperative programming and databases because they use crappy non-FP examples to compare. If you have a good database system, you don't need to use files and arrays that often. Thus, bragging about how your techniques make files and arrays interchangeable is falling on deaf ears. You are solving the wrong problem correctly. You claim I repeat myself too often, but you still cannot predict my responses and point of view. -- top ''You sure wish we were re-inventing, but we're not. We're making sublanguages, control structures, and powerful libraries and applications, extending into many realms. You're busy worrying about making reports.'' DomainPissingMatch-based intimidation again? ''You claim the benefits are overrated, but your examples are poor and deliberately worded to give yourself an non-existent advantage. Whenever one is discredited, you run to a new tack on the topic, to face the same slap.'' Odd, that is just about how I perceive your "tactics". See "knee in ear" analogy. ''Now, '''as predicted above''', you're returning to, "All this is unnecessary if you work with tables." This position-tenuous as it is-is tantamount to concession by you, TopMind, because you can't back it up. '' At this point it is anecdote-versus-anecdote on both sides. You say my examples are unrealistic, and I say yours are unrealistic. Only a field investigation team can settle such. ''Whenever people point out application domains that these techniques don't allow you handle with ease, you claim they're "too low level," even when it's clearly in the business application domain. Or you claim your approach is easier, although that's sheerly a matter of your perception (we've shown you how we made these things easy, and you say, "No no no! I'm just '''sure''' a case statement will save the day!").'' I presented the case statement as one possible alternative above. Nobody has found any objective problem with it. The claimed problems were based on anecdotes, not shown code. That weather example is not very good anyhow because the reader cannot verify most of the claims made and don't know the users' actual needs. When I ask questions, I get the equiv of "RTFM". You assume your writing is clear, but it sometimes is not. I can point out specific examples if you wish explore RTFM complaints more. ''Your pride is blinding you to a valuable tool. A tool that is just as applicable in programming central to databases as to network sockets.'' Perhaps if you used databases more, you wouldn't need network sockets and files as much. Like I have said many times, CompaniesHireLikeMinded. In a DB-centric shop, there are less data files and less socket programming to deal with. Maybe I live a sheltered life among RDBMS shops; the "Hunchback of Codd". Why not, DB's are wonderful tools that abstract a lot of "structure and attribute handling" stuff that others appear to reinvent from scratch over and over, and inconsistently also. Showing how your techniques allow you to tack DatabaseVerbs onto arrays, files, and sockets is an example of this. The next shop will tack on a whole *different* set of verbs/operations (and still not relational). You guys keep performing the same dance to different music without realizing it. -- AnonymousDonor (TopMind?) [If you like the database so much, then you should love the fact that HigherOrderFunction''''''s will let you wrap a database api around anything, even when it's not database related, and can't be. You work on recordset's all the time top, why don't you have a set of common higher order functions that apply to recordsets? If you did, you wouldn't have any patterns in your code, because you'd be able to do just about anything with a one liner. You should at least wrap this up...] myMap1 = loopSetupFoo([local vars as parameters]) while (myMap2 = getNextFoo(myMap1)) { doStuffTo(myMap2); } into function forEach(rs, aFunc){ while(row = getNextFoo(rs)) aFunc(row); } [so that you can do... ] forEach(loopSetupFoo([local vars as parameters]),function(each){ doStuffTo(each); }); [and avoid creating variables and having to pump out the same while loop a thousand times, it may not look like much, but over time, it saves tons of code, especially when you get all your basic tricks and idioms wrapped up into HigherOrderFunction''''''s so you don't have any patterns left in your code. Given the way you program... I'd do this..] function forEach(sqlString, aFunc){ var rs = runSql(sqlString); while(!rs.eof) aFunc(rs); } [so you could just do...] forEach("Select * from someTable",function(each){ Response.Write(each["field"]); }); [at the very least, it makes working with resultset's much simpler and cleaner, no setup code at all.] (Perhaps above should be moved to QueryAndLoop.) Huh? Tell me how a database would help with creating: * Software that tunnels arbitrary network connections through firewalls over SSL * A gateway between IRC chatrooms and AolInstantMessenger * A RayTracer * Software that receives financial transactions direct from the brokerage over FIX protocol * OperatingSystems * compilers and VirtualMachine''''''s You work in an RDBMS shop; fine, if it puts bread on the table, good for you. But don't assume that everyone else faces the same problem domain that you do. Those are all projects I've worked on, either at various employers or in school courses. Only 2 others (FictionPublishingExample and an intranet calendar application) benefitted from an RDBMS. Only 1 of the above (the ray tracer) did not benefit from objects. * I never claimed that what is good for my domain is good for all domains. The FP zealots are the ones who made large, general claims (at least that is how they came across to me). If our differences of opinions are based on domain needs, that is an important lesson in itself. ** Would that it was true. You sure think you can fix my weather application with just a few tables and case statements. ** ''I don't know yet. I don't have enough details from you. You admitted it's hidden via a non-disclosure agreement. Thus, it's a poor example to dissect: the frog is glued shut. -t'' ** You do what you can, use what you know, but if you stop learning and trying new concepts, you're going to be left behind. Don't be so afraid of it, Top. If you're not afraid, what is it? Don't accept our anecdotal evidence, make your own. ''Try making a simple application'' using a language that supports them. This isn't a huge time investment, it's a week at most. This is worthwhile for anyone who wants to grow as a professional software developer. ** ''I don't see very many of you busting your butts to try table-oriented techniques on more things. Anyhow, you guys made the "big difference" claims, and I have asked you to show them off with "challenge #6". Don't say I did not give you an arena to showcase your claims. If they don't apply to that example, at least make a decent effort to explain why it is an exception.'' ** Actually, I've used TOP recently and heavily to write a wiki. It's purely an exercise for my brain, not a project I intend to use anywhere, but I wanted to work with tables in a real web application to round out my knowledge a little. I made it very table-oriented. Personally, I didn't like the resultant design, but I feel a lot more comfortable with tables now (hence my heavy leverage of tables in the weather application). I give an example of how HOFs could be used to address Challenge #6. That lisp example isn't full-featured (does not meet all requirements yet), but already does the basics. And that's a rough, 30 minute stab at it. ** [I use tables every day, I even use control tables to specify user/field security for my applications. I use databases whenever I need to store data/meta data. I use tables to fake multiple dispatch in weak OO languages when I feel the need, but none of that removes the need for HigherOrderFunction''''''s.] ** ''What didn't you like about your TOP design? Is it something about specific tools or an inherent problem of TOP?'' *** Please, Top, let's keep on topic. I didn't like it because it's still a rough patch in my experience, and i feel I could have made it more elegantly with more familiar techniques. The experience was educational, and such experiences seldom lead to quality products. *** ''I am just asking questions, not making accusations. Maybe I could offer some tips if I knew more about the problems.'' Most of us like objects and HigherOrderFunction''''''s because we've found them widely applicable to the kind of work that we do. If they're not useful for you, fine, don't use them. But that doesn't mean that the rest of us are automatically ReinventingTheDatabaseInApplication. -- JonathanTang [His pride has kept him from learning anything in the two years I've seen him posting.] I am surprised you used the word "pride" instead of "arrogance". Your diplomacy skills are improving (slightly). The bottom line is that your claims and examples don't ring very applicable to the IT world as I observe it. They might save a line or two of code here and there, but nothing revolutionary. You keep using words such as "clearly better" and "major improvement". [Any language that uses HigherOrderFunction''''''s, IS clearly a major improvement over the exact same language without them, it's that simple top, it has nothing to do with what domain you work in. It's an extra tool in the box that has a huge effect on how you program and how much code you write, anyone who's actually used them, will agree with that. You are the one that doesn't have experience with them. You can't really speak intelligently about something you don't ever use now can you?] I am sorry, but I have yet to see a "killer example" from you. I will grant you speed and compile-time checking over string-centric techniques for things such as creating one's own control blocks, but I have not seen any convincing code-reduction examples from you guys. Further, you seem to be solving problems that I don't encounter very often. In summary: Shown: * Faster execution * More compile-time checking (some hold such dearly, others don't give a flying...) Not Shown: * Less code * High relevancy in database-centric shops Now, where do you disagree with this summary? -- top There were clear examples given of how it creates less code, you just didn't understand them. '' '''One does not have to understand code to know that they are not shorter.''' I can do the same thing with Eval and other dynamic language tricks. If the advantage is something other than code size, then you are not being clear. Your reuse claims are for things that I don't use very often in the first place. You are trying to sell a refrigerator to an Eskimo.'' There were also examples given on how it would help "your" database patterns, making it very relevant, you also didn't understand them, your summary isn't accurate because they were shown. You don't see a killer example, because you don't want too, it's that simple, you don't want to learn anything, you just want to defend what you know. I'm not explaining it anymore, you don't seem to have the willingness to learn anything new. ''Either it is less code or it is not. Are you implying that I am hallucinating code size???? You appear to be trying to change the subject away from code-size as a metric. -t'' ''I will consider reuse claims, but only in the context of a semi-realistic biz example where I can make some informed assessment of probabilities of the re-use scenario. Some of the scenarios given here are about making networking tools and device drivers, etc. I'm not challenging those domains because I have no experience on how likely one is too need to reuse X for B after using it for A. (And I can usually explain the reason why something is unlikely in the biz domain, such as describing the usual alternatives or other factors that complicate the reuse of using something as-is.) -t'' ------------- To clarify it for me, does the HigherOrderFunction enable the second example? * Less than HigherOrderFunction (First example) Keys = Select.Data(Entity.Name) Loop Key = Remove.Field(Keys); * Removes Key from Keys Until Key = EOF ** Code for each key Repeat * HigherOrderFunction (Second example) Keys = Create.Array.Iterator(Select.Data(Entity.Name)) Iterate(Keys, Get.Object.Code("** Code for each key")) * And I notice this becomes a one-liner Iterate(Create.Array.Iterator(Select.Data(Entity.Name)), Get.Object.Code("** Code for each key")) I have taken a copy of the above question to HigherOrderFunction wiki - it's not really a question about SQL --PeterLynch Close, see if I can clarify with some pseudocode showing both sides of the issue. First, we want reusable iterations over lists, of anything, even recordsets that come from databases, so, forEach takes any list, and a one arg function and applies it to each item in list, that's the HigherOrderFunction function goobleGorp(aList, aFunction) <-- just the method signature for simplicity function boobleGoobles(aGooble) <-- specifies amount to raise employees, notice anAmount as input goobleGorp(gobbleList, <-- call to goobleGorp with list from anywhere, even db function(each) <-- anonymous one arg function, required by goobleGorp, applied to each gobble each.wobble+=aGooble; <-- each.wobble += aGooble, notice aGooble is from parents scope end) -- creating a closure, preventing us from needing to pass aGooble end -- to the anonymous function, it is captured for free, allowing -- goobleGorp to always use the same method signature for its function -- argument making it totally generic and reusable, while still -- completely flexible and simple this would commonly be formatted as a one liner function boobleGoobles(aGooble) goobleGorp(gobbleList,function(each){each.wobble+=aGooble}) end ''Although not applicable in all cases, the above can be entirely done in SQL'' query("update Emps set salary = salary + $amt"); [It can't if salary is a complex computation that involves much business logic, don't try and apply sql, it isn't applicable to the sample, and is rather silly to think I don't know how to do a simple update statement, or that an update statement is in any way comparable to the above sample.] Every time I suggest that your examples "assume too much regularity (simplicity)" to fit real problems, you get on my case. Now you are doing the same thing. -- top [No, the whole point of that sample is to show you how to achieve regularity by avoiding parameter passing with implicit closures, enabling generic reuse of functions. I'm showing you that your objection, "assume too much regularity (simplicity)", is incorrect. It was not about doing a simple update to salary.] I mostly just see query language reinvention up there (ExpressionApiComplaints). You seem to have difficulty finding an example that does not rely on arrays and does not reinvent query languages or databases. There seems to be a pattern to your choice of examples. -- top [Here, I fixed the context so you can grasp it without thinking about employees and saleries] I think the change will confuse readers because replies and code comments are based on the original example. Besides, examples are easier to relate to when they have something specific instead of Gloobs and Foos. [ now read it really slowly, until you figure out that the example is about how to pass paramaters to functions, without explicitly passing them.] I can do the same thing with languages that allow functions to inherit scope from the caller. [You seem to have dificulty reading code and understanding clear explanations. Look at the aGooble variable, see how it's used by the goobleGorp HigherOrderFunction, without being passed to it. You always complain about not being able to achieve uniformity, well this is how we do it. It's not about arrays, it's not about query languages; it's about how to do something that saves you a lot of work when programming, "Not passing variables".] Note that one can do something similar with Eval: function looper(theList, theExpression) { for i = getNext(theList) { eval(theExpression) } } Remember that some languages let a routine inherit the parent's scope, while others (TCL) have multiple kinds of Eval such that one can optionally execute the expression in the caller's scope. But again, in practice I don't find a practical need for such very often. The regularly to take advantage of it does not exist often enough. And it is often possible to simplify the loop if the loop is used often. That way one can put the loop inside the functions instead of the other way around. Your code-saving claims seem to assume wordy loops. Here is another psuedoc-doe version that makes scope inheritance optional: function looper(theList, theExpression, scopeLevel) { for i = getNext(theList) { evalOnStack(scopeLevel, theExpression) } } Here the scopeLevel is the stack level, similar to the "upVar" TCL parameter. Zero is the current stack (inside of "looper"), 1 is one level up. ---- ''Top, you're not entirely incorrect. We have been giving you lots of examples involving arrays and query languages. That's only because you demand such examples, though. You say all others are unrelated to your sphere of interest. You can't complain about the playing field when you're the one who chose it.'' ''But, that aside, it's still worthwhile to reiterate our point. Using HigherOrderFunctions allows us to extend a language in ways that the desingers have not forseen. Kind of like macros, but safer and applicable in different areas. By using them, we can extend our languages via libraries in very interesting ways. There's a real advantage to this. The ability to safely extend the language and compose logic is a powerful and desirable one. '' ''Imagine if you could do it in SQL. You could extend your language in new directions to specifically address your database domain. I know the idea sounds attractive to me. -- dlf'' I will believe it when I actually SEE it. I am from the MentalStateOfMissouri. -- top [You have seen it... and you screamed RIGGED, yet it wasn't rigged, that 10 sample function above clearly demonstrates that you are wrong.] ''Then stop wasting time and test them out. Free test-drive, Top. JustDoIt. -- dlf'' So you are admitting that you cannot document in text your claimed benefits? If I ever figure out what the hell is so great about what you are bragging about, but have such a hard time articulating the benefits, then spank my sorry tongue-tied ass. ''Top, we've shown you some pretty damn compelling features of HOFs. You just refuse to see them as such. '' You are not even clear on why they are better. You would make a shitty trial lawyer. You would fail in a court of law (barring use of anecdotal evidence). ''Whenever we give a specific example, you seem to lack the knowledge to give it context. Whenever we enter into a domain you're more familiar with, you shout that we should just use tables and SQL. It's frustrating, you are the penultimate HostileStudent.'' Yeah, that's right. I am the bad guy and you are the good guy, with brilliant, airtight evidence. ''We're at a point where we're unsure what you would consider "compelling." I know what '''I''' consider compelling. '' Step 1: Declare exactly what you are about to demonstrate Step 2: Demonstrate with code or code examples. ''My weather app was compelling to '''my''' peers.'' * I am not your peer, cannot see the actual source code and requirements, and it is possible that your peers are idiots with RDBMS and Eval. I cannot inspect these issues, so have to take such claims with a grain of salt. See HowToSellGoldenHammers. [As far as I can tell, there's nothing that top will find compelling to prove to him, anything that he doesn't already know. If he doesn't currently use the technique, then as far as he's concerned, it's useless, or so it appears.] I have had code complimented also. We don't need yet another anecdote battle. '' However, you seem to lack the context/experience to read the code and you won't listen to anectodal evidence (for good or for ill), so we're rather out of options. The best thing to do is to AnswerTheQuestionThroughExperience.'' ''Quite frankly, this is best anyways. We can tell you they're great all day, but until you really experience them you may not be able to identify with the benefits. Computer Science's history is filled with people being unable to immediately grasp the benefits of new features, but time showing they were indeed a good idea. You yourself claim you're ahead of the industry by being TableOriented. Is it not possible people are ahead of you in other aspects of the discipline?'' ''Just because you do not see it does not mean evidence is not there.'' There is very little to distinquish between a '''religious fanatic''' and a GoldenHammer fanatic. '' By taking a closer look, on your own terms, I am certain you will find your take on what's so great about HigherOrderFunction''''''s. -- dlf'' ---- Top, for all your whining about incomplete evidence, your argument thus far has been weak, at best. ''The burden of evidence is on you, not me. Everything you presented can either be done with eval or dynamic function scope, and/or something that is not something I need in my domain. (Some I did not bother with a string/eval solution because they are demonstrating needs I don't encounter. But one is probably available.)'' We have shown you code that gives a demonstrable savings and increases flexibility. ''Show me more realistic "flexibility" scenarios, not foobar ("lab") examples. Foobar examples only show what is possible, not what is practical.'' Your response has been to say, "No, it doesn't." Even when we show how your approach would scale, versus the HOF approach (which turned out very bad for your approach), you still said, "No it doesn't." ''By "scale", are you talking about speed? I gave you machine speed credit already. If you mean the case list, you did not give enough details.'' At some point, Top, we've exhuasted due dilligence. We've told you. We've shown you. You demanded code for that "Example 6" and I provided a skeleton that did what it asked for. You never even responded to it. I've shown you requirements that your techniques '''cannot cleanly address''' and you've ignored that too. ''I made a complete, runnable example. I cannot compare a small skeleton with a complete running example.'' Code reuse on a grand scale. Modular design. Language extension. Pattern Simplification. HigherOrderFunction''''''s give you these benefits. We've shown you examples. You don't even address the issue directly, you keep reverting to tables. ''If tables avert the need for them, then tables avert the need for them. What else can I say?'' The issue is not about TableOrientedProgramming, it's about if HigherOrderFunction''''''s are an improvment on languages without. Tables are orthogonal. Heck, even closures are orthogonal. ''Maybe not. I originally thought so also, but your examples seem to be either reinventing databases or automating things that databases already provide, re your "array query language".'' You've given me Example 6 and I've met it with code. What more do I have to do? How many examples can we lay out side-by-side to show that HOFs result in clearer, shorter, faster code? -- dlf ''I would like to see code of a full, runnable example. If you don't have time for challenge #6, just say so and we will leave it at that.'' Maybe we should make a little competition. My version vs. yours. We'd have to talk about ground rules, but it would be interesting to see who could make a smaller, faster, clearer solution. I'll pit my toolbox against yours any day of the week Top. -- dlf ''What kind of ground rules do you have in mind? It already runs. The screen-shots on that page are of real output. If you have any questions about it, feel free to ask. And, I will try not to slip into "RTFM" mode, unlike Mr. Weather Example. (Note that I did not use any Eval tricks in that example, although maybe doing such could reduce the code-size a few percent.) One decision you may have to make early is whether you will use a database of some kind, or say s-expressions.'' ----------- I am pasting back a copy of the original "employee raise" code examples, if you don't mind. -- top function giveAllEmployeesRaise(anAmount) <-- specifies amount to raise employees, notice anAmount as input forEach(employeeList, <-- call to forEach with list from anywhere, even db function(anEmployee) <-- anonymous one arg function, required by forEach, applied to each employee anEmployee.salery+=anAmount; <-- anEmployee.salery += anAmount, notice anAmount is from parents scope end) -- creating a closure, preventing us from needing to pass anAmount end -- to the anonymous function, it is captured for free, allowing -- forEach to always use the same method signature for its function -- argument making it totally generic and reusable, while still -- completely flexibe and simple this would commonly be formatted as a one liner function giveAllEmployeesRaise(anAmount) forEach(employeeList,function(anEmployee){anEmployee.salery+=anAmount}) end ExBase version: USE empl REPLACE ALL salary WITH salary + raiseAmt SQL version: UPDATE empl SET salary = salary + @@raiseAmt // parameter syntax varies per vendor ---- This is probably showing my ignorance, but how close are StoredProcedures to being HigherOrderFunctions for databases? [Stored procedures are the database version of functions. If you could pass a stored procedure, as a paramater to another stored procedure, including it's execution environment(in other words, not it's name as a string), then it'd be a HigherOrderStoredProcedure, or if a stored procedure, could return another stored procedure as its result, not it's name, but an actual executable procedure as the result, it'd be a HigherOrderStoredProcedure, but as far as I know, that doesn't exist.] * Stored procedures are generally executed by name in most API's. Thus, a name can dynamically be determined/calculated at run-time. In this sense, it is similar to an "Eval" solution. But, there is generally no way to "grab" any context/scope outside of passed parameters. ------- Re: Why do my enemies always talk about "real behavior"? [I hope you realize how humorous this statement is, Top] I don't see the humor. You guys keep talking about the "power of behavior", but never demonstrate anything real. I only see reinventing DatabaseVerbs for each and every class, or MentalMasturbation that does not apply to real problems. Related: IsDeclarativeLessExpressive ----------- Some people are claiming that one or more of the FP code examples above are "significantly smaller" code-wise. Ignoring speed and "elegance" (cough), as far as I know, '''no FP example shrank the code size more than about 5%'''. If there is one, I missed it and would like it pointed out please. Note that some have been reworked since the original. I keep thinking we have closed out the size issue only to have it pop back up (such as ChallengeSixVersusFpDiscussion). I want to nip it in the bud. -- top ---- One more point in favor of HOFs vrs eval - code executed by eval is a string, and as such isn't part of the semantic structure of the problem. I know that Top is interested in exposing program structure and abstract ("syntaxless") language structures - evaled code can't participate in these techniques. HOFs, as a language construct, are certainly superior in this regard. ''HOF's are just references. One can reference functions by name or by some internal pointer. Using an internal pointer is a violation of OnceAndOnlyOnce if the name will suffice. Yeah I know, sometimes ugly pointers just run faster. -- top'' [A name is also just a reference. And if you think OnceAndOnlyOnce means you can't use references, then... well, I don't know what to say. It's like arguing with the Flat Eearth Society.] * Social intimidation aside, I still have not heard a good written justification (outside of performance) for having both a name and a RAM pointer for a function. Why have two primary keys/IDs? That ain't KISS. They are most certainly not just references. Many HigherOrderFunctions are curried (partially-bound) functions, with some elements bound to the function and others left free. Many others, even if not curried, are defined only once and used only once at their place of definition - introducing names for these can be a big headache for programming in functional style. One could argue just as easily that use of a name violates OnceAndOnlyOnce... or one could sidestep the whole silly argument by decoupling lookup with dereference. (The latter model is counter to the RelationalModel, I suppose - but tough.) ''Generally functions that are only used in one place are fairly easy to replicate, replace with other techniques, or are unnecessary.'' If "If-else" is also a function then the "if clause" are only use in one place, how do you replace it with other technique? Or how is "If" unnecessary? ''Why would you want to roll-your-own control structure for a one-time use?'' ---- About using SQL query for deletion. Has anyone realize that the SQL DELETE, UPDATE, SELECT statement use a form of Higher order Function? DELETE from person where person.name = "john"; is actually equivelant to lisp (DELETE :from 'person :where (lambda (person)(= (name person) "john")))) where one could put in macro to make it (DELETE :from person :where (= (name person) "john"))) Now Top, I want you to realize that the benefit of using DB is there because SQL syntax try to be familiar with the usage of Higher order function. However, SQL does not have the full power of HOF so it just provides you with fix capability to do it. That's why you cannot extends SQL to have INCREMENT age IN person WHERE person.name = "john" You can use UPDATE for it, but thanks to Codds for that. Because Insert, Update, Delete is so universal (it's like the word "Do" "Act"). Seeing this similarity I don't know why top hates too much about Higher order function, when he himself uses it all the time. Imagine You don't have a SQL DELETE statement. What would you do to get the same terseness as SQL you have now? ''Please clarify. I am not understanding your point. Use a stored procedural or function if you want an "increment" function. Add, change, and delete are fundamental building blocks, and that is why they are included. Arguing that we need HOF in case the SQL inventor skipped a beat is an odd argument. DrCodd did not create SQL, by the way. Nor is SQL the pinnacle of potential query languages. -- top'' SQL is actually a functional language, in that a statement can't have side effects while it is processing. (An update statement modifies tables only after all other processing has completed.) Joining in another table to a query is very much like creating an anonymous function. For example (please forgive my syntax mistakes as I am neither a SQL nor lisp expert): UPDATE employee SET salary = basewage + 0.05*SUM(sales) FROM employee JOIN orders ON employee.emp_id = orders.emp_id WHERE orders.qty > 1000 This might be expressed in lisp like: (UPDATE :from 'employee :key 'salary (+ (base-wage employee) (lambda (emp-id-val) (SELECT (* 0.05 (sum (sales employee))) :from 'orders :where (and (= emp-id-val (emp-id employee)) (> (qty orders) 1000))) (emp-id employee))) The point here is that the join and SUM is effectively creating an anonymous function. ''Perhaps one should distinquish between saying a query language *is* a functional language and saying a query language can be implemented (or implemented better) in a function language. I am not sure the debate here is how best to implement a query language. However, things may change if query languages allow user-defined or DBA-defined functions. MS-Access will allow such when defined in VBA, IIRC, but they are not recognized in aggregate operations though.'' --------------- '''It's not about me''' If you want HOF to be better supported and used more in languages and applications that you may one day be maintaining, then it '''benefits you''' to promote the value of HOF to one of the largest domains: custom business applications. If you can demonstrate to other readers besides me in a semi-realistic coded example (and perhaps realistic change scenarios) how the code and/or maintenance steps are shorter/fewer, then the world will accept HOF's more and languages and tools and training materials will better support them such that HOF's will be more common in YOUR work world and the work of your colleagues. Selling the idea to meteorologists isn't going to go very far. -t ''If I want to promote HigherOrderFunctions, I'll do it where the audience is genuinely interested in learning new techniques, and not operating under a closed-minded agenda that automatically and categorically deprecates everything that isn't some form of TableOrientedProgramming.'' I've many times tried to envision how they can improve custom biz apps, but so far haven't seen them significantly improving things. SystemsSoftware examples don't extrapolate across the line because certain regularities in that domain are rarer in biz apps because the biz rules are not shaped and managed by engineers (for good or bad), but those less interested in clean rules and logic. I'd like to see a coded example in the domain. If you are unable to provide such, just politely say so instead of calling me "closed minded". It just degenerates the discussion. I am not closed minded. Given good evidence, I will change my mind. ''I am implementing AnonymousFunction''''''s and HigherOrderFunctions in the RelProject, which is an alternative to SQL. The role of SQL in "custom biz apps" should be self-evident. One reason for doing this is to provide a simple, efficient, and effective way to support user-defined aggregate operators (in addition to the built-in SUM, COUNT, AVG, etc.) for use by the SUMMARIZE relational operator. This is roughly equivalent to being able to define custom aggregate operators (in addition to the built-in SUM, COUNT, AVG, etc.) for GROUP BY queries in SQL. A second reason is to provide an alternative to the EVALUATE mechanism -- which is a typical string-based "EVAL" implementation -- that is considerably more type-safe. The value of these in "custom biz apps" should also be self-evident. If it isn't, there's no point in continuing this debate.'' ''That said, maybe the particular domain of "custom biz apps" you're developing do not require the level of customisation, re-use, and type safety typically provided by HigherOrderFunctions. If so, that's fine -- if you don't need them, you don't need them!'' That is SystemsSoftware. I don't dispute the value of HOF there. And it's not that biz apps don't need re-use, it's that the re-use is more '''irregular'''. The kinds of influences on each variation is greater such that the boundaries are rougher and more unpredictable. It's hard to define stable GateKeeper interfaces up-front that work in new, unexpected situations without overhauling. Abstractions have to be loosey goosey to flex sufficiently if you want reuse (or least I haven't found a way to get tight abstractions to flex in the biz environment). -t ''I'm not sure I'm following you here. When you write "that is SystemsSoftware", do you mean all the SQL in your "custom biz apps" is also SystemsSoftware? Or do you mean that if you need to write a custom aggregate operator -- e.g., a statistical operation like standard deviation that might not be provided by your SQL DBMS -- that the custom aggregate operator is SystemsSoftware?'' My classification would depend on the details. If one had to know specifics of the innards of the database engine to write a custom aggregator, then I would call it SystemsSoftware. If it's designed such that it's more like a DBA defining a view, then I wouldn't. In the later case, the "objects" being referenced are the usual "visible" objects, such as tables, views, columns, indexes, etc. Of course, it may be in-between, and each DB engine may do it differently. ''There's no reason why a custom aggregator should need to know the specifics of the innards of the database engine. The objects being referenced would be tuples/rows, attributes/values, and relations/RelVar''''''s/tables.'' And I'm not convinced that a HOF is the best way to define such, but would like to analyze a specific scenario if you can provide one. It could be defined something like this: // Sample fizBin func aggr_fizBin(theValue, &collector) { if (! typeOf(theValue) in ["number","integer"]) { sys.throwStdError("this aggregator operator must be numeric"); } else { collector = collector + sqrRoot(theValue); } } ''That's a reasonable definition. Now imagine that some significant aspect of your aggregation function needs to change depending on row values, so that what you effectively need is n different functions, where n could vary between 1 and the quantity of rows in a given group of rows. I'm sure you could do it without HOFs, but HOFs generally make it easier to express.'' * What's a business scenario for such? We can envision call kinds of "lab" examples where such ability is "interesting", but I cannot turn it into common practice usage in the biz domain, either because it's not a common need, or I am not smart enough to envision such for the domain. The fact that other WiziZens have been unable to show such even though the challenge has been around for several years suggests to me that it's the former. * ''Sales forecasting often involves choosing from a variety of regression techniques that may vary depending on product sales history, product category sales history, arbitrary decision ("let's try this on product category "), seasonality ("we only ever sell those shortly before Xmas"), the number of products in a group ("if there's only a few of them, don't bother", or "if there's less than ten, calculate it like ") or all of the above in some computed combination. This is a scenario where selection or affectation of algorithm is something we may wish to perform inside an aggregate query, do it efficiently (there may be thousands of products with millions of historical records), effectively, and be able to re-use the code to deliver results quickly. E.g., when "I need the new forecasts for my 4pm meeting!" is received at 3pm.'' * That's arguably outside of "custom business application" and in the domain of "statistical analysis". Statisticians are usually hired (or borrowed) to work that kind of stuff at the places I've worked. And they tend to use pre-packaged tools such as SPSS, SAS, and "R". And "Eval" and other techniques have not been ruled out, except maybe for the "speed" issue. I suspect that each regression technique is too varied in terms of parameter quantity and output quantity/type to be manageable via simple uniform plug-ins anyhow, but I am not a stat expert such that I'm speculating a bit here. Swapability generally requires a uniform interface. -t * ''I'm sure it's possible to narrow the definition of "custom business application" until nothing remains in it, but... What do '''you''' define as a "custom business application"?'' * I'll LetTheReaderDecide if I'm being unreasonably narrow. For the sake of this discussion, how about I define it as a project in which a "generic" custom business developer would typically be a key part of the project if they need to hire/rent from the outside. If they are more likely to hire/rent those with domain-specific degrees, such as math/statistical degrees, then it wouldn't qualify. (Granted, a small org may use a biz developer, but they also wouldn't typically ask for a wide variety of regression techniques in that situation. If you want the Cadillac of statistics, including lots of regression approaches, instead of the Chevy, then you hire statisticians, not a "generic" biz developer.) -t * ''As a "generic" custom business developer for many years, I certainly had to apply specialist knowledge on many projects. That included employee scheduling, payroll, medical care plans, statistics, forecasting, accounting, differential and integral calculus, psychology, marketing, and so on. Sometimes the client was able to provide sufficiently detailed information that I could research the specialist knowledge and meet requirements on my own, sometimes I hired expertise. This was all "custom business applications" because it was custom, it was for business, and the result was applications. Would I have preferred using functional languages instead of xBase, C, C++, VBA, VB, PHP, Java and Delphi? Absolutely. Had sufficiently mature and cost-effective FP environments existed at the time, I would have used them in a heartbeat. If I were to go back into custom business application development today, I would be evaluating Erlang, F# and Haskell from the start. The control over side-effects and easy support for concurrency are alone worth the price of admission.'' * Well, that differs from my experience. I was asked to install SPSS for 4 statistical experts, for example. Most biz developers don't even remember their calculus unless fresh out of school. Anyhow, why do you have to turn to regression for an example? Why not something clearly more business-y? * ''What could be more business-y than sales forecasting? General ledger? In the trivial case, G/L is a bunch of GROUP BY / SUM queries in SQL and a gaggle of straightforward CRUD screens and reports. Not much call for FP there, let alone HOFs; in terms of language, xBASE or COBOL would easily suffice. (Noting, of course, that SQL is a functional language!) However, what if the requirements demanded a distributed G/L with graphical dashboards supporting arbitrary numbers of live-update end-user custom reports accessed via a Web interface, live update of Excel spreadsheets, and unlimited scalability? Now we're soundly in the territory where functional programming could well be of benefit. Old-skool xBASE or COBOL wouldn't begin to scratch the surface.'' * Sales people use road maps also, but that doesn't make road maps a business app. Why "should" they NOT use SPSS or SAS or R? That's what statisticians are trained to use. As far as the second, flesh out the detail specs and we'll see. * ''A good reason for not using SPSS, SAS or R is that the sales forecasting often needs to integrate tightly with existing inventory or manufacturing production management systems, right down to having a seamless UI. So, whilst prototyping may be done in SPSS, SAS or R, the prototype may wind up being re-implemented in C/C++/C#/Java/Delphi/VB/VBA/PHP/Python/Haskell/F#/whatever. As for "detail specs", how much more detail do you need? I've built successful products with less specification than that above.'' * That's fine, but that also means that the statistical tests become '''mostly settled''' before general integration because there is a prototyping stage. HOF's only significantly help if a lot of tests come and go with frequency. Medium and large organizations generally have a management approval and sign-off stage because they are the ones using and responding to the reports. New stat tests/reports wouldn't typically be coming and going willy-nilly. * ''It matters not whether they vary a little or a lottle. HOFs provide a simple, fast and type-safe way of expressing dynamic functionality. You can achieve a certain amount of the same capability via EVAL and the like, but at -- as already noted, repeatedly -- the expense of complexity, delimiter awkwardness, repeated run-time compilation and/or syntax checking, and limited static type checking.'' * Where does the dynamicness come into play exactly? They wouldn't likely change that often once the prototyping on SPSS etc. is complete. * ''"Dynamicness" comes into play when the generated function definitions are data-dependent. For example, I recently created a higher order function called makeLoader in Javascript to dynamically create a function that can be passed to window.setInterval (itself a higher order function that accepts a function to be executed at specified intervals), in order to repeatedly reload a collection of divisions via AJAX using my http://dbappbuilder.sourceforge.net/ajax.js AJAX library. It essentially looked like this (minus some irrelevant complexity specific to the problem, and modulo possible mistakes -- I've re-created this from memory), and note that makeLoader is a higher order function that returns a function which invokes xmlhttpPost to launch an asynchronous AJAX transaction that will invoke an anonymous function that calls updateInnerHTML to change a portion of a Web page in the browser when the asynchronous AJAX transaction completes:'' function updateInnerHTML(id, str) { element = document.getElementById(id) if (element != null) element.innerHTML = str } function makeLoader(url, element) { return function() { xmlhttpPost(url, "", function(str) {updateInnerHTML(element, str)}) } } for (i=0; is are, yes?) is, as far as I know, part of a communications system that uses each div to show status of a user, but it might be the status of a device because there was mention of that in the discussion. Maybe it's for both. I don't know and it doesn't matter. I was tasked to address the situation where a large number of divs need to be updated asynchronously using AJAX so neither the page nor some large portion thereof need to be bulk-reloaded, and that's what I came up with. It could as easily be something to display live stock portfolio updates, or monitor live status of sales points in a retail store, or keep track of live shelf loading in a warehouse, balances of accounts in something, whatever. Etc.'' * ''Why not put everything in one routine? That means launching the AJAX transactions must run serially. One delayed launch -- due to a network or server hiccup -- will delay all that follow it. Anyway, it's merely one illustration of the facility of HOFs.'' ** You didn't state separation as a requirement. I cannot read your mind, dude. There will be overhead with separation of processes such it should not be a default assumption. ** ''I assumed you'd be familiar with the usual AJAX requirements and behaviour.'' * ''How would you have designed Javascript's "timer"? How would you have changed the AJAX API to make it better without HOFs?'' ** As far as timers, I'd probably make them more like an OS scheduler: a "list" (table) of "launch strings" along with attributes and time info. In short, a ControlTable. And it has the advantage of mixing and matching languages/tools if we need. The PowerOfPlainText. I know that's different than JavaScript's design philosophy, but this is not a language shoot-out, is it? ** ''Heavens.'' Also, generally for something custom, one doesn't have to worry about making it overly generic such that it can accept any type etc. It's for a specific purpose and type and the person paying you usually isn't asking for mass reuse-ability, but to solve a specific request. Note that in my many years in "the industry", I have never been asked for something like a custom aggregator. There are usually ways to compute such with existing tools, and they are probably too industry-specific to qualify as something generic. SQL's overly-large granularity often complicates such, but that's a debate over query languages, not HOF's. ''Whether you're a developer who's never needed a custom aggregator, or a developer who creates them on a daily basis, could come down to the difference between (say) being a developer of financial reporting applications that mainly present summaries of historical data, vs being a developer of sales forecasting applications that frequently need one-off regression techniques. In either case, the job is almost the same -- "custom biz app" developer -- but the requirements and what you focus on might be dramatically different.'' ''Of course, nothing should force you to use HOFs if you don't want to, but isn't it better to have a language with a specialised capability that you don't need than to want that capability and not have it?'' Somewhere there is a topic on the trade-offs of this. The more ways there are to do something, the more techniques a developer has to learn to work with in existing code. It increases recruiting and training costs, and is thus not a free lunch. Anyhow, I don't think this topic is really about what features to have in a programming language, but rather whether HOF's can "significantly simplify" typical custom biz apps, which is the primary claim being addressed if I am not mistaking. I don't dispute that once in a blue moon there may be a biz app they can simplify, but I just don't see evidence of ubiquitous improvement. It may exist, I just haven't seen it via inspect-able source code. -t ''I don't know why providing HOFs in a language would increase recruiting or training costs. Hiring and training Java developers didn't become more expensive when Java introduced anonymous inner classes. Hiring and training C# developers didn't become more expensive when C# introduced lambdas. These are language features that are handy, but not necessary -- at least, compared to (say) being able to evaluate expressions, which is absolutely necessary. You can effectively use these languages without even being aware of such specialised features, and neither language had them in their earlier versions.'' * If developers start using such features fairly often, then the maintainer has to know more about them. Without some careful survey, it's hard to say for sure either way whether it increases training time. Yes, it's "good to know", but personal improvement is perhaps a different issue than the economics of it from a business owner's perspective. * ''If developers start using such features fairly often, clearly they're finding them to be of value, which makes them worth additional training time. Of course, "training time" for programming is usually the time it takes to obtain a ComputerScience degree, so one feature represents no more additional time or cost than any other.'' * That's essentially the ArgumentFromPopularity fallacy. Many techniques are used out of habit, familiarity, boredom (MentalMasturbation), resume padding, job security, fad chasing, peer pressure, catchy cutesy graphics, etc. * ''That's pretty cynical. Are you basing it on evidence, or is it your opinion? I mean, have you '''really''' seen developers using anonymous inner classes in Java for any reason other than slightly simpler code, usually in event handlers?'' * If popularity is the main metric, then Windows (desktop) is better than Unix, and MS VisualBasic is the best language/tool. As far as Java, "Eval" can be used for dynamic event handlers in dynamic languages, which Java is not. * ''I think you've misunderstood my point. There are numerous features that developers use because they're effective and simplify code or make it conceptually easier to write. I see no evidence this has anything to do with boredom, job security, peer pressure, or anything other than a desire to muscle out a few lines of code with less effort than some earlier version of the same language or environment. Java's anonymous inner classes are a good example. So are C#'s properties, or more recently, C# lambdas. HOFs fall into the same category of features.'' * Let's see specifics. That's what I keep asking for, and for some reason you guys keep dancing around the edges instead of come to the center of custom biz apps. * ''I made my living developing "custom biz apps" for fifteen years, but I don't know what "center of custom biz apps" means. Maybe no one else does either.'' * List some specific projects, and maybe we can find one that we both agree is a "core" custom biz app. If you've been there a while, you should have a bigger variety than those that lean toward the math-centric side, like your prior attempts. * ''Sure. Here are some upon which I've been lead developer and project manager, and which I consider to be "custom biz apps": Accounts payable, order-entry and inventory for a large book store. G/L, A/P, A/R, employee scheduling, payroll, and nursing care plans for a national home health care organisation. Donation management for a large charity. Inventory for a national optometrist firm. Billing for a plumbing firm. Sales forecasting and e-commerce for a gardening seed company. Job costing for a window manufacturer. Tank volume calculator for a manufacturer of irregular-shaped fluid containers. Budget management system for a school division. Student records and timetabling for schools. Repair job tracking for a phone company.'' * I wish to see ''coded'' scenarios demonstrating the "significant" improvement that HOF's would add, not a resume. There are already anecdotes of such; that's not the evidence bottleneck. People would like to see for themselves the kinds of code reduction or hand/mouse movements reduced (or whatever the metric is). That way we can actually see the improvement "live" in action with our very own beady little eyes rather than have to take somebody's word for it. * ''You wrote "list some specific projects" in order that "we can find one that we both agree is a 'core' custom biz app", so I did.'' * That's just the first phase. Then you need to code up enough mock parts to create a coded scenario of HOF's making life noticeably better. I can't do that because I don't know where the betterment is (allegedly) taking place exactly and how the betterment is being measured. -t * ''We haven't finished the first phase. Aren't you going to look at my list and identify those that you consider "'core' custom biz apps" so that "we [can] both agree [what] is a 'core' custom biz app"?'' * Why not continue with the ones already introduced and described on this wiki from past topics? Why start over with yet more? If HOF improvement is common in biz apps, then we shouldn't have to keep scrolling through the app list before we find something. That implies a lack of commonness. * ''You're the one that wrote, "list some specific projects, and maybe we can find one that we both agree is a 'core' custom biz app," not me. The point in "scrolling through the app list" wasn't to find something particularly HOFfable, it was to agree on what a "'core' custom biz app" is.'' * I don't think I can give the details without giving away company specifics. Perhaps I can strip out org-specific stuff if I think about it for a good long time, but I cannot make any promises at this time. I have some possible ideas for a fancier demonstration version of Challenge 6 such that SQL-knowledgeable power-users can define more reports without programming. But if you see no value in Challenge 6 as-is, I doubt you'd want that. We already have the campus, payroll, and challenge-6 examples fleshed out to varying degrees to play with and which are familiar to WikiZens; is there nothing in those to demonstrate HOF's being "clearly better"? * ''I can't find the original description for PayrollExample, and neither CampusExample nor "challenge-6" involve any processing or UI. Without adding more fiction to the existing fiction, I don't see anything that would benefit from functions, let alone higher-order functions.'' * I'm not sure what you want then. Challenge 6 and PayrollExample are fully executable (if you install the necessary free tools). * ''"puts 'Hello, World'" is fully executable, but is hardly illustrative of either real software or the need for HOFs. Your examples are little more than, at best, vaguely business-flavoured descendants of "hello, world". If you want a reasonable demonstration of HOFs, you need a program that actually does something.'' * It lacks realistic "user input" as far as specific reports. It's like a report-writer, say Crystal Reports, without sample "NorthWind" reports, I will agree. Is that the problem? * ''It doesn't just lack realistic "user input", it lacks realistic output, realistic calculations, realistic validation, or realistic error trapping.'' * I'm not sure what you mean. What is "realistic calculation"? It has basic validation. I agree it's a "small shop" app that may not be bullet-proof, but often that's good enough, especially if the users are nearby such that they can ask you about something if it goes wrong. Often these kinds of things are used to prototype reports. A heavy-duty fancy-pants one would make for an example that's too big for most readers. But a simple one can still have a lot of use. Being simple also means it's easier to dig in the code to make custom modifications. There are commercial tools like CrystalReports, but one cannot change the source code of such. This, you can both because it's relatively short code-wise, and you have the source code. * ''Are we even talking about the same thing here? PayrollExample? It's a mish-mash of undifferentiated code-in-tables, ColdFusion tags, and scripting. It appears to have no notion of SeparationOfConcerns, no concern for maintainance, appears to be unusable, and worst of all -- seems based on no understanding of accounting and bookkeeping, let alone payroll. Is it actually used in production? Martin's approach is even worse. Have either of you actually had to maintain payroll systems for a living? Apparently not.'' * No, I'm talking about Challenge 6. I realize a "real" system is going to be a much larger code-base. However, such is probably too big to deal with as an example on this wiki. But something like PE can be used for a quick-and-dirty cross-check of the production system where extreme differences between the two are isolated and compared as a QA step. * ''I've looked at it, and I think HOFs could easily be used to generate menu items for injection into the menu presentation system, and to define the event handlers for the menu items. I don't understand "something like PE can be used for a quick-and-dirty cross-check of the production system where extreme differences between the two are isolated and compared as a QA step".'' * I don't dismiss the idea that it might be re-implementable with HOFs. That's not the issue. As far as the cross-check, one runs two payroll systems: the formal production system, and a simpler approximating system. If one system says Fred gets paid 5.2k and the other says Fred gets paid 2.8k, then one should investigate Fred's calculations closer. * ''I thought the whole issue was, in fact, that HOFs and functional programming in general didn't represent an improvement. I think they do. As for quick-and-dirty cross-checks, HOFs are perfect for that. You can inject a functions into the main processing unit that represent the context. For example, one function might be the formal production system, the other might be the approximating system.'' * Code up a demonstration example for a custom biz app scenario then. Show HOF's being great. I wanna see it. * ''I did, with the JavaScript example shown above. As I wrote, the "collection of divisions" (I assume you know what HTML 'divisions' or
s are, yes?) is, as far as I know, part of a communications system that uses each div to show status of a user, but it might be the status of a device because there was mention of that in the discussion. Maybe it's for both. I don't know and it doesn't matter. I was tasked to address the situation where a large number of divs need to be updated asynchronously using AJAX so neither the page nor some large portion thereof need to be bulk-reloaded, and that's what I came up with. It could as easily be something to display live stock portfolio updates, or monitor live status of sales points in a retail store, or keep track of live shelf loading in a warehouse, balances of accounts in something, whatever. Etc.'' * ''Of course, there's also HofPattern, which shows how HOFs address a general category of programming problems whether business or otherwise.'' * GUI examples have the problems described in HofPattern. What about taking a ''portion'' of the existing payroll or campus scenarios and flesh them out, being that they can be large in total? Can HOF's help there? We can create a draft schema that stores typical state info (data) and then add a module that uses that info with HOF's to improve the code. * ''Having developed and maintained a commercial payroll system, I'm inclined to avoid PayrollExample because I don't think either Martin or you have a clue about payroll. CampusExample is a database schema rather than a specification, but you can assume a campus system will need timetabling. If it needs timetabling, then HofPattern shows how timetabling -- replace "employee scheduling" with "timetabling" -- can be constructed with HOFs.'' * Like I said repeatedly multiple times, I don't have domain experience in automated schedulers. I thus cannot comment on likelihood of a given ChangePattern claim, which would be important to evaluating HOF's change-friendliness. I'll give you a ''default'' victory for automated scheduling just to move on to another scenario. Deal? (I suspect in practice they used packaged software, but won't squabble about that any further.) -t * ''Here's an example that you'll surely have encountered: Logging. Invariably, you will have a complex process that needs to log its progress for debugging or post-failure analysis. If you inject the logging function into your process as a function, you can inject a log-to-screen version for use during development, a log-to-file version for use during testing, and a log-to-email version for use in production. You'll also typically have a level of logging (e.g., fatal only, errors + fatal, errors + fatal + warnings, etc.) that varies depending on some external value, such as a user privilege or verbosity setting. Thus, it makes sense to have the logging function generated by a higher-order function that emits the appropriate logging function based on user privilege and/or verbosity setting and/or deployment context (i.e., development, testing, or production.)'' ** If I stick the log into a database, then I only have to query it. I get most of the usual DatabaseVerbs out of the box, including contention management. You can do that with HOF's, but you risk green-codding. ** ''If you stick the log into a database, then what records the "FATAL: Unable to connect to database!" error message that should be logged? Anyway, that's merely another example. HofPattern applies to innumerable examples.'' ** Then you have far bigger problems. ** ''No, what '''you''' have is a logging system that isn't effective. And, again, logging is just yet another example. Remember HofPattern: It's applicable anywhere you need to inject custom functionality into an otherwise indivisible algorithm. Of course, you can also inject an object (in an OO language, of course) but that's simply a collection of functions.'' * ''By the way, please don't evade these issues with dismissals like, "I suspect in practice they used packaged software, but won't squabble about that any further." You can always wave away a problem by saying "just use packaged software" or some such, but that's a cheap and nasty way to duck the discussion.'' ** My point was that if it's a feature commonly done by packaged software, then it's not likely to be a "custom business application", and thus off-topic. For example, if HOF's are great at making spreadsheet software, such as MS-Excel, that's nice, but irrelevant to a custom biz app developer because they are usually not called upon to develop desktop spreadsheet software. It's not hand-waving, it's reality. ** ''These days, almost everything is "a feature commonly done by packaged software". Custom business applications are usually custom not because they're unique, but because they optimise business- or user-specific aspects to a sufficient degree that their development is warranted. That's why my JavaScript example above is realistic and relevant -- it's characteristic of the sort of user-interface optimisation that is often used to justify custom development.'' ** Custom biz apps are often the process of gluing together packaged services. And the better packaged app builders tend to resemble programming languages, putting us in the same boat. ** ''If they resemble programming languages, then inevitably there will arise a HofPattern problem that warrants HOFs.'' ** Your JS example doesn't state a custom business need in English. ** ''See above. To wit: "I was tasked to address the situation where a large number of divs need to be updated asynchronously using AJAX so neither the page nor some large portion thereof need to be bulk-reloaded, and that's what I came up with. It could as easily be something to display live stock portfolio updates, or monitor live status of sales points in a retail store, or keep track of live shelf loading in a warehouse, balances of accounts in something, whatever. Etc."'' ** Why am I stuck using AJAX? Is your claim being tied to a specific tool/environment? Are you sure you want to do that? We didn't settle such in HofPattern. And note that I only have cursory knowledge of AJAX, so if your example assumes a lot of AJAX experience or lingo, then I would recommend reviewing it. ** ''AJAX is a way to update portions of a Web page without reloading the whole page, or relying on non HTML5-compliant facilities. It's merely an example, designed to illustrate a concept. The concept is described in HofPattern. My example assumes no AJAX experience or lingo; the intent is to show how using a HOF is superior to the non-HOF code that follows it. AJAX is irrelevant.'' ** I'm not sure your "alternative" is the only option. But let's first settle certain things in HofPattern. ''I haven't seen any claim here that HOFs can "significantly simplify typical custom biz apps" or that they result in "ubiquitous improvement" in that domain. If that's what you're looking for, you won't find it, but it's not what HOF proponents have claimed. Custom biz apps tend to be CRUD screens and reports; i.e., data in, data out, minimal transformation. If that's what you're developing, HOFs may be rarely desired, though the tools you're using may internally employ them extensively. As noted above, however, for the times when you do want them -- such as custom aggregation -- they're handy and effective and arguably simpler than the alternatives.'' See the opening "clearly better" quotation and the related parent topic. ''I've read that. I see no mention of "typical custom biz apps". Do you?'' * It's implied if they don't qualify the domain scope. There was no WHERE clause. And they had ample time to correct it later in the thread. * ''Really? Is it also implied that they'll be developing declarative statistical programs? Industrial control systems in assembly language? Programming FPGAs in VHDL? Expert systems in Prolog? If not, why do you believe "typical custom biz apps" are implied? None of these domains (explicitly) employ HOFs or are likely to benefit if they could, but I don't see anyone else complaining.'' * I'm not sure what your point is. They repeated the claim even ''after'' I asked about evidence for the biz domain. If they intended to modify the scope of their claim, I don't recall seeing it in writing. My interpretation was that the benefits are either universal, or if there were exceptions, they didn't apply to biz. Here's paraphrased version of how I interpreted the sequence: ** Mr. X: "FP is clearly better. You just have to study it." ** Me: "Is it clearly better in the custom biz domain?" ** Mr. X: "Again, if you take the time to study it, FP is clearly better." ** Although not explicitly stated, it appears they are "endorsing" it for biz also. That's my honest interpretation. -t * ''Do you mean HOFs? The discussion here is about HOFs, not functional programming in general. My interpretation is that here the implicit context is that HOFs are superior to not having HOFs in every case where HOFs may be warranted. Which is unquestionably true -- they're certainly superior to constructing and passing strings to EVAL mechanisms, which almost invariably require awkward string juggling including painful delimiter escaping, cause awkward issues with scope, and almost inevitably preclude pre-runtime type checking. HOFs give you all of the benefits of EVAL with none of the downsides; typically not even additional complexity because the code is substantially the same as that passed to an EVAL, but replace "EVAL" with some syntax that says "this is a higher-order function".'' * This sounds like part of the larger "compiled versus dynamic/scripty" debate. The issue of "type safety" is already pretty well covered by those other debates. If "safety" is the overriding concern, then use functions/methods. If dynamicy is the driving factor, then use Eval. Specific to Eval versus HOF's, Eval has at least two advantages over HOF's: ** Off-code storage of expressions. I can put the expressions in a database, for example. ** Power-users can enter them without re-compiling. See CompilingVersusMetaDataAid. -t * ''Off-code storage of expressions, so that power users can enter them without compiling, is entirely possible. See the "Anonymous Operators in Relations" section of my brief article at http://dbappbuilder.sourceforge.net/docs/AnonymousAndFirstClassOperatorsInTutorialD.pdf'' * Tutorial-D, ugh. The key though is cross-tool/language sharing and storage of expressions. You have to use a monolithic tool-set to achieve it. * ''The paper shows that it is possible in general, not that it requires Tutorial D, and there's nothing in it that necessitates a "monolithic tool-set".'' * For one, they'd have to toss their existing RDBMS. And how could the expressions be stored in Tutorial D but actually used (processed) in say Python or Java or Dot.Net? * ''Same way as with any SQL DBMS, and they won't have to toss their existing RDBMS because I'm working on mechanisms to tie in existing DBMSs via JDBC. But this is getting off-topic; debating the worthiness (or not) of TutorialDee and the RelProject belongs elsewhere if it belongs at all.'' * But that's sharing text-encoded expressions, not HOF's. You seem to be reinforcing my point that '''HOF's are less cross-tool-sharable'''. * ''Every type definition in the RelProject has a text representation that can be used to create that type. Every value in the RelProject has a text representation that can be used to create an instance of that value. Every operator (function) has a text representation that can be used to create an instance of that operator. These text representations can be passed around and stored freely.'' * If Rel can act "kind of" like a dynamic language, that's wonderful, but hardly unique. It still doesn't address the key issues. * ''Actually, it does. But, again, off-topic.'' ''I have noticed that when someone essentially says "feature is cool!" you frequently jump in with (essentially, and sometimes eventually) a demand that the claimant prove that feature results in simpler, easier-to-understand, and/or less code in "typical custom biz apps". I find this odd, because I don't think I've yet seen a claimant that "feature is cool" explicitly advocate its use in "typical custom biz apps". In most cases, though it's rarely clear what domain the claimant works in, it does appear to be clear that it's not "typical custom biz apps".'' * Sorry, but I don't see where I make that mistake. It's usually when somebody ''insists'' it is clearly better in a general way that it becomes an issue. If it was "merely cool", then they wouldn't continue the debate and there would be no long debate threads. It takes two+ to tango. Search for and view the context around "The performance, syntax, and manipulation benefits of HOFs are obvious if you consider them" on this page to see repeat unqualified insistence. At least that's how I interpret it. * ''Again, I don't see the folks making industrial control systems in assembly language, or programming FPGAs in VHDL, or expert systems in Prolog -- who are perhaps even less likely to benefit from HOFs than you are -- complaining when somebody ''insists'' HOFs are clearly better. (And, in that case, my impression is that the only implied context for said insistence is "where HOFs can be used", not "typical custom biz apps".) So, it's just you qvetching as usual. If you don't want to go swimming in the HOF pool, why are you up to your knees in it and complaining about the temperature?'' * Somebody made a bold claim about FP, and I challenged them to demonstrate it in a certain domain, resulting in a long debate. If you view that as "qvetching", I don't know what to suggest. You have the freedom to ignore the debate if it produces unpleasant feelings in your WetWare. * ''So you essentially engaged in a DomainPissingMatch? Tsk tsk.'' * I may have called it "one of the most common", but not "better". * ''I'm not sure you can call it "one of the most common" until you define what it is. It's notable that I tried to use an example (sales forecasting) that I thought was quintessential "custom biz app" territory, but you obviously feel differently. I suspect much of the problem here might be that what I (and others here) define as "custom biz apps" is different from what you define as "custom biz apps". I thought your approach to this -- defining "custom biz apps" in terms of who builds them rather than what they are -- to be interesting. Unfortunately, it's rather unhelpful in determining what is, or is not, a "custom biz app" -- especially if we're looking at legacy or outsourced apps that have been around long enough for the author to be unknown or un-knowable. Could you tell us some of the "custom biz apps" you've developed? Then we'd have a better idea what you mean.'' * On this wiki he have considered to various degrees the CampusExample, PayrollExample, and "challenge 6" (ChallengeSixVersusFpDiscussion). And note I didn't define it as who actually built the app, but who would typically be hired for a given project. For example from above, my observation is that if an org wants heavy-duty statistics, they hire statisticians who use statistical software, not "generic" business developers. If they want quick-and-simple statistics, they might, but then wouldn't expect a wide variety of techniques, and thus HOF's won't help much. * ''Even if statisticians or mathematicians or some other domain specialists are needed on a project, their output typically needs to be integrated into a custom business application. The programmers of that application, therefore, may well benefit from HOFs in order to codify the statistical or mathematical calculations. And, again, could you tell us some of the "custom biz apps" you've developed? Then we'd have a better idea what '''you''' mean by "custom biz app".'' * CategoryBusinessDomain lists some more examples of the prior list wasn't sufficient for you. Why aren't the other ones sufficient (since we already have a intro for them)? If you need to sift a wide variety of industries to find one that can demonstrate HOF's well, that doesn't bode well for the claim that they are ''widely'' helpful. I don't dispute blue-moon improvements. -t * ''CategoryBusinessDomain lists general categories of applications. It gives no specifics. "Marketing", for example, could range anywhere from maintaining a list of business contacts to sophisticated pattern identification in customer buying behaviour determined from loyalty card data. "Finance" could range anywhere from a single table of currency conversions to stock portfolio management with real-time automated stock trading. The former might not benefit from HOFs, but the latter might. Do you consider the former to be a "custom biz app" and the latter not? That's why I'd like to know what ''you'' consider to be a "custom biz app", and please be specific. Obviously, CampusExample and PayrollExample aren't real (though my PayrollExampleTwo '''is''' real); what '''real''' "custom biz apps" have you developed?'' ** GoldPlating and the like can indeed be applied to biz apps. AI and advanced statistics, for example could probably be applied to many areas of a typical biz app to incrementally improve them. However, remember that my working definition is what a rank and file biz app developer would typically be called to work on. If an AI or statistics expert is brought in, then that section no longer falls under the working definition. An org still may have the typical biz developer do experiments, but for something that uses/applies mass HOF's, I suspect most will fall outside of that. I could be wrong, but you have to demonstrate it with a coded sample. (Eval can be really nice for experiments and prototyping.) Related: BusinessPatternIrregularity. ** ''Where do you see GoldPlating in my examples? A custom marketing application that identifies patterns in buying behaviour isn't GoldPlating a contact list, it's a completely different kind of custom marketing application. And I doubt anyone in the marketing department would say the contact list app is a "custom biz app" but a buying pattern analyser isn't a "custom biz app".'' *** It's not the existence of pattern identification, it's the level or quantity of approaches that would have to be used to justify HOF's. Typically managers would settle on a handful of statistical tests/reports and those get implemented after review and these are not added to very often. One does not need to "massively register" statistical sub-calculations over time. The need to often add/change/delete statistical sub-functions without having to modify anything else is just not likely to exist by experience. *** ''It doesn't matter whether they change often or never. HOFs can provide a simpler way to construct certain solutions than any other approach. The pattern is typically that shown in HofPattern. Is there '''anything''' in HofPattern that would be improved by using EVAL in place of HOFs? (Hint: No, obviously not.)'' *** We are ''not'' exploring the claim that that EVAL is a significant improvement over HOF's. And if you claim HOF's are (clearly) "simpler", then I'd ask you provide specifics on how simpleness is being measured. Remember, I cannot read your mind. After all these years, I shouldn't have to ask because you should know by now there's a 99.9999% chance I will ask. -t *** ''Simpler = lower Kolmogorov complexity, without using dictionary compression.'' *** EVAL is not necessarily higher. *** ''Please demonstrate, by converting my JavaScript example above to use eval().'' *** As described above, your request is rigged. *** ''As described above, it isn't. There's nothing "rigged" about it. It's a simplified version of actual code (based on my memory of it) used to display dynamically-updated data refreshed in near real-time. It's part of what I would call a "custom biz app".'' ** And no, I have no actual biz apps I can present on this wiki, just the fictitious samples (but 2 are downloadable and runnable). The actual ones are all proprietary, which is typical of ''custom'' business apps. As far as PayrollExampleTwo being "real", if it doesn't demonstrate anything related to this topic (such as HOF's being better), then it's not of much use here. Being "real" is great, but that by itself doesn't solve the issues here. I am not sure what you are getting at anyhow with this line of questioning. ** ''I'm not asking you to present source code, or even detailed descriptions. Just a list of names and brief descriptions would do. I suspect much of the disagreement over "custom biz apps" on this Wiki come down to the definition of "custom biz app", in a typically LaynesLaw fashion. If you'd identify some "custom biz apps" you've created, then we can agree on a common definition. General categories, as I've shown, don't help.'' ** See above near "scrolling through the app list". ** ''See my response. Do you have some objection to providing a list -- just names and brief descriptions are fine -- of "custom biz apps" that you've developed? That would help us arrive at an agreement on what a "custom biz app" really is. I'm afraid your two fictitious examples are far too fictitious to be even remotely realistic. CampusExample doesn't even list requirements; it's just a database schema.'' ** "Fictitious"? Please explain. CampusExample is basically a student and grade tracking CRUD system. A campus has to track students, classes, grades, etc. and do the typical things we expect of a campus, such as provide transcripts. I don't understand your complaint. Most of us have spent years in college and should have a pretty good feeling of the services needed. That's why it's a fairly good example for mutual analysis among WikiZens. ** ''CampusExample has no processing and it's laughably trivial. A real campus system can easily have over 10,000 tables and does useful things, like scheduling and calculating GPAs and verifying that course/programme requirements have been met or are not violated. (I have worked with real campus systems.) However, I can see application of HOFs in student/staff/room scheduling, for example. That said, I think HofPattern and my Javascript example above clearly demonstrate what HOFs can do. Obviously, there are alternatives -- the fact that usable systems can be created in old-skool COBOL is proof of this -- but are they better? Obviously not. In the places where HOFs are of value (see HofPattern, again), the alternatives are clearly worse.'' ** You've yet to demonstrate "clearly worse" with meaningful and objective metrics. I know you strongly believe they are great, but you have to externalize those beliefs to keep making such claims here. ** ''Awkward delimiter-escaping in strings, risks of code-injection, need for compiling and/or syntax-checking at run-time, lack of static type-checking, and awkward handling of scope, are all objective characteristics of eval()-based approaches. HOFs address all of these, with the only possible downside being that weak programmers may need an extra hour or two of training, reading or explanation -- once -- to learn how to replace EVAL with HOFs.'' ** These claims have all been discussed already. I won't re-address them here. Note that in a lot of the cases, Eval is not the counter solution anyhow, so complaints about string injection etc. are misplaced. ** ''You've usually referred to eval as an alternative to HOFs, so I mentioned it. If you argue that HOFs can always be replaced with something else, that's obviously true. As I've pointed out before, we got by for years with COBOL/xBASE/VB/etc. and no HOFs. Was that better? No, not in any way.'' *** The alternatives to HOF's depend on the situation. It's not one-to-one. COBOL? Why mention COBOL? And those who've used the TableOrientedProgramming features of ExBase usually had nice things to say about them. ExBase died for other reasons, not TOP. It would be like me blaming Lisp's lack of mainstream acceptance on HOF's. *** ''I mention COBOL because it's an example of a highly-successful language that doesn't provide HOFs, and it's used to develop custom business applications. If your point is that we don't need HOFs, you're right -- COBOL is proof of that. Does that mean it's better not to have HOFs? Absolutely not. It's unquestionably better to have HOFs, to handle situations like that shown in the JavaScript above or in HofPattern.'' ** Incidentally, perquisite checking is similar to some of the "oddity detection" and "employee bonus computation" reporting systems I've worked with. The hardest part is to create a user interface(s) such that power-users can manage most of the verification rules without calling on programmers often. It may be good job security for the programmer to micro-manage such rules, but it's not economical for the organization. Thus, a way eventually must be devised to encode the rules in such a way that power-users can create, change, and manage rules without writing app code. (There are still going to be hand-coded stuff, but we want to minimize the need for it. I try to make it part of the UI such that "Test X" may be hard-coded, but the user can find and reuse it in a drop-down box or the like to incorporate it into the existing Rule Manager.) The bottleneck is generally dealing with the WetWare of a power-user-friendly UI, not so much the hard-coding of the rules (which we are trying to avoid). Related: CompilingVersusMetaDataAid and BusinessRulesMetabase. -t ** ''I don't see the relevance here.'' ** HOF's make it more difficult to store business logic in the database, as either expressions or declarative/attribute-based rules. If the logic doesn't change much over time, then HOF's are not going to make a difference compared to traditional "procedural" programming. If there are a lot of changes, then end-users are eventually going to be brought into the mix to help create and manage such rules, and HOF's don't help much with a BusinessRulesMetabase. It's not going to be about programmers writing new HOF's, but creating interfaces for power users to a BusinessRulesMetabase. ** ''If you're replacing code with end-user programming, that's a different issue entirely. Of course, you know what usually happens as well as I do: After an initial honeymoon period, some end-user get tired of the awkwardness and limitations of your hybrid "code in database"+"code in software" solution and concocts a code-based solution of their own in VB or whatever. That end-user eventually winds up in the IT department, if her solutions are good enough to gain notice -- and that's who creates the "BusinessRulesMetabase"... In conventional code. Otherwise, someone else replaces the "code in database"+"code in software" solution with an Excel spreadsheet. Either way, awkward "code-n-load" solutions are rarely sustained. At least the superior integration of the relational model into programming languages (e.g., research/educational efforts like the RelProject) show promise in this direction, but current mainstream tools make this approach essentially unworthy of notice in the programming community.'' ** I never said the entire solution was "code in the database", in fact, most of it is NOT. There are ways to provide a flexible RuleBuilderInterface without using direct "code in database". BusinessRulesMetabase describes some of these. And you are correct in that such a tool cannot handle ''every rule'', but it doesn't need to. Specialized programming will still be needed. It's a kind of EightyTwentyRule: 80% of the rules are controlled by power users, and 20% by programmers. Ideally a programmer tries to integrate hand-programmed rules into the rule system so that they are composible with the rest of the rule-building kit. For example, a typical pull-down list of QueryByExample operators may have some domain-specific operations added among the usual "standards" (equals, not equals, starts with, ends with, etc.). Or extra pull-down conditions at the bottom. The power-user can then optionally incorporate them into other rule sets or projects (if made semi-generic). '''You seem to want to optimize the system for programmers while I want to optimize it for power-users.''' -t ** ''Your PayrollExample appears to suggest that the majority of the solution should be "code in the database". I certainly have no objection to providing end-user customisation and code generation. When the requirements warrant it and appropriate users have the ability to use it, it's an excellent thing. However, such features are completely orthogonal to HOFs. I know of no circumstance where a programmer's choice would be, "do we use HOFs, or do we use 'code in the database'?" In fact, the two can be complementary. If HOFs are used as in HofPattern to inject functions into a predefined algorithm, it's entirely reasonable for those functions to be evaluating expressions or other code loaded from a database.'' ** A fancier version of PE could use rule and/or expression builders. Most likely the variations will be in the database meta configurations, not the HOF's such that they don't provide anything significantly helpful. I doubt they'd change often such that a case/switch statement is plenty good. A case statement is insufficient only if there are mass additions and changes to the HOF's available but a relatively stable interface for them. I haven't seen any scenario that would have such. ** ''Could you show in the JavaScript example above, and/or HofPattern, how you'd use "a case/switch statement" in place of HOFs? What do you mean by "most likely the variations will be in the database meta configurations"? What is a "database meta configuration"? I've only seen that phrase used to refer to the configuration information needed to connect to a given database, e.g., a .NET connection string might be considered "database meta configuration".'' ** I cannot figure out what the heck that JavaScript thing is trying to achieve. It has no documented end-goal. I'm not in the mood for a trouble-shooting "bit-digger" session. It sounds more like a GUI control problem than a biz problem. GUI issues tend to be very language- and tool-specific, while I wish to focus on business logic itself if possible. By database meta configuration, I mean the power-user's rules represented as data. Perhaps there is a better word for it. (Note that I didn't create the topic title for BusinessRulesMetabase....at least I don't think it was me. That was a long time ago.) ** ''I've put the "JavaScript thing" on the Web at http://shark.armchair.mb.ca/~dave/hofajax/ so you can see what it does. If you don't like "GUI control problems" there's always HofPattern, which covers a general category of problems -- those that require customisation injected into indivisible algorithms -- not just GUI ones.'' ** Okay, then could you please give a non-GUI custom biz software scenario (in English) for such? ** ''See HofPattern. A good example is injecting the fitness function into a scheduling algorithm, for employee scheduling or creating school timetables.'' ** Like I stated there, I cannot evaluate the benefit of that because I don't know the typical ChangePattern(s) of that sub-domain. That is necessary info to evaluate the maintenance-friendliness of HOF's etc. I suspect most org's used packaged software for that, making it off-topic here. (I can usually state the reasons and give examples of why certain changes are likely or unlikely if I have some knowledge of the domain. The reader may not agree with my assessment, but at least I document it the best I can and they can compare it to their own experience or org and then make their own judgement on a given ChangePattern claim.) ** ''HOFs are no more or less "change unfriendly" than any other function. They're certainly less "change unfriendly" than having to alter multiple lines of duplicated code or complex code, which is what HOFs typically remove.'' ** You haven't identified common duplication in a realistic CBA setting, only in your imaginary toy examples that optimize for some imaginary over-caffeinated user world where the user wants to do 80 things at the same time and his/her browser looks like live TV of the surface of the Sun. Yes, such users exist, but also slow and dull users, and we must cater to them as well. In some ways more so, because if something is too simple, both the quick person and the dull person can still use it satisfactory. However, a version optimized for the quick person prevents the dullard user from using it all-together, and your phone rings: "Your..........stupid..........software.............ate............my...............re.....port!" ** ''I'd be happy to point out "common duplication in a realistic CBA setting", but you've still not agreed on a "'core' custom biz app". I'll assume here that a "realistic CBA setting" and a "'core' custom biz app" are the same thing. Go back up this page a bit, and point out which of my list of custom business applications are "'core' custom biz apps" and which are not, and then we can get somewhere. Until that happens, I'm sure every example I provide will either be (a) something you claim can be bought off-the-shelf and therefore isn't relevant, or (b) is not a "custom biz app" because it's too statistical/GUI-focused/something, or (c) isn't a domain you've seen before so you can't comment.'' ** I could also complain that you have rejected examples/scenerios already used on this wiki. Please PageAnchor your list. ** ''I've pointed out, several times, why your examples/scenarios -- FP Challenge 6, CampusExample, and PayrollExample are inadequate and/or incomplete. Instead of cluttering the page with PageAnchor''''''s, please use your browser's in-page search to look for "'core' custom biz apps".'' *** Okay, let's go with "e-commerce for gardening company". ** ''By the way, my so-called "toy" example isn't a toy at all. It's used in a "custom biz app" (I'm assuming a reasonably typical definition here) to graphically show the status of participating users in a real-time collaboration/communication tool. Even a "dull person" can instantly see which users are green (good) and which are red (problem), and instantly appreciate the proportion thereof. Furthermore, as it's a '''custom''' business app, it's been designed to meet '''specific requirements''' so the whole "quick person" vs "dull person" argument is moot: it meets the needs of its users.'' *** I have agreed the Brady-Bunch-Grid-Status-Monitor ''is'' a legitimate CBA. My disagreement was over your scoring being tied to a specific client technology. ** One doesn't need HOF's to do it in a general sense. JavaScript/DOM may require HOF's because its timer function requires HOF's as an input and it has no known practical alternative. "HOF's are better because the forced browser standard forces them on you" is not a very strong argument for HOF's. We've been over this already in HofPattern. ** ''We've already established that your suggested alternatives are inadequate (clicking frames, indeed!) As for HofPattern, it illustrates a '''general''' pattern where there is no reasonable alternative. Your suggested alternatives -- case statements embedded in implementations of indivisible algorithms, or use of "eval" -- are obviously inferior for reasons we've gone over several times. By the way, why do you mention the DOM?'' ** A decent client could potentially be built around JS, but we'd have to blow up and burn the existing HtmlStack. As far as our arguments over the status monitor grid thing, we'll LetTheReaderDecide rather than repeat our arguments here. And MS-IE's clicking sound bug is probably caused by a bad HOF ;-) * And why aren't regular functions sufficient? I'm not sure what you are envisioning here. I would expect the org would pick a half-dozen or so statistical tests and that quantity won't change much after the primary project is complete. If there is a lot of "fiddling" with statistics, then it would probably be in the commercial packages I already listed for reasons I already gave. * ''"Regular functions" aren't sufficient because they only return a value, not a function. HOFs return functions.'' * I meant as part of the solution, not the entire solution. We'd have to visit a specific coded scenario to compare such details, and thus are back to square one. -t I don't know if biz apps have "minimal transformation". It depends on how you define "transformation". They do a lot of "routing" of information based on various business rules. Group "A" may see (or receive) data group X, group B see data group X and Y, group C see something else, etc. The trick is not only to manage and define all this "routing", but allow users to see, understand, and/or control much of the routing also. ("Routing" here includes both virtual viewpoint alteration and actual "movement" of data. As far as power-users "controlling" it, see CompilingVersusMetaDataAid.) ''Routing is not transformation.'' Recombining is not transformation? Anyhow, let's try not to get bogged down over this term. ---- Discussion migrated to NodeJsAndHofDiscussion because it was getting TooBigToEdit. ---- See also: DynamicStringsVsFunctional, ChallengeSixVersusFpDiscussion, QueryAndLoop, HowToSellGoldenHammers ---- CategoryExample, CategoryFunctionalProgramming, CategoryBusinessDomain, CategoryJavaScript ---- DecemberTwelve