From discussion on EvidenceDiscussion about FunctorVsClosure and the use of HigherOrderFunctions and LambdaExpressions in general, the following challenge is set:
"Given a list of SKUs, sort them so that those starting with letters are sorted alphabetically, the numeric ones have to be sorted numerically (and appear strictly after all the alphabetic ones) including the ones with a dash in column six but they have to appear just before the 'P'-coded SKUs (i.e., those that start with a 'P')."
In other words, given the following unsorted list of SKUs:
1029381
1390283
2393840
99934
5932
1394821
2398221
B34982
P348293
A348924
Z398402
P393482
13902-85
23938-43
13948-22
23982-27
...they should emerge as follows:
A348924
B34982
13902-85
13948-22
23938-43
23982-27
P348293
P393482
Z398402
5932
99934
1029381
1390283
1394821
2393840
2398221
A Java 8 Solution
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class Demo2 {
// Return s as Long if s is numeric. Return null if it isn't.
private static Long obtainNumber(String s) {
try {
return Long.parseLong(s);
} catch (NumberFormatException e) {
return null;
}
}
// Return Long representation of s if it's numeric with a dash in a given position. Return null if it isn't.
private static Long obtainNumberFromNumericWithDashInPosition(String s, int position) {
if (s.indexOf('-') != position)
return null;
String sWithoutDash = (new StringBuilder(s)).deleteCharAt(position).toString();
return obtainNumber(sWithoutDash);
}
final static int maxNumericLength = Long.toString(Long.MAX_VALUE).length();
// Obtain an internal string representation of an SKU for sorting purposes
// such that SKUs starting with letters are sorted alphabetically,
// the numeric ones have to be sorted numerically (and appear strictly after
// all the alphabetic ones) including the ones with a dash in column six
// but they have to appear just before the 'P'-coded SKUs (i.e., those that
// start with a 'P').
static String getSorter(String sku) {
Long number = obtainNumberFromNumericWithDashInPosition(sku, 6 - 1);
if (number != null)
return "B" + String.format("%0" + maxNumericLength + "d", number);
number = obtainNumber(sku);
if (number != null)
return "Z" + String.format("%0" + maxNumericLength + "d", number);
if (sku.charAt(0) < 'P')
return "A" + sku;
return "C" + sku;
}
public static void main(String[] args) {
List<String> list = Arrays.asList(
"1029381",
"1390283",
"2393840",
"99934",
"5932",
"1394821",
"2398221",
"B34982",
"P348293",
"A348924",
"Z398402",
"P393482",
"13902-85",
"23938-43",
"13948-22",
"23982-27"
);
Collections.sort(list, (str1, str2) -> getSorter(str1).compareTo(getSorter(str2)));
for (String s : list)
System.out.println(s);
}
}
Constructing a string on which to sort -- which is the usual way to handle this sort of problem in SQL -- seems a bit dirty, but it's simple.
What's this demonstrating exactly?
You proposed SQL and other approaches as alternatives to HOFs on SummaryOfHofExamples. On EvidenceDiscussion, you wrote, "[a]nyhow, without exploring a specific specification and written pattern, it's difficult to say which is the ideal here. There are many ways to skin a cat with two birds."
So, here's a specific specification of an example of a typical problem encountered in custom business application development. I've solved it using a Java 8 LambdaExpression passed to the built-in Collections.sort HigherOrderFunction. Perhaps you can show how some of the alternatives to HOFs that you've suggested compare to it?
What does the HOF get you? It's looks like a one-off kind of problem. (Note that if you have several millions of these, then doing it in RAM may cause problems, but I'll ignore that issue for time being.)
The HOF enables you to inject the specification of the custom collation order into the general-purpose sort algorithm implementation, which makes it a practical demonstration of the HofPattern. It means handling a "one-off kind of problem" like this doesn't require writing a new one-off 'sort' algorithm implementation or altering an existing one.
Yes, RAM is a limitation in this particular example.
I'm sorry, I'm not seeing what the HOF is getting one. Your explanation is not clear to me in terms of practical issues. I don' know what you are comparing it to in your mind that is worse in production. And I don't want to make an SQL version because we'd be comparing SQL to Java, which creates other language-related comparison issues besides HOF's
What the HOF is "getting one" is the ability to inject a customisation (a custom collation order) into a generic algorithm (sort). See HofPattern.
What I'm hoping to compare this to is your list at the bottom of SummaryOfHofExamples, where you suggest a number of alternatives to HOFs but don't explain how they're alternatives. I've compared FunctorObjects with HigherOrderFunctions/LambdaExpressions on FunctorVsClosure, which presumably covers your "objects" alternative, but what about the rest?
Mechanic: "You should add a tri-flux capacitor to your car. It gives injection customization."
Customer: "But why would I need injection customization?"
Mechanic: "Because you car is better with injection customization than without."
Customer: "How so in practical terms?"
Mechanic: "Because you'd have a tri-flux capacitor in your car and other cars don't. Therefore, it's better than the other cars."
Huh? What does that have to do with this page?
- Do you feel the mechanic is giving a good answer?
- It might be an excellent answer. What does a "tri-flux capacitor" do and how does it compare to its alternatives? Or, to put it in relevant terms, how would you solve the CustomCollationOrder problem using the alternatives you've suggested on SummaryOfHofExamples? The reason a "tri-flux capacitor", er, HigherOrderFunction/LambdaExpression approach is "better than other cars" is because, as I pointed out above, "it means handling a 'one-off kind of problem' like [a CustomCollationOrder] doesn't require writing a new one-off 'sort' algorithm implementation or altering an existing one."
- Why would we have to alter the sort algorithm if HOF's never existed? A method could be used to define a compare operation for a given array/list instance, if the API's and/or language is so designed.
- In other words, you're talking about an alternative where 'sort' is defined as a base class, and its 'compare' method is defined as an override-able method? Levering inheritance to implement a customisation of a base class is a form of altering an existing implementation. For one-off CustomCollationOrders like that shown here, it would generally be fine. However, as a general strategy, the DependencyInversionPrinciple suggests that DependencyInjection (such as via HOFs or FunctorObjects) is preferable.
- DependencyInversionPrinciple is a weak principle, in my opinion. It's a "rule of thumb" to keep an eye out for, but should be weighed against other issues. And I would not expect custom software to have gazillion nor often-changing comparing strategies for reasons covered at EvidenceDiscussion. -t
- There's no mention of a "gazillion nor often-changing comparing strategies", so I'm not sure why that's an issue. One custom strategy -- like that shown here -- is sufficient. Why do you think DependencyInversionPrinciple is a weak principle?
- I'm not even sure it violates DIP. But rather than get into a likely vocab fight, how about you demonstrate a practical and relatively likely "problem" caused by the overriding technique mentioned?
- There's no overt problem, but as a general strategy there's a limitation: You can only create customisations statically, via inheritance. In languages that support dynamic classes or prototypes this is less of an issue, but it means customisations can not be generated programmatically. With HigherOrderFunctions, it's possible to programmatically generate program-defined custom collation orders.
- OOP can be as static or as dynamic as a language/API want's to make it. But more important, I don't see a common need to programmatically generate program-defined custom collation orders. As mentioned in EvidenceDiscussion, if we wanted to make collations power-user-configurable, then we can study various UI's for that, perhaps similar to email filter UI's, or something else as found in BusinessRulesMetabase. I imagine it would have (optional) groupings, priority levels, comparison types, standard collating sequences (EBIDIC, ASCII, etc.), case control, trimming, and probably something similar to Regex's. In general, it appears to me you are getting "meta-happy", trying to find uses for meta-ness where none really exist, or at least the benefits are relatively small compared to WetWare/staffing issues such that WetWare/staffing issues either over-power parsimony/symbolic-elegance, or make it difficult to get a clear evaluation. Back to the good ol' GreatLispWar.
- Indeed, program-defined custom collation orders are relatively rare. Situations like the so-called "Brady Bunch Intro" example at http://shark.armchair.mb.ca/~dave/hofajax/ or the code simplification at http://blueskyworkshop.com/topics/Java-Pages/lambda-predicate/ are more common examples of the application of HOFs/lambdas that don't have simple alternatives.
- I disagree, but I'll leave the "Brady Bunch" dispute at NodeJsAndHofDiscussionTwo rather than reinvent it here.
- You disagree with what, specifically? What about the code simplification example?
- "Simplification" is in the eye of the beholder. I place grokkability and good modularization over code size at times. Anyhow, let's not reinvent the usual debates here.
- PageAnchor RAM-Structure-Query
- You don't think the final code shown on http://blueskyworkshop.com/topics/Java-Pages/lambda-predicate/ is grokkable and well modularised? If not, why not?
- Are you introducing a new example, or is this intended to be related to collating? If it's new and you wish to discuss it, a new topic may be in order. My initial survey is that it's proving GreencoddsTenthRuleOfProgramming and is unknowingly making a case for TOP and TOP-friendly languages (TableOrientedProgramming). -t
- I'm not introducing anything new, merely continuing the thread. To recap: I asked you about the code simplification example. You responded with "'simplification' is in the eye of the beholder" and said you value "grokkability and good modularization over code size". I asked if you if you thought the code simplification example was grokkable and well modularised. Why not answer the question?
- If you mean the "blueskyworkshop" link, the first one is the most grokkable and strait-forward. I suppose if one is not allowed to use a RDBMS, or SQL on collections (some languages or std. libraries allow SQL or at least WHERE-clause-like string queries on RAM collections), and one really does need to create a query-like interface, then the final example shown is consolation prize. An OOP "override" interface is probably also possible for that. Please don't ask me to make an example, I don't want to at the moment. (An Eval can also be used process a query-like expression.)
- Imagine that a user employs Eval in a language with LambdaExpressions and HigherOrderFunctions without knowing how either are implemented or what LanguageParadigm may have inspired them. From that user's point of view, Eval performs exactly same operation as a LambdaExpression passed to a HigherOrderFunction, except that instead of specifying it in native syntax it has to be constructed as a string with appropriate delimiters and escape characters, it usually can't reference variables directly (because it doesn't implement closure), any external data must be filtered or avoided in order to prevent code injection, it has to be explicitly passed to an Eval() function instead of being invoked like a function, and it inevitably runs slower. Why would that user (or any user, for that matter) choose Eval over LambdaExpressions/HigherOrderFunctions?
- Eval may be better suited for a hobby project than production. But it does have the ability to be supplied or stored externally from the EXE or script (in files, DB tables, URL parameters, etc.) such that one doesn't have to change the EXE or script to change a query. Note that it is possible to "capture" pre-passed scope by appending/inserting a current value: "a + b + " & c & " + d"; where "c" is a current-scoped variable, and "&" is for concatenation.
- What, in this circumstance, makes Eval better for anything let alone a hobby project? An equivalent lambda expression could be specified as a + b + c + d with no labour over delimiters or dereferencing variable 'c', which I hope doesn't contain external data or you're inviting code injection. Why would you risk a plethora of security, performance and reliability problems caused by storing native application code in a table and executing it in the same space as the application -- which I presume is intended to allow PowerUsers to edit formulae and the like -- rather than safely embedding one of the various interpreters (usually implementing a dialect of Lua, Python or Javascript) made for that purpose?
- What if we want to store expressions and formulas in a database table so that power-users can edit and manage them? I realize there are parsing libraries available for some languages, but not all. Or use the Eval approach for prototyping, and then hard-wire the formulas later for production (or refine parametric interfaces using them).
- Storing expressions and formulae in a database table so PowerUsers can edit them is fine. The secure and safe way to permit that is to embed a scripting language or expression parser. Letting the code in your database table have unrestricted access to your application's address space is a recipe for disaster. I can imagine using the Eval approach briefly for prototyping, but given that embedding a scripting language or expression parser is straightforward, I can't imagine leaving it in place for more than a minute or two. It might be left in place for a while if the project was waiting for some other developer to deliver the scripting language, but even then I'd rather print the language statements to a debugging console than execute them.
- The trade-off decisions for risk versus time versus resources etc. is ultimately with the manager/owner. In practice they often want to skip security safeguards to "get it out" and/or save money. Beyond making recommendations, it's usually not the developer's call. If the owner of a company wants to knowingly gamble with security, that's his/her prerogative; it's their money, not yours. (Practice prudent CYA, such as documenting your recommendations against risky practices.) Some owners will indeed suggest that they'd accept say a 1/3 chance of being hacked to be first-to-market with Widget X.
- Those are project management concerns. Technically, there is almost never a reason to prefer Eval over an embedded scripting or expression-evaluation language, and there are good reasons to avoid it.
- Irreverent distinction; one must do what the job asks despite the category of the reason or task. "Technically" you do your damned job as the boss tells you. Statements like that are yet another reason for me to question the rationality of your perspective from a real-world/actual-field perspective. You are an idealist. Your usage of "reason" in "never a reason" is troubling. Reason or purpose given from what perspective or authority? God? Spock? Shut-in math or philosophy nerds? Cucumbers?
- ["Technically, there is almost never a reason to prefer Eval over an embedded… language" means "there is almost never a technical reason to prefer Eval over an embedded… language", which is an eminently reasonable assertion. These have been covered already, but for completeness: Eval approaches demand a more elaborate parser and hence reduce performance, the ability to eval at all is typically restricted to interpreted languages or alternatively demands that a compiled binary also include a copy of the compiler itself, and there're myriad security concerns arising from the approach -- there are no technical benefits of Eval over a simplified, scope-restricted, secure expression parser, and many technical downsides. Thus, as claimed, there's no technical reason to prefer Eval over embedded scripting or expression-evaluation languages. There might however be project-management reasons to select Eval over alternative approaches, such as wildly restrictive time constraints. -DavidMcLean?]
- "Technically" doesn't technically meany anything sufficiently clear. Tools are evaluated based on weighing tradeoffs. The importance of the factors being weighed per tool feature is up to the customer/buyer. If they intentionally value quick-and-cheap over other factors, that's their prerogative. "Technically" means nothing in that context. Optimize choices for stated goals. Machines don't invent the goals and thus no "technical" automaton is involved in setting the goals (at least in this decade). It's a human selecting the goals, not technical equipment. We build rockets because we want to go to the stars. No machine told us to go the stars; machines and math don't give a shit either way.
- You seem to be misunderstanding the use of the term "technically" here. I didn't mean "technically" to mean "actually", as it's sometimes used; I meant "technically" as in "the technical domain". As our other correspondent pointed out, there is never a technical reason to prefer Eval over an embedded interpreter. All the technical justifications point to using an embedded interpreter rather than Eval. There may be ProjectManagement and/or business reasons for using Eval, but there may be ProjectManagement reasons for doing all sorts of things that are either irrelevant from a technical point of view, or outright nonsense. Years ago, a friend of mine told me his boss insisted that he remove all named constants from a C program, leaving only numeric literals. His boss thought it would make the program run faster. It wouldn't. There is no technical reason for eliminating named constants in C, but there was a good "keep your job" reason even though it's technical nonsense. That does not mean there is any kind of technical justification for eliminating named constants. Likewise, there is no technical justification for using Eval in the case described here.
- I am NOT talking about the owner misunderstanding the results of a trade-off (cause/effect). I don't know where you got that from. There may be a valid business reason for excepting certain risks, such as trying to be first to market in order to say take advantage of the NetworkEffect. But that should not be my concern as a hired coder anyhow other than given recommendations. The ultimate goals, and related trade-off decisions, are NOT mine to make. That's a moot issue, even if the owner is making foolish choices. It's not our concern. My writing is from the perspective of a regular coder in terms of the tools available to deliver the on the owner's goals. The logic behind the owner's goals is simply NOT our concern and it is pointless to make it a concern in this context.
- I'm not talking about misunderstanding results of trade-offs. I only used my anecdote as an example of an external body (like a ProjectManager) affecting or taking away our technical decision-making freedom. If the owner says she needs to be able to evaluate expressions stored in a table by 6.00pm (it's 5.45 right now), then Eval is a reasonable choice, but it's not an engineering choice. From an engineering or technical point of view, I wouldn't choose to use Eval() over an embedded interpreter, because there is no engineering or technical justification for using Eval() over an embedded interpreter. There are engineering and technical justifications for not doing so. However, there may be business reasons -- like a desperate time constraint or unreasonable boss (as per my anecdote) -- that force me to use Eval() instead of an embedded interpreter.
- If not for actual time/labor/resource constraints, an "engineering" solution would be mass GoldPlating in almost every case. Time/labor/resource constraints affect just about EVERY project, some more than others. A "rush job" is no different other than the degree to which these affect it such that a Boolean classification of "engineering" versus "non-engineering" decision is meaningless.
- Why do you think an "engineering" solution would be "mass GoldPlating in almost every case"? The case here is an excellent example of where it wouldn't be the case. In every aspect except implementation time (and that being a difference measurable in an hour or two, if you're using a pre-existing interpreter library), an embedded interpreter is a preferable engineering solution to using Eval(). How is that GoldPlating?
- El Wrongo! It may be that with further analysis and domain experience, some kind of parametric interface or "logic/set tree" rule-builder interface can be created. "Raw" expressions are often an intermediate stage until the patterns of queries/requests/reports is better understood. Further, with enough resources, maybe they can hire a big programming staff to hard-code and maintain such would-be-queries in the app itself rather than have power-users do it.
- You appear to be moving the goalposts. Of course other things can be created if the requirements warrant other things. The original goal, stated above as a question, was "[w]hat if we want to store expressions and formulas in a database table so that power-users can edit and manage them?" For that, an embedded expression evaluator is technically superior to Eval(). It's not GoldPlating, it's just a solution with fewer flaws than Eval(). How did "logic/set tree rule-builder interfaces" get in there, and "hire a big programming staff" is the opposite of "[w]hat if we want to store expressions and formulas in a database table so that power-users can edit and manage them?"
- You are correct that I did generalize/widen my scope without stating the change. I want my responses to be useful in general. I guess if we do focus on just "expressions that power-users can change", then, yes, the gold-plated (or maximumly-engineered) version of that requirement is an embedded expression evaluator, I concede. It does seem kind of a violation of OnceAndOnlyOnce since the language already has an expression evaluator: "Eval()". It's a shame it has to be reinvented/re-included as an attached library. Something is off. Perhaps the real gold-plated solution is a language with an access-controllable Eval(), such as the ability to only allow or deny a list of operations, scopes, libraries, etc. Hmmmm, gets one thinking...maybe that's how code-in-tables (TOP-ish stuff) can be facilitated. PowerfulCodeEvalDiscussion.
- Use of a BusinessRulesMetabase wouldn't change -- for example -- the custom collation code above, except for the code inside the getSorter() function. Instead of invoking native code that defines how to generate a sortable string from the SKU, it would invoke the BusinessRulesMetabase interpreter to generate a sortable string from the SKU. Nothing else would change.
- I'm not sure what your point is. I'm not saying your design would make it difficult to use a BusinessRulesMetabase, I just haven't seen any clear evidence it's the best way to interface to a BusinessRulesMetabase.
- My point is that a BusinessRulesMetabase is totally orthogonal to the use of HOFs/lambdas, or any other language construct. If you have a function that returns a string, it makes no difference whether you invoke native code to obtain the string or invoke a BusinessRulesMetabase interpreter to obtain the string.
- There's no argument here, by the way, that HOFs/lambdas are the "best" way to achieve anything except for requirements where the alternatives aren't as good. For example, if you wish to define dynamic specialisation of a generic algorithm, and IF/CASE statements would involve editing existing code, and accessing a SQL DBMS would involve unacceptable overhead or the problem doesn't involve querying, and a FunctorObject requires reference to its defining context, and method overriding doesn't allow the desired degree of dynamism, then use of a HOF/lambda is probably the best choice. It's isomorphic to making the choice between using a FOR loop vs using a WHILE loop. Obviously the circumstances are different, but the mental action -- choosing which programming-language construct best solves the problem -- is equivalent.
- You haven't identified a practical need for a certain "degree of dynamism". You are still trying to sell refrigerators to Eskimos. You seem enamored by the POTENTIAL of HOF's for certain dynamism or meta power, mistaking it for practical benefits. But in practice the situations that can take advantage of it are rare, at least in shops I work at. A code base of 530 lines but using 2 paradigms is superior to a code base that has 500 lines but uses 3 paradigms by my judgement, because it's less likely to confuse typical staff. The average business values predictability of production over higher average productivity, and catering to average staff provides that expected predictability (less stumpage halts) in exchange for less long-term average productivity. That's the real world as a I see it, and how I adjust the trade-offs to best fit it. My personal preferences be damned. --top
- [How so? Perhaps you could demonstrate how method overriding (the technique which was claimed not to provide the "desired degree of dynamism") might be used to address the precise problem presented at the top of this page, if you don't mind. What's the "Eskimo" approach, if higher-order functions are refrigerators? -DavidMcLean?]
- What "precise problem"? How is above allegedly unique from other custom collation problems? Is it not possible to have a method implement a comparer? Is your comparer somehow special? Where? How? Granted, I don't use direct array/RAM-centric sorting very often and thus am not an expert on RAM-centric custom sorting. The database or database-like API's take care of the vast majority of sorting needs I encounter such that I don't run into the need to stretch array/RAM-centric sorting to its limits. When I do sort arrays, the built-in collation is usually plenty fine. Thus, I do feel like an Eskimo being sold refrigerators here in that I don't use them enough to know the ins-and-outs of refrigerators. (Note that I have since added to my prior paragraph above).
- I find your lack of imagination disturbing. Whilst you may not need dynamically-configurable collation orders, can you not imagine that others might need them?
- I'm looking for staff fit, not creativity. Why gum up code with another paradigm that may confuse when the existing paradigms are perfectly fine? MentalMasturbation is for your own time at home, not for team projects.
- Who said anything about creativity? I'm talking about meeting requirements in an effective and efficient fashion. I'm not sure what you mean by "gum up code" with "another paradigm". I'm not talking about paradigms. ThereAreNoParadigms. There are only language features that make it easier to express the solutions to problems.
- We'll have to AgreeToDisagree on how to achieve "easier to express". I won't enter into a LaynesLaw loop over "paradigm". One could use "general technique" in its place. If an OOP-ish language idiom can do the job, then why introduce another, HOF, which may confuse or delay grokking. Any reduction of code size appears to be minor at best, except under rare circumstances.
- [All language features exist specifically to "make it easier to express the solutions to problems" in some way, whether it's by making code more concise, making code more robust (type checking and the like), or making code safer (memory-wise, for example); there's no need for debate on that point. Perhaps an OOP-ish technique can do the job in this case. What technique? How? How does it differ from the higher-order-function technique? Incidentally, is a higher-order function really a fundamentally distinct and unfamiliar language idiom? A call to a higher-order function is not particularly different from any other function call, after all -- it runs some code, using its arguments to customise the code that's run, and returns a value -- with essentially the only distinction being in the expected argument types. Is "function" really such an esoteric, unfamiliar type to be passed around? It's standard practice in OO to pass around objects, and it's quite common to pass an object to inject a certain method implementation into some code (i.e., DependencyInjection), so OO devs will already be fairly familiar with the concepts and implications behind the ability to pass functions around. Why would it become a distinct, confusing concept when a function is to be passed without being wrapped up in a class? -DavidMcLean?]
- Because devs use objects every day. If they are used to delivering packages via a motorcycle after having been doing it for several years, why have them start delivering packages via go-carts to (allegedly) save a few pennies of fuel? Confused or erroneous driving is more costly to the org than a few pennies of fuel savings. You focus on the machine, not on the driving process (WetWare) and the impact of that to the bottom line.
- [Exactly, devs use objects every day. Many objects -- in particular, those that fall under FunctorObject -- exist solely to carry a single method implementation around. OO devs are familiar with this. First-class functions are, in object-oriented languages, actually just a shorthand way to express a FunctorObject, modulo some added closure-related convenience as discussed on FunctorVsClosure. Thus, OO devs are already entirely familiar with first-class and higher-order functions in most respects; for most cases, all that needs to be picked up is the new syntax, and learning syntax alone is easy. There's no huge, fundamental distinction between OO techniques and functional techniques putting up a cognitive wall between them; some, such as these being discussed, are merely slightly different takes on the same concept which are more or less convenient depending on the precise context. -DavidMcLean?]
- You seem to be making a good case for keeping OO without knowing it. And even though the "leap" may be relatively small for some, when the current guy leaves and the new guy comes in, they have to reinvent that transition, including possible rough spots during the transition (such as encountering the ex-dev's work). Some will make the transition relatively smoothly, some won't. That's the nature of people: they all think different. The only thing they have in common is that they could work with a typical set or shop set of languages and language features during the interview/test. You want to deviate from the known knowledge base, creating risk for the org. The shop is not out to evangelize FP, they are out to get work done with available staff and hiring pools.
- [Good. I want to keep OO. It's a useful way to model computations. Alone it's not the most concise or flexible way to handle some problems, of course, such as the one presented in this topic; this is why multiple means of expressing computations exist. As for the rest of your response… what transition? What do you mean? -DavidMcLean?]
- Yes, what transition, and what "known knowledge base" is being deviated from? If I infer correctly, you appear to be assuming that developer knowledge growth stops at some arbitrary point roughly around (say) C# 2.0 but not reaching C# 3.0, or Java 7 but not reaching Java 8. Developers don't learn language features by LanguageParadigm, filtering out all the ones from FunctionalProgramming. They just learn language features.
- Staff comes and goes. You typically shouldn't code to an individual code reader, but to a typical code reader of that organization so that it's still grokkable 5 years from now when the current coder is roughly 60% likely to be long gone.
- To a typical code reader in an organisation that uses C# 3.0 or Java 8, all the features of the language are "grokkable". Why would any feature of the language be less grokkable than another?
- In my experience they are. IndividualExperienceShapesPerceptions.
- Logically, developers have less experience with new features than old features. Couldn't it be that what you perceive to be less grokkable, is in fact less grokked due to lack of experience -- a problem trivially fixable with practice and time? I remember when functions and procedures of any kind -- in any language -- were initially confusing to developers raised on Fortran IV or BASIC, but with practice and time, those developers learned to deal effectively with functions and procedures. Experience fixes inexperience and lack of understanding. Why should new features be any different today?
- It appears I am not explaining my observations very well. The longer-term average skill-set has stayed about the same for the 25+ years I've been in the biz. New developers flow into the industry and more experience ones typically move into management or some other IT-related field. (Programming is kind of like pro-sports: it's mostly for the young, for good or bad: fingers, eyes, and/or patience will eventually give out.) The average age and experience and skills for practicing coders has remained relatively steady over the years. Time will not fix that. -t
- The difference is that the young are trained in (or discover, because they have no conceptual baggage to make them turn away) FunctionalProgramming features from the outset, just as our generation were taught procedures, the preceding generation was taught structured programming, and the generation that followed us was taught ObjectOriented programming. What are new features to you in Java 8 or C# 3.0, they've been taught from the beginning.
- Perhaps QwertySyndrome in terms of JavaScript could do so for a while, but I bet some new language or fad or trend will supplant JavaScript fairly soon. Too many hate it; it's ill-suited for what people want to use it for, and has unnatural OOP syntax/conventions. What is actually being done with JS can be done just fine, and more naturally, with a decent OOP language. IDE's may eventually hide most of HOF and JS's ugliness from typical developers. The longer an ugly standard remains in place, the more tools appear to hide the ugliness.
- Who said anything about JavaScript? However, it applies equally to JavaScript and its inevitable successors, though I'm talking about FunctionalProgramming in general and mainstream "enterprise" languages like Java 8 and C# 3.0 in particular.
- Dream on.
- [A nuanced and thoughtful rebuttal indeed. Do you mean to imply we're imagining the steady growth and proliferation of functional features in mainstream languages? It's clearly happening. C# brought in delegate syntax, and then lambdas. Java offers anonymous inner classes, and now also lambdas. Ruby, Python, JavaScript all made functions first-class right off the bat. Even the mess that is PHP adopted support for first-class functions and closures. There simply don't exist mainstream languages that haven't evolved to provide this functionality. -DavidMcLean?]
- As you keep forgetting, languages tend to add idioms over time to look "with it". COBOL now has OOP. But that doesn't mean typical developers actually use all features or new features. FP is in fad-mode right now such that languages are adding it in this point in history. New languages of the future may not (such as if they do OOP right to be more competitive with FP-style syntax/idioms).
- [I don't keep "forgetting". I simply refuse to accept the assertion that languages tend to add idioms over time to look "with it", along with the implication that there are no other, more compelling reasons to add language features. In the absence of specific examples or proof, your assertion is essentially baseless. It's trivially shown that added language features can simplify certain expressions; witness the difference between the classic-Java approach of anonymous inner classes and the newer Java 8 lambda feature, for instance, as shown on FunctorVsClosure. Even if language idioms are only added to make old languages seem hip, they still quite obviously provide the ability to simplify expressions which were verbose and complicated without them. That's useful, and that's clearly another possible reason to add language features. Why assume the most useless of motives without some proof? -DavidMcLean?]
- Why assume YOUR version without proof? Yours is not the default feature motivation model. And I already agreed that they can plug shortcomings in Java's OO (as-is). You won that sub-debate already; in the name of OnceAndOnlyOnce, please stop repeating it.
- [I'm not assuming any particular feature motivation model without proof. Pay attention. "the ability to simplify expressions which were verbose and complicated without [a feature]" is a "possible reason to add language features", not necessarily the reason language designers have in mind. Nonetheless, it is a far more compelling reason than "to be hip"; is it not reasonable to assume it's at least a more likely motivation, with that in mind? -DavidMcLean?]
- You seem to assume humans are more rational than I do. HumansSuck and most are biased without knowing it. ("Rational" is relative. Fad-chasing may have certain social benefits.)
- [Sure, that's true enough. Why assume bias is the only reason behind a decision without any evidence, though? Why not just leave it as "unknown"? -DavidMcLean?]
- I gave my opinion about what the motivation probably is. If you don't want to hear my opinion, tough.
- [If all you meant to present was an opinion, why make a claim as though it's an outright fact? Why attempt to use it as a counterargument? -DavidMcLean?]
- As you keep forgetting, language designers add idioms over time not to "look 'with it'", but because they think those idioms are useful. Sure, OO languages may implement (say) FunctorObjects that are indistinguishable from LambdaExpressions, but when they do, is there any point in making a distinction? Regardless what you call it, or what LanguageParadigm inspires it, it's a useful language construct.
- In general, no they don't, although HOF's can be a "patch" for ugly OOP design of a given language.
- In general, no what don't what? Yes, I guess HOFs can be a "patch" for ugly OOP design, but they can be more than that. http://blueskyworkshop.com/topics/Java-Pages/lambda-predicate/ doesn't appear to be patching "ugly OOP design". Neither is http://shark.armchair.mb.ca/~dave/hofajax
- I've already commented on both of those elsewhere. OOP done well could handle such apps just fine (and/or RDBMS or TOP).
- Yes, you've commented on both of those, but not demonstrated how "OOP done well", and/or RDBMS or TOP "could handle such apps just fine". Can you point to pages and/or PageAnchors where you've demonstrated use of OOP "done well" (and/or RDBMS or TOP) to implement the examples on http://blueskyworkshop.com/topics/Java-Pages/lambda-predicate/ and http://shark.armchair.mb.ca/~dave/hofajax ? Note that they are runnable applications, using real languages. Speculative languages are weak, unless you clearly show that they're implementable.
- You forgot, I already conceded that HOF's make decent patches/work-arounds for flaws/limits in common EXISTING languages. If you find something unimplementable with my pseudo-code examples, you are welcome to point it out.
- That's damning with faint praise. Your implication is that HOFs and lambda expressions are at best a workaround. So far, your example at InAppQueryExample is not runnable, lacking both instantiation and base class definitions. The language might be implementable, but it's impossible to tell given that its only example is incomplete. The app is missing parts, so it's not clear how your "OOP done well" alternative to HOFs/lambdas is intended to work.
- Since it's marked as being under construction, it's clear to normal people that it's premature to complain. Further, it's best to complain AT the topic of the complaint topic, rather than scatter criticism all over like a bad-factoring programmer on crack.
- My criticism of InAppQueryExample is directly relevant to the current discussion. If you don't want it to be criticised whilst it's incomplete, don't post it until it's complete. Otherwise, although it's clear that your example is under construction, it's not clear what is still to be constructed and what will be omitted.
- Some like to peruse half-finished examples (as a curious read-only reader). I don't know how Wikizen's would vote on that preference such that I am not prepared to accept your personal preference as a general wiki norm. If you are bothered by half-finished examples that are clearly labelled as being half-finished, then simply ignore them. (I don't mind comments AT the half-finished page, but am bothered by repetition of page criticism placed at OTHER topics. It's not stable enough to make and use a summary yet.)
- I'm not prepared to allow the possibility that every example will be headed with "[under constru" in order to deflect any and all criticism, on the basis that it's not finished yet. Therefore, I'll criticise what I see, where it appears most appropriate to do so. You can always respond by directly answering the criticism or by completing or altering the example and indicating you've done so.
- Fine, be a repetitious anti-text-factoring asshole and wear it like a Goatse badge of honor.
- {Other than C maybe.}
- [Well, C has got function pointers. Clang provides lambda expressions of a sort, too, although those are nonstandard. So, no, even C approximates the functionality. ;) -DavidMcLean?]
- [It's not unique from other custom collation problems. It's a simple, concrete example of a nontrivial custom collation problem, which is suitable for studying the merits of different approaches to implementation, such as through higher-order functions, overriden methods, language-expressions-as-strings plus an eval() operation, and so on. It's certainly possible to have a method implement a comparator; if it's not too much trouble, we'd like to see your take on that, so it can be assessed relative to the other possible approaches to solving the problem given. -DavidMcLean?]
- I am not interested at this time in making a method-override example etc. Perhaps some other month.
- How do you identify and measure "meta-happy"? How do you know when developers are "meta-happy" instead of using "meta" facilities appropriately? If "meta-happy" developers use meta-facilities instead of OOP, do "FOR-loop happy" developers use FOR loops instead of WHILE loops?
- What evidence do you have the "Wetware/staffing issues" are relevant here?
- What evidence do you have that WetWare/staffing issues are NOT relevant here? The default is not that they are not relevant. Anyhow, this "evidence fight" appears to repeat EvidenceDiscussion.
- I have no evidence either way. Thus the default is <null>. Therefore, discussing it is pointless. Likewise, I could suggest that power consumption is relevant, and that HOFs cause shorter battery life (or longer battery life -- you pick) on mobile devices. Shall we discuss that?
- Like you keep forgetting, anybody is welcome to propose an observed relationship and personal observations to back an alleged relationship on this wiki. If it differs from my experience I will simply say so and move on. For some reason anecdotal evidence or discussions of anecdotal evidence seems to send you into a conniption about evidence. Seek therapy instead of obsess on that issue, it's an annoying trait. I know you PERSONALLY don't want anecdotal evidence on this wiki, but you do not make the final call, and complaining about it repeatedly is childish and unprofessional. At least I realize my name-calling and cussing is a bad habit I need to work on. You haven't reached the recognition stage yet, and are still in the deflection and denial stage.
- I'm happy for anyone to propose an observed relationship and personal observations. I'm not happy for nothing more than an observed relationship and personal observations to be used as a justification to argue for change to -- or restrictions to -- programming and programming languages.
- Likewise, I don't see why we should assume we should stuff every idiom and paradigm into our production code just because we can. The fact the industry keeps rejecting Lisp for 50+ years is fairly strong evidence that parsimony and symbolic elegance is NOT the primary criteria for judging languages and code design, and that typical staff has limits to what they can practically absorb. It's not just my personal observations. --top
- The fact that we're still talking about Lisp (and a fair number of us are using it) 50+ years after its invention is fairly strong evidence that the benefits Lisp introduced continue to deliver value. The fact that production languages are embracing FunctionalProgramming features is evidence that languages designers consider certain "idioms and paradigms" to be worth including. Note that we've moved from AssemblyLanguage to high level languages because certain idioms and paradigms were deemed worthwhile. If your attitude toward idioms and paradigms was universally embraced, we wouldn't even be using AssemblyLanguage -- we'd still be toggling switches on the front panel of the CPU. Actually, we wouldn't have switches because we'd be tying wires together. ...Assuming we've got wires -- I'm worried that staff will be confused by the "electricity" paradigm.
- You present the fallacy that if some abstraction is good, then we should crank the knob up to 10. If drinking some water is good then we should put everybody under water. There is a Goldilocks zone of abstraction and/or idiom quantity. I've been in the field for going on 30 years and don't see any notable increase in FP by rank and file developers, except where a language and/or API doesn't give the developer many practical alternatives. Dream on, FP ain't going anywhere.
- [You're right; FP isn't going anywhere. It's here to stay. -DavidMcLean?]
- FP never goes anywhere, beyond niches and zealots.
- I remember when they said that about OO. I remember when they said it about structured programming. I don't remember when they said that about high-level languages, but say it they did.
- Some "fads" stick, some don't. Didn't we have this discussion somewhere already? Deja Vu.
- They said exactly that about OO, structured programming, and high-level languages, too. "It's just a fad!" they cried, but it wasn't.
- Like I mentioned the last time we had this discussion, the OO fad-pushers were half wrong and used OOP wrong, pushing it for domain modelling. (Further, good TOP could supplant OOP in my opinion.) CASE, expert-systems, Logic-tree-builders, Ruby-on-Rails, and others I cannot remember at the moment came and went (or at least niche-ified). FP already had a day in the sun in the mid-80's and will fail to catch on for the same reasons it did before.
- Again, almost exactly what the pundits said about OOP in the late 1980s, shortly before it soared in popularity. You may not like the fact that FunctionalProgramming is gaining in popularity, but that won't stop it from happening.
- Dream on. Every new technology/technique has critics, both the ones that stay around and the ones that fade. Existence of criticism is not a good determiner of staying power.
If you have an alternative to using HigherOrderFunctions/LambdaExpressions (as you've suggested on SummaryOfHofExamples) to solve this problem, please demonstrate it.
Here is an SQL version. Can't guarantee it or something close works in all dialects. Most of the common dialects have some kind of "CASE" statement(s) that can function as an IF/ELSE kind of construct, but the syntax varies. -t
SELECT target, sortfield FROM
(
SELECT target,
CASE
WHEN instr(target || 'XXXXXX',6,1) = '-' THEN // sku with a dash [1]
'B:' || target // first letter controls general grouping
WHEN instr(target,1,1) BETWEEN 'A' AND 'O' THEN
'A:' || target
WHEN instr(target,1,1) BETWEEN 'P' AND 'Z' THEN
'C:' || target
ELSE
'D:' || right('00000000000000000000' || target, 20) // pad with zeros [2]
END CASE AS sortfield
FROM mytable
)
ORDER BY sortfield
[1] The X's are only to avoid index-out-of-range error by guaranteeing min length of 6
[2] If the equivalent of "maxNumericLength" (Java version) is available in a dialect, then we may be able to avoid an assumed max.
Cool. There are a couple of bugs: When there's a dash in the SKU, it should be treated as numeric. I.e., remove the dash and do the right('00000000000000000000' || target, 20) on it. The other bug is that if the first character in 'target' is, say, a '*', it will pad the target with zeros resulting in an incorrect sort. However, these are mere quibbles. Thanks for creating it. In an application where a SQL DBMS is already in use or readily available, it's certainly a reasonable alternative to using Collections.sort(). Of course, there are many more customisable algorithms than those that can be easily implemented in a SQL query.
- I'm not sure how those come about from the problem description, but if you are satisfied the "fixes" are relatively minor tweaks, I'll leave them as a reader's exercise.
Now, how about the other alternatives to HOFs at the bottom of SummaryOfHofExamples?
I never said all were always replacements in all circumstances. Perhaps I should clarify that on that topic.
Until you created the SQL example above -- which is appropriate for one circumstance -- and until I created FunctorVsClosure to illustrate FunctorObjects as an alternative to HOFs/lambdas, it wasn't clear how any were replacements in any circumstances. It's still not clear how EVAL is an alternative to HOFs/lambdas (though I can imagine), and it's not clear how "CASE/IF" statements are an alternative to HOFs/lambdas.
Examples are nice to have, we both agree. I believe that both EVAL and CASE/IF were described before, but the scenarios given by the "HOF side" were not fleshed out enough to give further details or sufficiently useful comparisons of worth beyond suggesting possibilities to explore.
I'm not sure what you mean by "the scenarios given by the 'HOF side'", but the scenario on this page was clearly "fleshed out enough" to permit creation of Java and SQL examples. Could you show how it would be implemented using EVAL and CASE/IF?
Other than possibly proving it can be done ("runs"), I don't see any benefit in using those other techniques for this particular example so far.
The benefit is that it would illustrate how EVAL and CASE/IF are alternatives to HOFs, as you suggest at the bottom of SummaryOfHofExamples. If EVAL and CASE/IF are only beneficial alternatives in certain cases, perhaps those cases should be identified and explained.
I don't remember all of the examples that influenced those suggestions. The "weather" example may have been one of them, but since it's allegedly protected by somebody's non-disclosure agreement, we cannot explore the PRACTICAL trade-offs further.
Alright, fine, here's a draft OOP mini-sample:
listFoo = list(3, 2, 8, 9, 4, etc);
class sortIt extends Sorter {
main(listFoo); // constructor
comparer(x, y) { return y.someProperty - x.someProperty; } // method
}
[How's this work, exactly? If that's the complete code snippet you'd need to sort a list, it doesn't correspond to the usual OO semantics of any programming language I've seen. What does all this syntax mean? -DavidMcLean
?]
It's not much different than the pseudo-code found in the NodeJsAndHofGuiDiscussion topics.
[True. The semantics of your 'class' construct weren't well-explained there either. What's it do? It's clearly not the same thing 'class' does in any current language. -DavidMcLean?]
Indeed. Speculating wildly, it appears your 'class' construct actually creates a prototype instance, and a method header with a body defines/overrides that method whilst a method header without a body invokes it, and a constructor is always named 'main'??? Is that what you have in mind? If so, how do you define a custom constructor and then invoke it?
- Fair questions, but I choose not to answer them today.
If we're assuming a conventional OO language of the C#/Java variety, it could look like this:
Sort sorter = new Sort<String>() {
public int compareTo(String str1, String str2) {
return getSorter(str1).compareTo(getSorter(str2);
}
};
sorter.sort(list);
This example creates an anonymous subclass of Sort with the 'compareTo' method overridden. Assume getSorter() and 'list' exist and are defined as shown at the top of this page. 'Sort' is a generic class that defines sorting a specified collection (of a specified parametrised type) via the 'sort' method. A custom comparison operator can be defined by overriding the default 'compareTo' method.
You could have the constructor do the sorting and eliminate the invocation of 'sorter.sort()', but it's generally considered bad practice to put significant processing in a constructor. See ErrorsInConstructor. However, if we use that approach, it will look like this:
new Sort<String>(list) {
public int compareTo(String str1, String str2) {
return getSorter(str1).compareTo(getSorter(str2));
}
};
For comparison's sake, the Java LambdaExpression equivalent -- using the Collections.sort() Java 8 code from the top of this page but reformatted to make the functionality of each line roughly correspondent across these three examples -- is:
Collections.sort(list,
(str1, str2) ->
getSorter(str1).compareTo(getSorter(str2))
);
The Java/C# style is far from the ideal in my opinion. I prefer OO languages that blur the distinction between class and object. Prototype-based OOP tends to be this way. Either way, the "code size" is dependent on specific language design.
Prototype-based OOP is eminently reasonable. However, your example above of what I presume to be prototype-based OOP is unclear. See the comment associated with it.