Out of curiosity, why is it considered bad practice to put "significant processing in a constructor"? It's like an on-start event and/or a main routine (or could be). Is that a language-specific recommendation tied to an implementation flaw or compiler design oddity?
- {I think its pretty similar rule to "don't put too much in any method (function, file, package, module...) NickKeighley}
- I didn't put too much code in it. -t
- How do you know? It's similar to "don't put too much in any thing", but a constructor invocation is special in that there's no possibility of returning an error code directly, and throwing an exception results in a null instance. That's precisely what this whole page is about. Do you want to have to test for 'null' or risk null-pointer exceptions? Do you want to deal with possible zombie instances that are only partially initialised? If not, make sure the constructor can always (within reason) succeed.
- Addressed already below. Note that if you choose use the "big constructor" approach, which is not forced on one, you don't even typically need the result object so it would not matter if it's half-ass. You are making a mountain out of molehill. Essentially if you do it all at once, you don't need the result and thus a zombie object makes no diff. If you do it in steps, then it's no longer a "big" constructor. It's a self-solving problem. -t
- If the result object is "half ass", you still need to know about it, as it probably has a bearing on whether the operation the constructor does succeeded or not. Getting that error information back to the caller is more complicated and more fragile when using the "big constructor" approach, as shown in the "Thelma" and "Louise" examples at the bottom of the page. As for making a "mountain out of a molehill", I merely answered your original question -- why it is considered bad practice to put significant processing in a constructor. I explained the reasons; you argued about it. You could have answered "Oh" and left it at that, so I don't think it's me doing the molehill mountaineering.
- Why do I "still need to know about it"? Let's not duplicate the Louise debate up here in the name of OnceAndOnlyOnce.
- Don't you think the caller needs to know that its invocation of a 'sort' operation failed, and maybe why?
- I don't see the relationship between these two. There are multiple ways to get error info, and having a COMPLETE result object is only one. We have at least these 3 options available to a designer/user, which may not be mutually exclusive:
- 1. Error info is given during an error handler inside the constructor.
- 2. The operation fails, but a "zombie object" still exists to examine. (It may still have error and/or state info inside.)
- 3. A try/catch wrapper over the entire initializer.
- Yes, and the issues with these approaches are discussed below.
It complicates detecting, catching, or otherwise handling failures in the constructor. Do you throw an exception and wind up with no instantiation and possibly no way to inquire further as to what caused the exception? Or do you not throw an exception and make the instance status available for inquiry via some method but require that the object-user check it? If you assume constructors will always succeed, it's trivial to have methods either return a status or throw an exception.
- {I much prefer exceptions to "zombie objects". If the object didn't complete construction then it shouldn't still be walking about. Saying construction should always succeed is a mistake. IMHO NickKeighley}
- Construction should always (within reason) succeed. Construction is exactly what the name implies -- construction, i.e., initialisation. If you're using the constructor for processing, that increases the risk of creating "zombie objects" or even nulls, which are worse than a zombie object, they're outright dead.
- {And I think you're elevating a Design Rule of Thumb into a Must Do. I'm disagreeing that a constructor should always succeed and I don't agree you've reduced complications by having partially constructed objects. I have to test for null you have to test for partial construction. NickKeighley}
- As I said on the page that spawned this one -- to which the first sentence on this page is a response -- putting fail-able operations in a constructor is "considered bad practice", so avoiding it is by definition a "rule of thumb" rather than a categorical imperative. And I'm not arguing for having partially constructed objects. Partially constructed objects are as bad as non-constructed (null) objects. I'm arguing for having constructors that, within reason, should always succeed. Why shouldn't constructors always succeed, within reason?
- {Constructors shouldn't always "succeed" because sometimes they cannot successfully construct the object. Consider a Database object, if it fails to connect to the underlying DBMS (DBMS down, Network down etc) then you don't have a valid Database object. You can *pretend* it worked and add IsItReallyInitialised?() and WhyDidntItInitialise?() methods but you essentially have a partially initialised object that is going to be a pain throughout the rest of your application. NK}
- You shouldn't be establishing a database connection inside a constructor. That should be done by an explicit open() method, which may be implicitly invoked -- if the database connection isn't open -- by the methods that access the database.
- Who said anything about databases? I didn't bring up databases here. In some cases we may indeed want fastidious and/or custom handling, in others not. Let the designer decide how fastidious they want handling to be; I offer a choice. (We could have an onDBerror handler method, I would note. Also, there are systematic techniques for handling routine data errors to avoid reinventing a jillion handling wheels, but that's another topic.)
- Our correspondent brought up establishing a database connection as a possible failure-prone activity that a constructor could perform.
- I don't see it significantly different a style of issue than say running out of RAM during sorting at this point.
- It's exactly the same kind of issue, with the same response.
- The typical design choices that come to mind are:
- Handle the error at the specific spot of likely triggering, such as when a query is issued to a database or when more memory is requested (if using say C).
- Have API-level handlers handle it, such as the on-x-error methods shown below, in which the API user would put in their own handler logic.
- Let some outer try/catch block set handle it, if available in the language.
- Let the system-wide handlers handle it, which is typically one of those:
- The built-in system handlers, perhaps if the following option doesn't "override" them:
- Custom system-wide handlers, per error category, if the language allows.
- (Choices not necessarily mutually exclusive)
- In general terms, all of those options are already discussed on this page. As noted above, the database connection scenario is no different from running out of RAM or any other error that might occur inside a constructor. Perhaps our correspondent mentioned it because he frequently uses some class that establishes a database connection in its constructor.
- I'd say ItDepends on intended usage and typical error patterns/frequency. -t
- No, in general it doesn't. There isn't any processing you can do inside a constructor that you can't do outside a constructor, and but processing inside a constructor conflates initialisation with execution. Obviously, there may be reasons to violate this guideline (e.g., initialisation that requires processing) but it should not be considered a normal case for the reasons described on this page.
- Your opinion is noted. I still disagree, per WaterbedTheory.
- WaterbedTheory says that if you push complexity down in one area, it pops up in another. Comparing example "Thelma" to example "Louise" at the bottom of the page, how does WaterbedTheory relate?
- More paradigms/language-idioms.
- Where are the paradigms/language-idioms in the examples? I just see some code. If the lambda expression is new to you and therefore scarily unfamiliar, shall we replace it with an appropriate FunctorObject? Remember, the point of this page wasn't to focus on HigherOrderFunctions or LambdaExpressions, but to focus on issues related to doing error/exception-generating processing in constructors.
- It's not me, it's about typical staff. Better to stick with OOP rather than make the army knife too wide.
- Again, this page is not about lambda expressions and higher-order functions. If you'd rather not see the lambda expression, I'll replace it with a FunctorObject. The issues with processing in constructors, which is what the example is intended to focus on, will remain the same.
- FunctorObjects are awkward, and are arguably not an OOP construct.
- Not an OOP construct? Huh? Of course they are -- a FunctorObject is a class with a method, defined so that the method can be invoked later and passed to something that might want to invoke the method. How else would you define a method so that it can be invoked later in some future context, but so that it contains information about our current context?
- Sorry, I disagree.
- How could they not be an OOP construct? They can be defined in any OOP language without extension. A FunctorObject is a class with a method. What could be more OO than that? And, again, how else would you define a method so that it can be invoked later in some future context, but also contains information about our current context?
- Okay then, for the sake of argument, if it is OO, then a FunctorObject is one OO solution to a given problem, and it may be one OO option among multiple OO options. If that's the case, what's your point again? OO choice is good, no?
- Sure, it's good. My point was that I could replace the lambda expression in the second "Louise" example, below, if the lambda expression bothered you. This page is about the issues related to handling errors resulting from processing in constructors, not about HOFs/lambdas vs non-HOFs/lambdas.
- Typically API's for application devs you don't have a "lone function" floating around. The "sorter" API would typically be part of something bigger:
class x extends CollectionProcessor {
...
method comparer(x,y) {...} // sorting collation
method filter(row) {...} // similar to SQL WHERE clause (optional)
method formatter(row) {...} // format output row (optional)
}
.
- Granted, for SystemsSoftware you might want a smaller granularity. (If our topic is splitting, then perhaps it needs a re-org)
- I'm not sure what you mean by "floating around", but you certainly have classes with a lone function. It's the essence of the CommandPattern (though, notably, the CommandPattern uses FunctorObjects to implement the OO equivalent of closures!) Your CollectionProcessor appears to be conflating multiple operations into one. That may be appropriate, or it might be lacking in TypeSafety.
- This seems somewhat similar to our "modularization" discussions in NodeJsAndHofDiscussionTwo. Like I said above, it's common and natural for there to be a "kit" of related services. I agree that in SystemsSoftware more granularity may be a better fit, but for app devs, pre-packaging is often a better fit so that it's more like paint-by-numbers: fill in methods you need and skip those you don't. It's almost like the code side of filling out a QueryByExample form. It may add bloat compared to the high granularity approach IF using high granularity, but the flip-side is that the relationship among services is more grokkable to typical devs (in my experience/observation). What's an example of missing type-safety? -t
- I think this is drifting OffTopic. It's lacking in TypeSafety if what should be three different types (or classes) are combined into one, such that they are all the same type. Though, in thinking on it, calling it a TypeSafety problem is perhaps too harsh. It's more of a StampCoupling issue.
I'm not sure what you mean. It sounds like a specific debugger, error message, and/or stack trace listing may be poorly designed in a given language or tool, rather than an inherent fault of "big" constructors.
No debuggers, error messages, and/or stack trace listings are involved. With a "heavy" constructor, you've got this...
Processor p = new Processor(someParameters);
// Did the constructor fail or not? Find out.
switch (p.status()) {
case Processor.ODX_CONDUIT_FAILURE: do_something(); break;
case Processor.PICKLE_SWITCH_STUCK: do_something_else(); break;
case Processor.SUCCESS: do_good_stuff(p); break;
}
// Anything we do with p, we have to check to see if p is valid or not.
if (p.status() == Processor.SUCCESS) {
do_something_more(p);
}
... Some stuff not referencing p happens here...
// Do more with p, but always check its status first!
if (p.status() == Processor.SUCCESS) {
another_thing(p);
}
...or this, if you wrap the constructor invocation in exception trapping:
Processor p = null;
try {
Processor p = new Processor(someParameters);
} catch (Exception e) {
// do something, but no idea what unless we provide fine-grained exception handling
}
if (p == null) {
// deal with some possibly-unknown error, because p is null so we can't ask p.
} else {
do_something_good(p);
}
However, if you ensure the constructor always succeeds, you can unconditionally do this:
Processor p = new Processor(someParameters);
...then you can do this with return values:
switch (p.operation()) {
case Processor.ODX_CONDUIT_FAILURE: do_something(); break;
case Processor.PICKLE_SWITCH_STUCK: do_something_else(); break;
case Processor.SUCCESS: do_good_stuff(p); break;
}
...or this with exceptions:
try {
p.operation();
} catch (Exception e) {
p.thing_you_do_when_operation_fails();
}
p.something_else();
...and p should ideally always be in some useful state.
However, this strategy isn't a strict rule; it's merely a good idea. Some component systems like JavaBeans assume there is a default (parameterless) constructor that will unconditionally instantiate a class. If it errors out during instantiation, you may have no indication why the instantiation has failed -- other than that it failed -- for the reasons noted above.
In my domain, I'd rather the language be designed to be human-friendly rather than machine friendly. Perhaps an interpreter/compiler hint can be provided to skip certain checking to gain speed in special situations (the downside being unhelpful error messages etc. if something goes wrong.) -t
This has nothing to do with gaining speed, and everything to do with making coding as human-friendly as possible.
I guess I am not understanding your point. A code sample for a semi-realistic scenario could maybe help.
It is a semi-realistic scenario. If it helps, replace the class name "Processor" with "Truck" and the method names with stuff about wheels and engines and trailer hitches. It won't change anything; it's idiomatic Java/C# OO.
Sorry, I still don't know what you are trying to achieve. Trucks and wheels don't necessarily generate exceptions. If there is a pattern to object errors, there may be better ways to deal with them, such as onErrorTypeX methods, but the devil is often in the domain details, which are not available here. And I am not sure what you are comparing to what. What specific quantity or aspect does the alternative improve? Less exception types that have to be handled? Less code per exception type that has to be handled? Better grouping of exception-handling code?
Ok, replace "Processor" (or "Truck") with "Sort". The general principle is that it's generally easier to keep error-generating code in methods and use the constructor for initialisation. If you put error-generating code in the constructor, it's harder to deal with it. If the "devil is in the details", handle special situations as you see fit.
What situations? The best place to put or handle a given exception is dependent on a lot of things, such as domain context and intended usage.
I don't know what situations, that's why they're "special". In other words, apply the general principle unless you have a good reason not to.
A "general principle" given without clear justification and not tested against realistic scenarios is not something I want to rely on.
The justification is provided in the examples above, including within the comments. As for realistic scenarios, they're as realistic as they can get. I've illustrated constructors and method calls, which are exactly like those found in every C# or Java program. You can even copy the code into VisualStudio or EclipseIde and run it, if you create a simple Processor class that implements the methods, constants and constructor shown in the examples. Other scenarios would differ in little more than identifier names; there'd certainly be no change in approach because C#/Java only provide so many ways to directly invoke constructors and methods wrapped with or without exception handlers.
Again, I don't understand what you are comparing. You say Thing A is better than Thing B, but only show an example of Thing B. Nor are you specifying what factor(s) you are measuring between them, such as "less code", "better grouping of exception handling code", etc.
If you have a constructor that throws an exception, you wind up with no instance. Therefore, you can't query the instance (because there isn't one) to see what went wrong with it. If you have a constructor that fails internally but doesn't throw exceptions, it can't return an error code. Therefore, it has to store the error code in the instance and you have to query the instance on every method call to see whether the instance is in a valid state or not.
On the other hand, if constructors always succeed, you can trap exceptions on a method-by-method basis and continue to use the instance and/or individual methods can return error codes without you having to check before every method call to see whether the instance constructed successfully or not.
Therefore, error handling is easier if it's based around methods rather than constructors.
Okay, that explanation makes a bit more sense. But much it still seems a language-specific issue. Couldn't it be handled as such?
try {create object or class X}
catch (e) {print("[?],[?],[?]",e.errorMsg,e.errorCode,e.methodName);}
If it's truly a common issue or concern, then objects could be given an optional onStartError method that can be used to define custom problem handling during constructors. -t
Sure, you can do that, but if 'e' is nothing but NullPointerException, where was the error and what caused it? Alternatively, you can use a set of fine-grained exception handlers (having pre-defined a set of classes inherited from Exception, of course) but that makes the error handling more complex. Either way, you've presumably got to go back and try instantiating the class again, which adds yet more complexity and potentially performance overhead. Adding an 'onStartError' method would work, but it's more complexity in a different direction. Why not avoid all that and use the constructor for its intended purpose -- initialisation -- and put processing in the methods?
If you're trying to pack all the processing into the constructor in order to avoid calling a method, aren't you kind of trying to un-OO the OO? In a sense, an object that only does processing in the constructor, and nowhere else, is the same thing as a procedure.
If you mean it can act like a procedure, sure. Making it flexible means it can act like a variety of block-related things. Remember, one of the goals is leveraging developer's existing knowledge of OOP for a variety of things rather than learn and/or switch back in forth between different "kinds" of things. Further, often other methods would be defined for other purposes. Our examples tend to over-simplify things to keep the examples simple. And we can sub-class to create "templates" to factor app-specific commonalities. And in the example, it's more like defining an event than a procedure; a comparing routine that is "fired off" when the sorting system needs it. It's not run once.
Sorry, you've lost me here.
As far as NullPointerException, if the compiler/interpreter generates an error, there should never be a "NullPointerException". All errors should generate an error info object ("e" in the example). If not, it's bad language/interpreter design.
[NullPointerException does generate an exception object containing information about the attempt to call a method on a null reference. What it doesn't do - and can't, practically, do - is tell you where the null came from in the first place. The actual source of errors is the source of that null, so a NullPointerException object is often not valuable. -DavidMcLean?]
I am not following you. What's an example situation of a hard-to-detect "problem" null?
[Anything along the lines of:]
someMethod(arg) {
arg.doThing();
}
[If null is passed into such a method, a
NullPointerException is the result; it's therefore advisable to check all object references for null before invoking methods on them, in all code that invokes methods. Of course, determining that arg is null doesn't help you figure out
why it's null and therefore doesn't help you fix your code. -DavidMcLean
?]
I don't see how the alternative would make debugging such easier. (Languages arguably shouldn't allow Null's to begin with, or at least not allow Null objects. I find the concept of Null ONLY sufficiently useful for numerics. Or allow a "required" indicator on parameter definitions, somewhat similar to ColdFusionLanguage's CFargument's "required" attribute, to indicate that blank/empty/null parameter "values" are not allowed for a given parameter. If a null object is passed, then the stack-trace tells you what sent it.)
There are languages that don't allow nulls, or that make them safer, such as NiceLanguage. In languages like C#, Java, C++, etc., a good way to avoid nulls is to put processing in methods and reserve constructors for failure-proof initialisation as suggested above. Thus 'Processor p = new Processor()' should always succeed, which means p will never be null.
Why are you repeating the original claim? There appears to be a communication gap that is not closing as volume of text increases. I don't think I wish to pursue this any further at this time.
I'm not repeating the claim for its own sake, but to point out that the original claim is effectively a solution to your suggestion that "languages arguably shouldn't allow Null's to begin with".
- Sorry, you lost me.
- You wrote, "languages arguably shouldn't allow Null's to begin with". If you don't define constructors that can throw errors, they'll always succeed. Thus, code like 'Processor p = new Processor()' will always succeed, which means 'p' won't be null, which means you're much, much, much less likely to encounter nulls. In a language that supports nulls, it's as close as you can get to achieving your suggestion that "languages arguably shouldn't allow Null's to begin with".
Note we could design the class/object/API such that the "running" of the sorting is optionally delayed if the developer feels they wish to separate the steps for better error handling, at the possible expense of more code. Sometimes you want succinct, sometimes you want fastidious.
// All-in-one version
class x extends Sorter {
main() {self.sort(theCollection);} // constructor
method comparer(x, y) {...}
}
// Alternative all-in-one version if overloading is allowed on constructors.
// The parameter-free version of "main" would do nothing.
class x extends Sorter {
main(theCollection);
method comparer(x, y) {...}
}
//
// Separated (delayed run) version with error handler wrapper
class x extends Sorter {
method comparer(x, y) {...}
}
try {
x.sort(theCollection);
} catch(e) {
print("boo boo [?]",e.msg);
}
This seems to be conventional OO, more or less, if you ignore the conflation of classes and prototypes. Could you explain how this is better OO (for some as-yet-undefined definition of "better") such that that HOFs/lambdas are contraindicated, particularly as your syntax is inevitably less familiar to the typical programmer than HOFs/lambdas?
Better than what? You complained the "conventional" way was too verbose. Often there are tradeoffs such that being compact and being error-handling-friendly may conflict. I'm offering a choice to LetTheReaderDecide which factor they want to optimize for. Are you claiming HOF's give one the best of both worlds?
So far, that seems to be the case.
I have not seen a clear demonstration of that, although comparing methods to HOF's or classes to HOF's is apples to oranges. Anyhow, It doesn't sound like a significant practical problem to me and I won't continue being curious about it. If some variable or object being fed into a sorter keeps going sour for whatever odd reason, you study why and come up with a plan to remove the problem, or catch it earlier.
See FunctorVsClosure, which makes the "apples to oranges" comparison you've described. As for your "study why and come up with a plan to remove the problem", that is what is described in this section -- a plan to reduce some of the difficulties of error handling by confining errors to methods.
I don't see where it addresses debugging difficulty, and I don't see why "confining errors to methods" does any magical debug helping. If you really want to communicate this alleged issue to me, you'd probably have to create realistic bug-causing situations under both designs and present realistic stack dumps and/or debugging sessions steps with numbered steps and numbered lines and descriptions that reference the numbered steps and numbered lines. Heavy details and heavily reality and heavy numbering/counting/labeling are helpful when making clear documentation so as to not leave out some unstated assumption without realizing it. You see, I cannot read your mind. (Complicating this is language-specific design issues.)
A bad collection (to be sorted) should be caught before attempting to sort it, I would note. It's not the sorter's job to detect and explain a missing or null collection, which per above perhaps shouldn't even be allowed to exist in languages. You declare any empty container (zero nodes/rows), and then fill it up with some other process such that the sorter will ALWAYS get a collection with zero or more nodes under every coding situation. (I suppose you could pass it the wrong type, such as an integer, but that's a type-checking issue.) If you claim HOF's are a work-around to Java's flaws, I won't bother challenging it because I'm not interested in that.
HOFs aren't a work-around to "Java's flaws". As I already explained above, but here it is again...
Imagine the following code:
Processor p = new Processor();
Case 1.
If you allow the constructor to throw exceptions:
- The value of 'p' will be null. Therefore, you've got null to deal with, potentially everywhere 'p' is used.
- The value of 'p' will be null. Therefore, you can't invoke any method belonging to the instance to inform you what happened, because there's no instance.
- You can use multiple custom exception types and multiple handlers to provide fine-grained exception handling, but if the constructor is complex, it may require an awkward proliferation of exception types and associated handlers in order to provide sufficient detail to act appropriately on a failure.
- The only way to recover from such an exception is to invoke the whole constructor again.
- How is this different from an old-fashioned function call?
- Instance construction involves two operations, allocation of a new instance, and invocation of a constructor on that instance. If an exception is thrown by the latter, the former is undone, with the result that any partial work done by the latter becomes inaccessible.
- So? I'm not seeing what practical problem this MUST cause. The new instance can be seen as a bonus byproduct. Why complain about a bonus? Yes, if you rely heavily on the "result" instance, it can cause problems, but that's the same with a function result: The more an app depends on the result of the function, the more problems caused by the function's failure. I don't see how HOF's magically gets one around such a trade-off. Some stuff produces stuff, and some stuff only changes stuff, and some stuff produces stuff but that produced stuff is of only minor value. If you depend on stuff coming out, then failure of the stuff producer causes problems. If you don't depend on stuff coming out, then failure of the stuff producer does not cause any known problems related to the stuff that did or didn't come out. Stuff Dependency 101. -t
- I was indicating the difference between instantiation and "an old-fashioned function call" purely in terms of error handling. If you don't need the instance and you're only invoking 'new Processor()' to use its constructor as a substitute for a function call, that's ok (though it has needless overhead), but why not just use a function call? If you're using C# or Java (or similar languages), just define a static method somewhere and invoke it. Then it is "an old-fashioned function call". However, if you need access to the constructed instance, and the constructor can fail -- either with a thrown exception or some internal failure -- then the problems noted above are something you'll have to deal with.
- I suppose you could argue that the technique shown normally produces an object while the HOF version doesn't. One could argue that's a waste of memory and/or name-space, but that appears to be a different subject. (I can't find a need for mass quantities of HOF's in my domain. Other domains may vary.) But, nothing is obligated to actually use/depend-on the object. -t
- Yes, that's it -- it's a waste of memory, name-space, and CPU time. You could simply use an ordinary function call. However, in and of itself, an ordinary function call isn't an alternative to using HOFs/lambdas.
- Re: "Why not just use a function call?" - 1) Ordinary function calls cannot pass blocks (without using unfamiliar HOF's), and 2) The object/class gives a framework with multiple possible "run" techniques, such as the examples above that give the API user a choice of "instant" run or delayed run in the same object, and 3) we may have other methods for other purposes, such as a row/node "filter" criteria method. (HOF's could be used for that, but it's usually awkward syntax, and doesn't allow a "template" approach whereby one can subclass and override methods they want to change.)
- The OO approach to passing a block with one "delayed run" method is a FunctorObject. See FunctorVsClosure for a comparison of FunctorObjects vs use of HigherOrderFunctions/LambdaExpressions. It's true that LambdaExpressions on their own can't be subclassed or overridden, but in an OO language where methods can be used as HigherOrderFunctions, it's possible to have subclasses with overridden methods that return subclass-specific lambda expressions for passing to another HigherOrderFunction.
- Why do you think HigherOrderFunctions and LambdaExpressions are unfamiliar? For C#, Java, Python, Ruby and Javascript there are a plethora of reference materials available online, which cover LambdaExpressions and HigherOrderFunctions along with every other feature of these popular languages. Other than perhaps because of being introduced more recently, why would one language feature be any less familiar than another?
- I think we've had this conversation at least 8 times on this wiki. 9 likely won't change anything. We don't agree; get over it.
- Then stop making unsupported assertions like (from a few bullet points above) "HOF's ... [are] usually awkward syntax".
- They ARE supported. You are just not personally satisfied with the evidence I gave.
- What evidence is that? Could you provide a reference or PageAnchor? Perhaps I missed it.
- http://apod.nasa.gov/apod/ap030630.html
- Really? Does such juvenile behaviour accomplish anything?
- GIGO. You are trolling here.
- Huh? How am I trolling? I asked a reasonable question. Don't you think it's possible that a reader of this page -- perhaps running across it via Google -- might want to know what evidence you have?
- Come on dude, everyone knows perfectly well the above is degenerating into the usual:
print "A: I gave evidence.";
print "B: That's not evidence.";
while (forever) {
print "A: Yes it is.";
print "B: No it's not.";
}
.
- I know YOU enjoy such repetition, but I DO NOT. I hope such love of repetition doesn't leak into your code.
- You've argued repeatedly that your anecdotal evidence is reasonable evidence for this wiki. Why are you unwilling to point to it, if it's reasonable evidence?
- You've already seen it and you don't think it's "reasonable" (because you are biased against me). Why rehash that bickerfest here yet again? It gets old. Now stop trolling. I'm truly sorry you are not satisfied with the evidence, but that's the way it is. If it pangs you so much that you are compelled to repeat the complaint many times, then see a doctor and get Prozac or something rather than disturb those around you with abnormal behavior that resembles an obsessive-compulsive disorder.
- Don't you think the casual WikiReader, who might see only this page, would like a reference to your evidence?
- See GreatLispWar and the links at the bottom of that topic, dear reader.
- Excellent. It gives the reader the opportunity to see the criticism in the discussion above the links, and thus balance is maintained.
Case 2.
If you allow the constructor to cause errors but not throw exceptions:
- You can't return an error value, because the only thing a constructor invocation can return is an instance of the specified class.
- So, you have to store error information in the instance so you can access it via a method to find out what went wrong.
- Then you need to check the stored error information prior to every method call to see whether the instance is in a usable state or not.
Case 3.
If you strictly use the constructor for reliable, error-free initialisation:
- You need never have null instances. In the example, 'p' will never be null. Unless you assign null to a variable or return it from a method, you never need deal with null again, ever.
- You can have methods throw exceptions and/or return error values. This allows fine-grained, targeted error-handling.
You didn't supply the info I asked for, and therefore I am still confused. This merely repeats statements/claims already given.
The "realistic bug-causing situations" can be the usual ones: files don't exist or are improperly named, access to a resource is disallowed by security settings, the name of some dynamic thing you're trying to create already exists, network connections fail, URLs aren't found, servers are down, storage devices run out of space, operations take too long and time out, values are out of range, etc. Whether these happen inside a constructor that does "heavy" processing, or inside a method, any of these and more could be the cause of an error condition or a thrown exception.
It wouldn't make sense to give you line numbers, because there is only one line relevant to the scenario: 'Processor p = new Processor()'. Items 1 and 2, above, are all about what you'll have to do if you allow Processor's constructor -- which, as I'm sure you know, is invoked by 'new Processor()' -- to throw an exception or have internal errors.
Stack dumps and debugging sessions aren't applicable here. This isn't about debugging. It's about run-time error and exception handling.
For one, you are not comparing the alternative, such as "Here is an EXAMPLE of the kind of error the HOF version can catch that the OOP version cannot...".
No reason why I would. Invoking a HigherOrderFunction is no different from any other method invocation. Declaring a LambdaExpression is no different from declaring any other kind of expression. Thus, HOFs and LambdaExpressions are equivalent -- in terms of run-time error and exception handling -- to putting error-generating functionality in methods but not constructors. It follows, then, that HOFs and LambdaExpressions are superior -- in terms of run-time error and exception handling -- to putting error-generating functionality in constructors.
I give up trying to communicate with you on this. I invite a different writer to try a different angle. Hint: try a realistic and clear example with side-by-side code and clarity in what is actually being measured.
I suggest doing some reading on conventional OO languages like C# 3.0 and Java 8, how their object model works, and how HigherOrderFunctions and LambdaExpressions are implemented on top of it. It would give you background that would clarify much of this. I'm afraid my contributions are no substitute for language references and textbooks, since I've not the time, ability, or inclination to provide their equivalents.
Why stick to conventional languages? The original discussion didn't limit the target languages. I'm just asking for a fuller and realistic example that demonstrates the alleged flaw in a concrete way. (You can even do it in a conventional language, with the caveat that relying on language-specific features may limit your point.) Apparently that's asking too much around here, where it's customary to sound like a vague round-about befuddled professor. This topic is nothing more than a huge lesson in miscommunication.
I suggest sticking to conventional languages to begin with, because they are commonly-used, almost universally-recognised, and they have extensive documentation.
- But they also have some annoying short-comings in my opinion.
- What alternative languages do you suggest using? The issue here is not what language to use, but what reading would provide the appropriate general OO programming background sufficient to understand the arguments presented here.
- What's more important, getting the "right answer" or being clear to readers? Ideally we'd want both, but I'm not sure that's possible without writing hundreds of pages at this point. Specific language design appears to factor heavily in these debates. At this stage I am just hoping I can plant the seed of the idea that some of the bloat seen in the "pro HOF" examples (for lack of a better term) may be language specific and NOT an inherent fault of OOP.
- You've yet to show an example of an OO language equivalent to HOFs/lambdas that conceptually works, and you've so far refused to address questions that might clear up the conceptual issues, so if there is a hypothetical OO language that provides clarity equivalent to HOFs/lambdas we've not seen it yet.
- What do you mean by "conceptually works"? Are you just confused by my pseudo-code, or have you identified a likely show-stopper? Yes, a full implementation would be nice, but it's not going to happen any time soon.
- Questions were raised about your pseudo-code which you explicitly refused to answer. As long as such questions are unanswered, it calls into question whether or not your syntax ideas are viable. It's also not made clear why you think your unconventional syntax for your imaginary languages will be more familiar than conventional HOFs/lambdas in real languages.
- That appears to be a redundant complaint. Please work on that annoying aspect of your writing style. You come across as "whiny".
- As long as you refuse to answer questions, the questions will be raised, as they're relevant to the current discussion.
- That's not a valid excuse for such repetition, and I'm sure most WikiZens will agree. I view such badgering as hostile behavior.
- You appear to be using your code examples as if they were sound and accepted evidence, despite the fact that their validity has been questioned. You refuse to respond to the questions. Don't you think it seems somewhat disingenuous to treat evidence as sound that his been queried as being unsound, refuse to answer the queries, and then continue treating it as sound?
- For the sake of argument, let's assume your complaints about the examples were valid. Two wrongs don't make a right. It would still not be justification for such repetitious badgering.
- It's not badgering, it's countering a use of evidence as if it is sound with a reference to the the fact that it is not considered sound.
- It's either badgering, or very poor textual factoring.
- Do you think it's reasonable to use incomplete examples as if they were complete? If I did it, wouldn't you take me to task for it?
- The subject is your alleged sins, not mine. Stop trying to change the subject. Even IF my examples were a steaming pile of maggot infested shit, that's STILL not a valid reason for YOU to badger or repeat. 2 wrongs don't make a right.
- If your examples are a "steaming pile of maggot infested shit", then it's reasonable to point out that fact every time you make reference to your examples and imply -- even distantly -- that they're not a "steaming pile of maggot infested shit." Otherwise, the casual reader might be mislead into thinking that your examples aren't a "steaming pile of maggot infested shit."
- Make a page anchor or topic.
- I might make a page about it, but it seems simple enough to point out the flaw without reference to other content.
- It's not a "flaw". Stop giving out false information.
- An incomplete example is flawed, by definition. Using it in debates it as if it is complete is also a flaw.
- Almost all examples that attempt to reflect real issues on this wiki would then be "flawed" because showing the complete system and/or context is usually unrealistic. Almost all examples will have some degree of lab-toy-like simplification applied to them to make them digestible to the reader. "Incomplete" is thus a matter of degree. In my opinion you are drama-queening your summary words because of your bias against me. And why not say "incomplete" instead of "flawed" if your complaint is that it's incomplete?
- Examples that are incomplete are usually obviously feasible. Your hypothetical OO language examples are both incomplete and raise questions about their feasibility, which you've declined to answer. That's quite different from, say, a Java example that is obviously runnable but for some additional class definitions.
- I didn't decline, just said I'd do it later. And I don't remember any "show-stoppers" being identified, just alternative interpretations. If you identify a case of "OO cannot do X no matter what because [insert brilliant reasoning steps]", please state it.
- Until "later" comes, the questions remain. Whether there are "show-stoppers" or not is hard to tell given that the semantics of your language are ambiguous and unstated. For one thing, it's not clear how you distinguish defining a constructor from invoking a constructor. For another, it's not clear how you distinguish defining a non-instantiated class from instantiating a class, or even if you do distinguish them. The possibility that your class is actually a prototype instance appears to be something that we gleaned, rather than something you defined, leaving one to suspect that you are not clear on the distinction between classes and instances. In short, it's not clear how or if your language works. You'd be much further ahead to take an existing language and use it -- like JavaScript, if you wish to employ prototypes -- or take an existing language and clearly indicate how you would alter it. Creating a language from scratch and assuming your reader can divine your intent purely from your rather nuanced syntax merely leaves the reader with unanswered questions.
I've given concrete examples above that illustrate the issue. They're even runnable, if you create a simple implementation of Processor.
- They're foo examples. I want to see realistic problems, not hypothetical shit.
- Is your vulgarity necessary? A "real" example would be structurally identical. It would differ only in variable, class and method names.
- The issue is relevancy to practical issues. Does it cause real and/or significant "problems" in the wild, or is it merely a speculative or invented worry of yours over something that's rarely a real issue in practice? You often seem to get "caught up" on esoteric obscure things, making planets out of molehills.
- Since any "real" example is structurally identical, it obviously causes precisely the problems described above. Here's something realistic: Imagine you have a class called Downloader with a constructor Downloader(string URL) that downloads the content at the URL to temporary storage, and then allows us to read the content via a 'string getDownloadedContent()' method. The constructor can fail because the URL is malformed, it can fail because the URL is inaccessible, it can fail because the URL is accessible but the download failed part way through, it can fail because we don't have permission to write to temporary storage, it can fail because the temporary storage area fills up whilst we're writing the URL content to it. There are probably more failures that can happen, but those are plenty. If the Downloader constructor throws an exception for any of the errors, then Case #1 applies, above. If the Downloader constructor encounters errors but doesn't throw an exception, then Case #2 applies, above. If the constructor simply takes note of the URL -- which should never fail -- and getDownloadedContent() does all the processing, that's Case #3, above.
- That's better; something concrete and realistic (although arguably not HOF/block-related). There are many approaches to error handling and how an API and/or language deals with it. If something can throw a wide variety of fairly common errors, than try/catch blocks may not be a good way to address them. (Arguably, try/catch blocks are a bad language feature to begin with.) One approach is to create handler methods such as onError for a catch-all (not necessarily mutually exclusive with detail ones), onUriError, onResourceFullError, onIoError, onAccessError etc. I'm assuming the object is successfully created for all these scenarios. (Some way to optionally let the "the system" handle it with a default error-and-halt should also be available, and perhaps be what happens if no "onError..." methods are populated. ) The approach chosen depends on intended use, shop/language/API conventions, etc. Do we want a Cadillac API or a Chevy API? (See WorseIsBetter)
- It's not HOF/block-related because this topic was originally an aside about error handling in OO constructors. As an aside it (initially) had nothing to do with HOFs/lambdas.
- In the given example, what useful purpose will a collection of on<x>Error methods serve? What will they do? How do they propagate error information back to the calling context? What do you gain by putting heavy functionality in the constructor?
- The on-x-error methods provide simple, convenient, and optional slots to easily put custom error-handling into. It's as close as one can get to paint-by-numbers in coding. As far as how and when to propagate errors, that's a rather involved topic with lots of different approaches and philosophies and domain issues. I don't wish to delve further into that at this time. As far as "what do you gain", I already addressed that. In short, developers can leverage existing OOP knowledge without having to dabble in FP, and they get flexibility in terms of multiple possible "run" techniques and multiple possible/optional methods, inheritance for reuse of common methods/attributes, and/or future expansion beyond what function/method parameters alone are good at.
- If the on-x-error methods provide "simple, convenient, and optional slots to easily put custom error-handling into", what are you going to do in them? Given the example above, when the URL is malformed, or the URL is inaccessible, or the URL is accessible but the download fails part way through, or we don't have permission to write to temporary storage, it fails because the temporary storage area fills up whilst we're writing the URL content to it, what are the associated on-x-error methods going to do about it?
- Isn't that an app- or domain-specific question? Such heavily depends on the context and intended usage in my experience. Do you want to me to create a little scenario and make some pseudo-code for it?
- I don't think it's app- or domain-specific, because it applies to the general strategy of using overridden methods to handle errors in a constructor. It requires that the methods are fully capable of correcting the error, and/or they can return error information to the context that invoked the constructor. Both of these are theoretically possible in any OO language, but not necessarily easily or without pitfalls. It's easier in languages that support inner classes or some equivalent because the on-x-error methods can reference the outer environment (i.e., a class in which the class with the on-x-error methods is defined) directly. If the class with the on-x-methods is a top-level class, you're pretty much forced to access global variables or singletons (bad for all manner of reasons) in the on-x-error methods, or you require a high degree of coupling -- like passing a reference to the environment calling the constructor into the class with the on-x-error methods. Perhaps a little scenario and some pseudo-code would help clarify what you have in mind, but I think a simple explanation would help more.
- See Example "Thelma".
- Yes, that's an example of the "inner classes or some equivalent because the on-x-error methods can reference the outer environment" that I described above. It works, but it's complex and potentially fragile. What happens, for example, if one of the on-x-error overridden methods that you've defined throws an exception? Since on-x-error is called from within the constructor, does the exception leave the constructor in an unstable state? How do you know? See Example "Louise" for an alternative based around avoiding ErrorsInConstructor.
- I don't know how you are measuring "more complex". It takes longer grokking time to understand what one is looking at for yours. And your what-if situation is relatively obscure. If you encounter such in the field, you fix so it doesn't happen again. For SystemsSoftware perhaps blue-moon nested error-handling-level issues are more important because a given piece of code is ran far more times than most custom domain software, but not for most custom biz software. Use try/catch and/or declare/run separation (above) for those rare situations where it matters enough. And I only used 2 paradigms, you use 3. It's generally harder to grok the intermixing of 3 paradigms over 2 (for typical devs), unless it gains one significantly more in some other metric. You seem to be arguing for a swiss army knife with 15 blades instead of 10. I argue that 15 delays the blade selection decision process in such a way that more time is wasted on grokking delays or selection-confusion errors than that saved by having blades that are somewhat more optimized to a given task (having more), on average. Errors caused by mis-grokking the code during code writing or maintenance probably will cause more total problems than nested error handling shortfalls. Again, you are optimizing symbols over grokking. -t
- I didn't use any paradigms. I used language features.
- Call them "language idioms" if you prefer. I don't want to LaynesLaw dance over "paradigm" here.
- How many programmers keep a list of "language idioms" in a book, so they can carefully exclude those that might originate from FunctionalProgramming? Obviously, they don't. Programmers learn language features, they don't learn language idioms or paradigms and slot language features into them (or not) depending on their personal views about the idiom or paradigm.
- I am not understanding your point.
- Programmers don't think in terms of "language idioms" or "language paradigms". They think in terms of language features. I've not yet heard a programmer say, "I'm not going to use lambda expressions because I reject FunctionalProgramming." They might say, "I don't get lambdas yet," because they're new (either the programmer or the language feature), but then they learn them because they're part of the language.
- They are already trained in and have lots of experience with OOP. FP takes a different mind-set. It's a sufficiently different way to "pass blocks". And they don't necessarily learn every part of a language JUST because it's part of the language features. (They perhaps may be forced to if FP zealots kept forcing such constructs into API interfaces. That would be an interesting experiment to watch...from a distance.)
- "Trained" programmers are trained to use one or more languages. They are not "trained" in OO as an abstraction except in university education, and then they're almost invariably taught FP along side. Being "trained" to use a language means being trained to use all of its features. A good trainer, facing (say) a room full of experienced Java programmers and explaining Java 8 lambda expressions for the first time, will demonstrate how a lambda expression is just a familiar FunctorObject with all the class scaffolding trimmed off. It doesn't take a different mind-set. Your use of the term "FP zealots" demonstrates that the only "different mind-set" is your arbitrary bias against what you see as some FP hype invasion carried in by HigherOrderFunctions and LambdaExpressions, rather than any rational evaluation of language features.
- Somewhere we've had this "training" discussion already. I disagreed with your assessment, based on developers I encounter in the field. Your domain/specialty is different from mine, and perhaps this is the reason for our different experience. And it's not "arbitrary bias". I observe and come to conclusions based on those observations. True, some of them are probabilistic "conclusions" (have a degree of uncertainty), but that's all either side has because formal grokking studies are sparse, per below. I believe I've developed a kind of "nose for fads" over the years. You admitted below you are not very interested in the sociological side of things such that it makes sense you'd be less keen to fad patterns. -t
- Can you be confident in your "nose for fads"? Didn't you once consider OO a fad?
- I considered overuse of OOP a fad. I had agreed they work reasonably well for GUI's, which means I wasn't against ALL of OOP. Back then it was not well known where it would work well and where it wouldn't, with a good portion of published examples being domain modelling. I have not seen a compelling reason to replace OOP with FP in general. Most examples seem to be based limitations of OO in given languages or around SystemsSoftware needs.
- So, it turned out that OOP wasn't a fad. Maybe inclusion of language features derived from FP won't be a fad, either. How would you know? You might not have found a compelling reason to replace OOP with FP, but maybe these people have: http://cufp.org/
- You are making it sound Boolean. Perhaps some aspect(s) of FP will find it's way into the mainstream, but it's too early to see what parts will. Your arguments for it so far are very weak in my opinion, just like the early OO pushers pushing for OO domain modeling, making up improbable change pattern claims and other errors in judgement.
- Some aspect(s) of FP have already found their way into the mainstream. LambdaExpressions and HigherOrderFunction have been part of C# since C# 3.0 and Java since Java 8. PHP has them too, in a typically PHP-ish bit of bodgery. There's the growing popularity of Scala. And there's JavaScript. What's notable is not that LambdaExpressions and HigherOrderFunction exist in popular languages, but that programmers now use them without question. The other day, one of my colleagues good-naturedly complained that his students were all using LambdaExpressions in their code, before he'd had a chance to give them some material he'd written on how to use LambdaExpressions.
- Again, this appears to be become their OOP and/or libraries are limited. It's a band-aide over other areas. (Yes, I know you disagree, let's not reinvent that debate here in the name of OnceAndOnlyOnce.)
- I think you're wrong. You've made no case to indicate otherwise. Your attempts to demonstrate how OO code could be better, and thus obviate the desire for HOFs/lambdas, raises more questions than it answers. It's quite possible that your suggested alterations to conventional OO languages couldn't possibly work.
- PageAnchor swiss15
- I never claimed "better". Where did you get that? Back to the Swiss army knife analogy, I'm only claiming a knife with 12 blades is GoodEnough rather than introduce a knife with 15 to have more choices of blades which may result in blades that may be a somewhat better fit to a given situation, BUT at the expense of a fatter knife. Typical developers will get confused by too many paradigms/language-idioms in my observation (yes, I know you disagree per your observations, you don't have to state so).
- So your argument is that your hypothetical OO language could actually be worse, but at least it doesn't use HOFs/lambdas? I presume you're aware that functions are a FunctionalProgramming construct. Do you propose to remove functions from your language, and thus reduce the number of blades to 11? Shouldn't that be even easier to understand?
- Yes, it could be slightly worse, I will concede, with the WaterbedTheory tradeoff that we are using less paradigms/language-idioms. (In the swiss-knife analogy, fewer blades will typically mean that the fit-to-task of the selected blade will on average be lower, assuming the optimum existing blade is selected.) And one cannot get rid of functions without getting rid of methods. It's possible to only use methods, but they may often "look like" what's typically called "functions". Anyhow, I'm comparing OO to HOF's, not OO to functions. The AlgolFamily idioms are more or less stuck in stone at this point, for good or bad. Perhaps it's QwertySyndrome, but I don't have a second copy of Earth to test that on. God won't let me play with his copy yet.
I suspect you feel this is a "huge lesson in miscommunication" because you lack some of the background knowledge about conventional OO which is assumed. Again, a good reason to do some reading on conventional OO languages.
I suspect you feel this is a "huge lesson in miscommunication" because you lack some of the background knowledge about writing on non-academic subjects. Again, a good reason to do some reading on how-to-write, especially with regard to making useful examples.
Beyond engaging in an appropriate degree of review and reflection to achieve the nominal level of clarity expected of all writing, I have no interest re-training to become your personal technical writer. My usual audience is a mix of beginners, experienced technical people (i.e., "non-academics"), and academics. I note that out of all of them (and there are quite a few of them), you are the only one to consistently have difficulty with my explanations. Therefore, if you can't understand them, it's up to you to find supplementary material. It's not up to me to provide it.
And I've seen good writing, and yours is not it by far. (Although, I will consider the possibility that different writing/teaching techniques may match up better to different minds such that a communication technique that works well for one person may not work well for another and vice verse.)
Of course my writing isn't good. Like probably everyone else here, I'm not a technical writer. I'm a programmer and academic. If you can only understand writing done by a professional technical writer and can't understand writing about your field from one of your peers -- writing that many of your peers, including those with presumably much less experience, can understand -- then the problem is yours.
For one, I have not seen a decent survey. Second, I'm not your peer because I am not an academic. Anyhow, I personally find your writing style difficult and frustrating. If others love it, so be it.
You might try reading what I write more than once. It's technical writing, not a novel. Sometimes it takes more than one pass to understand something. Sometimes it takes a stronger background in the subject material, so you need to do some googling and read something else before coming back and trying again.
My rule of thumb is 3 passes before I give up. If I don't know what you are getting at after 3 passes, then it's rare that 4+ will do the job.
That explains so much. If that's how you've treated complex or novel technical material throughout your education and career, it's not surprising that your knowledge about many subjects is superficial. Novel ideas aren't understood immediately, or even in 3 passes, unless they're trivial. 20 passes may be more appropriate, often accompanied by trying relevant math and/or code on your own.
I meant for colloquial material, not equations or what-not. Your writing is not about novel things. It's just...bad. If you mean your writing style is "novel", then perhaps I agree. But based on past experience with YOUR writing, volume of repeat readings is not the solution to deciphering it. If 10 rereads didn't work several times in the past, why would it start working now (for similar material)? One definition of "stupid" is doing the same thing repeatedly but expecting a different result from the past.
Even colloquial material with which you're not familiar is (by definition) novel. If you could point out what is bad about my writing, then perhaps I can correct it. However, increasingly it appears that the problem is not my writing style per se, but a result of my assumption that you are an experienced OO and functional programmer -- at least to roughly the degree that I am, and that most of your correspondents appear to be -- and that your criticisms of language features are based on your extensive experience of both paradigms. Is that not the case?
Past attempts to analyze your writing style generally leads to topics similar to LaynesLaw and/or FractalVagueness. In general I tend to view programming as a process of human minds, while you view it as direct symbolic analysis and patterns. I approach it like a sociologist and economist and you approach it like a mathematician. Our reference points are simply too different. And I fully admit I am NOT an experienced FP programmer. I've explained the reason somewhere on this wiki already. Does your writing assume heavy experience in FP?
It assumes a moderate experience of FP. It assumes a moderate experience of OO. I suspect you experience LaynesLaw and/or FractalVagueness due to your relative inexperience in both FP and OO. I'll endeavour to make my explanations not assume as much experience, and it's true that I approach code mathematically. To me, it is mathematics. Social and economic issues are certainly relevant, but I consider them part of ProjectManagement, not the code itself.
Code is language for (mostly) communicating ideas to and among humans. ProjectManagement involves code and coder management. You cannot separate them because grokking is an economic issue.
Sure you can. Most of this Wiki, and any programming language reference guide or manual is proof that you can separate them. How many of those texts rank features in terms of how hard/easy they are to grok? How many programming textbooks have a chapter on how the difficulty of understanding certain programming concepts described in the book will have an impact on some future projects' ability to integrate that code into applied projects?
"Can" doesn't say anything about economics. A shop "can" write all code in BrainFsck, for example. Does one need an OfficialCertifiedDoubleBlindPeerReviewedPublishedStudy that BrainFsck slows down staff before a BF fan should heed? SoftwareEngineering sorely lacks OfficialCertifiedDoubleBlindPeerReviewedPublishedStudy's and that's why our field has embarrassing DisciplineEnvy compared to fields that are more empirically driven, like biology or chemistry. Lack of studies on a given factor does NOT mean that factor is not important. Unfortunately, we mostly only have anecdotal evidence to use as a proxy for systematic studies. Authors tend to write on metrics that are easier to measure, or just write How-To's, because it creates less drama and better reviews. SovietShoeFactoryPrinciple in authorship. Nobody gets into trouble for writing how-to's, and they sell.
That's right, "can" says nothing about economics. That's because most of us don't care, beyond noting that higher-level programming is generally more expressive than lower-level programming. That's why we don't program in BrainFsck. If you're concerned about the grokkability of language features, then you do the research, because you care about it. I don't. Most of us don't, so we're not going to do it for you, and we're not particularly interested in talking about it.
Do you think it helps your understanding of the general industry of software to ignore factors that are hard to study or that don't interest you? Don't you think you'd be more well-rounded if you paid more attention to a wider variety of factors? You are the Greek in the EvidenceEras, stagnating at the on-paper stage. I suppose I also cherry pick what I focus on based on interest, but at least I admit it's because I'm less than perfect rather than because some universal rule of logic says it's not important.
I do believe the most important future discoveries will come from the study of WetWare and NOT on teasing about symbols on paper. It's almost like spending a billion dollars to improve the efficiency of gasoline engines by studying the engine itself but only a million on driver behavior and habits. It's fairly obvious it's best to spend the next available million on driver behavior studies than on yet more engine-centric research. The low-hanging fruit of the math side has already been picked.
I cannot do formal grokking/WetWare studies myself, but perhaps I can float some suggested viewpoints, observations, and frame of references that may help guide the future formal explorers of IT WetWare.
Suggesting viewpoints, observations and frame of references is excellent. Using them to deprecate anything that smacks to you of FunctionalProgramming because you believe it to be a fad, is not.
I have a right to my opinion about what is a fad based on personal observations, and such opinions are an acceptable norm on this wiki. So stop fucking complaining about that again repeatedly redundantly again. It is soooooooo fucking annoying and whiny and repetitious.
Is your rudeness necessary? You have a right to state what you think is a fad. Based on evidence that is no more than your personal observations, you have no right to try to limit our use of what you think is a fad.
What, did I send a killer robot to your house to keep you from writing HOF's? I hacked your Roomba to spit dust all over any HOF's. Or the Clockwork HOF approach?
As much as that would be EPICALLY COOL, no you didn't do that. You do like to hop into every mention of anything related to FunctionalProgramming to point out that you think it confuses developers.
I'm not aware I've done that. But I do have a right to state my opinion about FP if the topic of FP happens to comes up. If I factor my opinion poorly, show me the spot where I did it, and I'll attempt to clean it up.
I think it would be best, when FP happens to come up, to stick a "See also TopOnFunctionalProgramming (or some such)" at the bottom. Interjecting your opinions mid-stream -- which, historically, you have done -- is not appropriate.
Projection. Anyhow, I think this topic is cooked because we are going right back to our usual bicker-loop topics. My summary can be found at PageAnchor swiss15.
What does "projection" mean, here?
That seems pretty obvious. Take a guess, I'm curious how your peculiar mind reads it.
You appear to be referring to the (largely Freudian) psychological notion of projection, but I'm not clear what you think I'm "projecting" onto you. Generally, it refers to denying something in myself and attributing it to you. I'm not sure what you're referring to, here. I haven't gone around randomly interjecting anti-TableOrientedProgramming comments into the TableOrientedProgramming pages, for example.
Example "Thelma"
Let's assume a menu option in a GUI desktop app sorts the text lines of the current active document. When the menu option is selected, the current contents are copied to a work file, such as the "temp" directory. (Copy step not shown). If the function below returns True, then resulting work-file contents becomes the active document, else it's ignored (not shown, possibly to be overwritten via a future operation, or deleted when app ends).
func sortWorkFile(workFilePath, foo): Boolean {
var msg = ""; // error message
class fsort extends fileSorter { // to define AND run sorter
main(workFilePath); // constructor, run sort (optional)
method comparer(x, y) {if (foo) {...}} // collation criteria
// optional error handlers
method onUriError(e) {msg="Problem with file path used for work files";}
method onResourceFullError(e) {msg="Unsufficient disk or RAM space.";}
method onIoError(e) {msg="Problem reading or writing work file.";}
method onAccessError(e) {msg="Problem with file access rights for work path.";}
method onOtherError(e) {msg="Uncategorized error: " + e.descript;}
} // end-class
if (isBlank(msg)) {
return True; // no errors
} else {
win.popUpMessage(msg + " Operation cancelled.");
return False;
}
}
Keep in mind we are NOT obligated to use the On-X-error methods for error handling. If a language supports it, Try/Catch style can be used instead, per coder choice. I am not necessarily recommending the above API design for all intended uses or domains. Although, it may make a nice language-wide design for all non-trivial library operations to support this kind of convention.
Example "Louise"
func sortWorkFile(workFilePath, foo): Boolean {
class fsort extends fileSorter { // to define but not yet run sorter
method comparer(x, y) {if (foo) {...}} // collation criteria [1]
} // end-class
var msg = ""; // error message
switch (e = (new fsort()).sort(workFilePath)) { // run sort
// optional error handlers
case fileSorter.onUriError {msg="Problem with file path used for work files";}
case fileSorter.onResourceFullError {msg="Unsufficient disk or RAM space.";}
case fileSorter.onIoError {msg="Problem reading or writing work file.";}
case fileSorter.onAccessError {msg="Problem with file access rights for work path.";}
case fileSorter.onOtherError {msg="Uncategorized error: " + e.descript;}
default {return True;} // no errors
}
win.popUpMessage(msg + " Operation cancelled.");
return False;
}
Note that it's slightly simpler than "Thelma" and it doesn't rely on potentially fragile method overriding to support error handling. Note that example Thelma appears to be missing the instantiation of 'fsort'. If we assume it's somehow implicit, remove '(new fsort())' above.
I'm not clear what 'foo' is intended to do. Using a lambda expression, I'd probably do something like this:
func sortWorkFile(workFilePath, comparer): Boolean {
var msg = ""; // error message
switch (e = fileSorter.sort(workFilePath, comparer)) { // run sort
// optional error handlers
case fileSorter.onUriError {msg="Problem with file path used for work files";}
case fileSorter.onResourceFullError {msg="Unsufficient disk or RAM space.";}
case fileSorter.onIoError {msg="Problem reading or writing work file.";}
case fileSorter.onAccessError {msg="Problem with file access rights for work path.";}
case fileSorter.onOtherError {msg="Uncategorized error: " + e.descript;}
default {return True;} // no errors
}
win.popUpMessage(msg + " Operation cancelled.");
return False;
}
Then sortWorkFile is invoked something like:
sortWorkFile("/tmp/", (x, y) -> x.compareTo(y));
Note that the 'comparer' is passed into sortWorkFile (and then fileSorter) as a lambda expression, and fileSorter.sort(path, comparer) is assumed to be a static or "class" method.
Note that none of these examples show how what's being sorted (lines in a document) are referenced by the fileSorter.
[1] Foo was only intended to illustrate some minor parameter that has a minor impact on collation. Typically one passes in a switch or two for miscellaneous things, such as maybe "ignoreCase". I agree that passing in the comparer logic is more general than setting a switch, but that also makes the interface more complex. An ignore-case parameter is instantly recognizable, while a comparer operation may confuse or delay the coder studying the API doc. It's arguably a HOW instead of a WHAT.