Reputation: 11963
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
Reputation: 53665
My strategy follows Don's suggestions fairly closely:
go
if it is the "worker" or otherwise very similar in purpose to the original function.step
and start
for args to a fold.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
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
Reputation: 38893
I tend to call boolean valued functions p
for predicate. pred
, unfortunately, is already taken.
Upvotes: 3
Reputation: 137947
General rules for locally-scoped variable naming.
f
, k
, g
, h
for super simple local, semi-anonymous thingsgo
for (tail) recursive helpers (precedent)n
, m
, i
, j
for length and size and other numeric valuesv
for results of map lookups and other dictionary typess
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