News for hackers. Stuff that matters.
Re:How to reduce the cognitive load of your code by ergothus on Tuesday March 29, 2016 @02:11PM (#11383544)
I agree with everything you said. As an old Perl guy, I found it hilarious that Perl has a reputation for illegibility, while I find it easier to read than most Java, because in Perl I can express my intent, and in Java it's buried in the noise.

One cognitive problem I've not yet found a good solution to:

So I have a high level routine to, say, sort the items in a display. Said sorting has some cascading effects, so I'll have a high level function that calls some mid level functions that call some lower level functions.

But at some point I get a name clash between a high level function and a low level one. Like...I'm calling the high level sortDisplayedItems, (or sort_displayed_items) and there's a low level function that ACTUALLY sorts them (as opposed to handling the other bits). Sure, I can give it a distinct name (e.g. sort_items), but when skimming the code, it's unclear which is which. If I have to mentally parse the code to understand what is happening, I've failed at readability/maintainability.

How have others handled this?

Re:How to reduce the cognitive load of your code by gknoy on Tuesday March 29, 2016 @02:24PM (#11383643)
> I'm calling the high level sortDisplayedItems, (or sort_displayed_items) and there's a low level function that ACTUALLY sorts them (as opposed to handling the other bits).

You're absolutely right: naming things is hard. When I was writing Scheme long ago [in university], our convention was to name helper functions with `-helper` in the name:

    sort-foos
    sort-foos-helper
    sort-foos-merge-helper
Now that I write mainly in Python, the convention seems to be to use underscores to indicate things that you should ignore. Usually that's done at a method level, where your consumers are calling the object's public API (sort), so the object's namespace helps reduce such name collisions. The addition of triple-quote docstrings for method comments helps a lot at clarifying purpose as well:

    class FooSorter(object):

        def sort(self, foos):
            """Sort an iterable of Foo objects"""
            # ... sanity-check inputs ...
            self._sort(foos)  # do the sort operation

        def _sort(self, foos):
            """
            Sort things we already know are Foo objects.
            This is a helper for sort(), which already 
            sanity-checked our inputs.
            """
            # do the actual sorting
Re:How to reduce the cognitive load of your code by yekim on Tuesday March 29, 2016 @03:00PM (#11383944)
Wrt Perl being readable / legible; I think most find Perl hard to read because there are 3-4 different ways to do the same thing. And many of the more "advanced" ways of doing things rely on short, terse single characters that act essentially like magic and behave differently in different situations. At least this is what I remember as I climbed the Perl ladder.

Contrast this with say, Python, where readability is highly valued and they have a one-way-to-do-certain things mentality, like sorts for example, means code is generally easier to grok, debug, and extend.

Thus, readability becomes especially important when you have to deal with someone else's big ball o' mud and you need to fix a high stress, high visibility, lines down issue at one in the morning because the original programmer is long gone, didn't leave any comments in their code, and decided to play "look at me, I'm so clever with how I use this operator", but forgot to account for a certain scenario that manufacturing decided to roll out the other day without telling anyone. Can I get an amen?

Re:How to reduce the cognitive load of your code by ergothus on Tuesday March 29, 2016 @04:10PM (#11384474)
I won't disagree with most of what you said (though I will say that having one way to do it does NOT mean that said one way is particularly readable/maintainble - looking at you, Java).

That said, for every time I've hated that someone got terse and clever, I've loved that I wrote something that _read_ well, particularly when returning to previous code I don't remember. Coding is like a language, you can use that expressiveness to be terse, to be rambling, or to be clear. Learning to do so is a skill that has to be learned (cost), but can be more expressive (benefit). Languages (or patterns) that restrict that reduce the cost, and reduce the benefit.

Consider, for example, that Python takes great pride in it's explicit readability, and yet it chose "lambda" as a keyword (making no one happy outside of higher math), that "def" was chosen instead of "define", and that one of the most powerful parts of the language (comprehensions) are highly prone to using secret knowledge. [] works differently than (), for example.

So, what you've described are all valid reasons that Perl has a bad rep...but I don't blame Perl for them anymore than I blame JS for the hideousness that is the DOM, or Python for the bad scripts that have been written in it. Regardless of the language, cleverness and/or terseness without regard to future people (including your future self) is just not a good practice.

Re:How to reduce the cognitive load of your code by mavelikara on Tuesday March 29, 2016 @05:24PM (#11384997)

  though I will say that having one way to do it does NOT mean that 
  said one way is particularly readable/maintainble - looking at you, 
  Java
  ..
  So, what you've described are all valid reasons that 
  Perl has a bad rep...but I don't blame Perl for them 
  anymore than I blame JS for the hideousness that is the 
  DOM, or Python for the bad scripts that have been written 
  in it.
Will you be willing to extend the same charity to the bad Java code you have seen? Or do you have specific complaints for your harsh comments singling out Java?
Re:How to reduce the cognitive load of your code by ergothus on Tuesday March 29, 2016 @05:48PM (#11385187)
If you're looking for me to say I'm biased and occasionally say some exaggerated complaints....yeah, that's true. But as I still feel the basis has reasoning, I'll continue to answer:

Yes and no.

For the bad Java code I've seen, Java does not bear the responsibility. (And I've seen my fair share of bad Java code because I spent 5 years as a Java dev in a place that had a collection of bad coders and worse code). I've also (since) seen great coders with quality code (in Java), but I definitely retain some bias from my earlier experience.

BUT, when your language design promotes certain things, then yes, that language takes the hit. For example, some of the Perl options are just plain terrible (e.g. changing 0 indexing) and Perl takes the blame. Perl also moved away from those, recognizing the errors. Books like Higher Order Perl (and associated efforts) came out to promote best practices.

Java expressly sought verbosity, and thus takes the blame for verbosity at the expense of clarity.

Other practices are encouraged by the community. That's a gray area - it's not the language at fault, but...it's common. These can range from small and almost petty (Perl promoted underscores in variable names for REASONS, while Java promotes camelCaseBecauseIGuessSomePeopleLikeToSquintToParseVariables.) Not really the LANGUAGE fault, but definitely an expectation. (and one I've grown accustomed to in the name of working with others). Other problems are larger (Kingdom of Nouns) - still not the Language fault, but you can expect it if you're dealing with the code.

Many a time I've tried to trace through some Java code...weaving through "Impl" classes and factories, trying to find where some piece of logic is implemented...only to find myself in an empty class definition. That's a fault of both encouraged practices and poor practices.

When people complain of poor Perl code, it's usually code that was either written by someone that didn't do that as their main job, code from newbies, or code that has changed hands repeatedly with no one trying to make it maintainable. Most any code will suffer in those conditions. Java, thus far, has had the worst overall quality of code in "professional" collections that I've experienced. But then again, I've really only seen code in any quantity in 3 or 4 languages, so....anecdotal experience is anecdotal and subjective.

(I have to give props to Python here...while I have a few nits about a few things, and I'm not well-versed enough to discuss code quality of anything I skim, I've seen a lot of code that was probably "bad" but nonetheless avoided some mistakes that are commonly made in other languages, particularly with new coders.)

Re:How to reduce the cognitive load of your code by Retric on Tuesday March 29, 2016 @02:41PM (#11383787)
One approach that can help is to name things based on what the functions actually do.

  validateSortDisplayedItems
  {
   validation Logic
   ...
   sortDisplayedItems(); //Actually sorts items.
  }
This can be harder to maintain, but really long names end up a useful code smell.
Re:How to reduce the cognitive load of your code by ergothus on Tuesday March 29, 2016 @02:48PM (#11383852)
I find it a bit...incorrect.

I mean, your above code LIES. If I call validateSortDisplayedItems, I don't validate, I validate AND sort. Plus, what do you do if you have "validateItems" and "sortItems", and then one function that calls them each in turn? call it "validateAndSortItems"? Yuck.

Re:How to reduce the cognitive load of your code by Retric on Tuesday March 29, 2016 @03:20PM (#11384121)
Depends on the Validation Logic. I like the style that basically works like this:

  validateSortDisplayedItems
  {
   if(!DisplayedItemsValid())
   {
     CorrectDisplayedItems();
     if(!DisplayedItemsValid())
     {
       DisplayValidationError();
       return;
     }
   }
   sortDisplayedItems(); //Actually sorts items.
  }
AKA validate means try and make valid, not verify that data isValid. So, you can't just do if(isValid) sort; the bonus is unrecoverable errors end up at leaf nodes vs. the happy path.

At the high level, your function might be sortClicked, which can then respond to that by calling a wide range of functions. (userCanSort,SortData,UpdateDisplay)

PS: I find the validate > correct loop is generally the important and error prone part of code, so I give it priority. The happy path where everything works is more or less an addendum.

Re:How to reduce the cognitive load of your code by kazinator on Tuesday March 29, 2016 @04:36PM (#11384668)
The problem is that the validateSortDisplayItems is some sort of public function. The caller doesn't care about the validation, only that the items are sorted.

All kinds of functions everywhere validate their arguments; that doesn't deserve to be in their name.

Re:How to reduce the cognitive load of your code by Retric on Tuesday March 29, 2016 @05:26PM (#11385005)
The question is why you need a helper function? IMO, public functions are a special case and your generally better off using different naming schemes for internal and external functions.

Granted, if it's pure pass-through then reusing the name is a non issue. Foo(x){foo(x);} is not confusing. Foo(x){junk; foo(x);} is.

One option is Foo(x){validateForFoo; ValidedFoo(x);}

Alternatively for private functions JunkFoo(x){junk(x); foo(x);} And replacing JunkFoo with a meaningful name if possible.

Again though, public function Foo(x) should really call internal function Bar(x) otherwise it's really easy to couple things on both sides of an interface.

Re:How to reduce the cognitive load of your code by kazinator on Tuesday March 29, 2016 @09:36PM (#11386366)
The computation represented by "foo" could actually not be contributing any new semantics to "foo". It could just be some boiler plate that is needed to extract and isolate various resources out of context "x", so that these pieces can then be passed to the low-level "foo", which knows nothing of the aggregate "x". "junk" could be some locking an dunlocking, or debugging, or preparing the display in some way or whatever.

If someone wants to know how to name the low-level helper which does the real work and the interface over it, we just have to take it for granted that the separation is necessary and that it makes sense for either function to have the name "displaySortedItems" or whatever.

Re:How to reduce the cognitive load of your code by Retric on Wednesday March 30, 2016 @09:57AM (#11389276)
If your stripping boilerplate then your operating on different things. Which seems more readable?

  SortGuiElement(){junk; SortUserNames(); junk;}
  SortUserNames(){junk;}
vs.

  SortNames(){junk; sortNames() junk;}
  sortNames(){junk;}
It might seem obvious and the code might be identical, but I see the second case very frequently in other peoples code.
Re:How to reduce the cognitive load of your code by kazinator on Wednesday March 30, 2016 @11:36AM (#11390057)
Re-using an identifier with just a case difference is criminal.

You want:

  SortNames(){junk; SortNamesImpl(); junk;}
  sortNames(){junk;}
or whatever: SortNamesGuts(), InternalSortNames(), DoSortNames(), LowLevelSortNames(). Anything but just flipping the case of a letter or two in the "SortNames" identifier.
Re:How to reduce the cognitive load of your code by Retric on Wednesday March 30, 2016 @12:13PM (#11390335)
I agree that reusing the same name with different capitalization is bad, but I have seen this from several developers across many different teams and several languages. These same people often have Foo(x){junk; Foo(x,y,z); more junk;} sure if it's a pass though that's fine but if you have 10 foo('s) that all have internal logic then please come up with some actual names.

Amway IMO, SortNames() and Internal/Do/LowLevel/SortNames() are almost as bad because just looking at the names it's not obvious what's going on. Yes, visually they look different which helps and consistency can make things even more readable. But, even just SortNamesHappyPath() gives some idea of what's going on.

Re:How to reduce the cognitive load of your code by 0xfadedace on Tuesday March 29, 2016 @11:07PM (#11386685)
A convention I've seen in Java libraries and that I'm fond of is:

  public sort() {
    preconds()
    try {
      doSort()
    } catch (ex) {
      handle(ex)
    }
    postconds()
  }

  private doSort() throws Exception {
    // core impl
  }
Re:How to reduce the cognitive load of your code by bcheung on Tuesday March 29, 2016 @03:55PM (#11384379)
Make them separate functions each with their own local state or use namespacing. The problem sounds like too much state in a single algorithm. Break it down into subalgorithms each with their own state.
Re:How to reduce the cognitive load of your code by kazinator on Tuesday March 29, 2016 @04:32PM (#11384633)
I always call the low level functions with some clear name related to the high level one by convention:

- sort_displayed_items_guts

- sort_displayed_items_impl

- ll_sort_displayed_items (ll == low level)

- do_sort_displayed_items