Skip to content

Instantly share code, notes, and snippets.

@cgrand
Last active September 30, 2016 11:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cgrand/62bff7678034fb4f4d8be31016a734e5 to your computer and use it in GitHub Desktop.
Save cgrand/62bff7678034fb4f4d8be31016a734e5 to your computer and use it in GitHub Desktop.

#The "Double bananas" (( smell

Usual offenders:

  • inside a threading macro to "protect" a fn; action: stop the threading or refactor it with as->
  • ((comp f g) x) or ((juxt f g) x) or ((partial f x) y) (the three most frequent); action: unroll the comp/juxt/partial
  • in other cases it's worth considering giving the poor thing a name

In general I find ((f x) y z) hard to follow, harder than (g (f x) z) for example. In the second case the role of (f x) is given by g (eg (f x) must be a predicate). In the first case the role of (f x) is given by f alone. To recap: with (g (f x) z) you have two sources of information to infer the nature of (f x) (and may realize there's a glitch when the two sources are discordant), with ((f x) y z) you only have one source of information.

Giving a name to (f x) allows to reintroduce a second source of information (when the name is sensible).

Last, when ((f x) y z) occurs frequently it may be worth invertigating if:

  • f could accept extra args -- and turn those expressions in (f x y z)
  • or a helper around f could be created -- this occurs when a lib is used in a way unforeseen by its author.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment