Errors In Constructor

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?

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.

        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)
       }      
. 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".

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:
         print "A: I gave evidence.";
         print "B: That's not evidence.";
         while (forever) {
            print "A: Yes it is.";
            print "B: No it's not.";
         }
. Case 2. If you allow the constructor to cause errors but not throw exceptions: Case 3. If you strictly use the constructor for reliable, error-free initialisation: 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.

I've given concrete examples above that illustrate the issue. They're even runnable, if you create a simple implementation of Processor.

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.


EditText of this page (last edited October 14, 2014) or FindPage with title or text search