gcbenison
gcbenison

Reputation: 11963

What makes a good name for a helper function?

Consider the following problem: given a list of length three of tuples (String,Int), is there a pair of elements having the same "Int" part? (For example, [("bob",5),("gertrude",3),("al",5)] contains such a pair, but [("bob",5),("gertrude",3),("al",1)] does not.)

This is how I would implement such a function:

import Data.List (sortBy)
import Data.Function (on)

hasPair::[(String,Int)]->Bool
hasPair = napkin . sortBy (compare `on` snd)
  where napkin [(_, a),(_, b),(_, c)] | a == b = True
                                      | b == c = True
                                      | otherwise = False

I've used pattern matching to bind names to the "Int" part of the tuples, but I want to sort first (in order to group like members), so I've put the pattern-matching function inside a where clause. But this brings me to my question: what's a good strategy for picking names for functions that live inside where clauses? I want to be able to think of such names quickly. For this example, "hasPair" seems like a good choice, but it's already taken! I find that pattern comes up a lot - the natural-seeming name for a helper function is already taken by the outer function that calls it. So I have, at times, called such helper functions things like "op", "foo", and even "helper" - here I have chosen "napkin" to emphasize its use-it-once, throw-it-away nature.

So, dear Stackoverflow readers, what would you have called "napkin"? And more importantly, how do you approach this issue in general?

Upvotes: 11

Views: 3638

Answers (4)

Dan Burton
Dan Burton

Reputation: 53665

My strategy follows Don's suggestions fairly closely:

  1. If there is an obvious name for it, use that.
  2. Use go if it is the "worker" or otherwise very similar in purpose to the original function.
  3. Follow personal conventions based on context, e.g. step and start for args to a fold.
  4. If all else fails, just go with a generic name, like f

There are two techniques that I personally avoid. One is using the apostrophe version of the original function, e.g. hasPair' in the where clause of hasPair. It's too easy to accidentally write one when you meant the other; I prefer to use go in such cases. But this isn't a huge deal as long as the functions have different types. The other is using names that might connote something, but not anything that has to do with what the function actually does. napkin would fall into this category. When you revisit this code, this naming choice will probably baffle you, as you will have forgotten the original reason that you named it napkin. (Because napkins have 4 corners? Because they are easily folded? Because they clean up messes? They're found at restaurants?) Other offenders are things like bob and myCoolFunc.

If you have given a function a name that is more descriptive than go or h, then you should be able to look at either the context in which it is used, or the body of the function, and in both situations get a pretty good idea of why that name was chosen. This is where my point #3 comes in: personal conventions. Much of Don's advice applies. If you are using Haskell in a collaborative situation, then coordinate with your team and decide on certain conventions for common situations.

Upvotes: 1

hammar
hammar

Reputation: 139840

In cases like this, where the inner function is basically the same as the outer function, but with different preconditions (requiring that the list is sorted), I sometimes use the same name with a prime, e.g. hasPairs'.

However, in this case, I would rather try to break down the problem into parts that are useful by themselves at the top level. That usually also makes naming them easier.

hasPair :: [(String, Int)] -> Bool
hasPair = hasDuplicate . map snd

hasDuplicate :: Ord a => [a] -> Bool
hasDuplicate = not . isStrictlySorted . sort

isStrictlySorted :: Ord a => [a] -> Bool
isStrictlySorted xs = and $ zipWith (<) xs (tail xs)

Upvotes: 2

sclv
sclv

Reputation: 38893

I tend to call boolean valued functions p for predicate. pred, unfortunately, is already taken.

Upvotes: 3

Don Stewart
Don Stewart

Reputation: 137947

General rules for locally-scoped variable naming.

  • f , k, g, h for super simple local, semi-anonymous things
  • go for (tail) recursive helpers (precedent)
  • n , m, i, j for length and size and other numeric values
  • v for results of map lookups and other dictionary types
  • s and t for strings.
  • a:as and x:xs and y:ys for lists.
  • (a,b,c,_) for tuple fields.

These generally only apply for arguments to HOFs. For your case, I'd go with something like k or eq3.

Use apostrophes sparingly, for derived values.

Upvotes: 25

Related Questions