Function names, left-to-right coding style

Hi everybody!
Since I have started coding in PureScript a couple of month ago, I find myself constantly switching between $ and #, <$> and #, >>> and <<<, do and >>=, letin and where. There is a reddit post which tasks about this:

Also, I thought functions are best expressed with verbs like replaceSomething, convertSomething or getSomething to distinguish them from constants. Then I saw function names like head, headers, handlers, fromFoldable, toEither.

In the reddit post the user MtnViewMark makes a strong case of “The idiomatic style, models how such expressions are said in English”. As I understood it, code as you speak in math, e.g. y = sin(... as “y equals the sin of …”. >>= translates to “is bound to” I guess. Thus, constants should be x1, h etc. and nouns for functions names. where fits better to = then letin.

On the other side, I think idiomatic style breaks down pretty quickly because a lot of names already exist and short sentences are easier to read than long ones. Thus function names should be verbs, usage of #,<#> and the code reads like “replace something and then convert and then note”.

Has anybody come up with a consistent approach of how to name new functions other than is... for checks, mk... for constructors and design for qualified import or can point to a library or code which reads well?

2 Likes

Hi, welcome!

This is something I’ve gone back and forth on a couple of times. I generally prefer right-to-left style at the moment and I think it is more idiomatic, in the sense that it’s more common in the code that I’ve seen. I suspect that the most important factor in readability is how familiar the reader is with the style in use; I don’t think either style is strictly better in of itself. I do think that switching between them too much in the same codebase makes things more confusing, so I generally try to pick one and stick to it.

One thing that I am fairly confident about is that trying to structure your code so that it reads like natural language is a fool’s errand. As much as we might wish otherwise, code just isn’t natural language, and code that has been made to look like natural language often just ends up being tortured and strange. The most important thing is getting the types and the functions right so that they accurately model your domain; the names can come afterwards.

Generally I’d advise not worrying about any of this too much! I think the best way to develop your thoughts on this subject is to just write a bunch of code and see what works well for you.

10 Likes

Welcome! We just had a debate about this at work, so I’ll add my $0.02. A work buddy of mine essentially argued @hdgarrood’s point:

I do think that switching between them too much in the same codebase makes things more confusing, so I generally try to pick one and stick to it.

I tend to come down on the side of judging it case-by-case and choosing the right direction for a given expression. To me, it seems that left-to-right style emphasizes how to perform the calculation and in some ways obscures what it actually evaluates to, while right-to-left emphasizes what your expression is and in some ways obscures how you get there. In the former you’re starting with the data you have, and then a sequence of transformations that you have to read through to the end to see what actually comes out the other side. In the latter you’re starting with what gets returned, and follow it with all the arguments needed to produce that thing, and you have to read through to the end to see what all went into producing that value.
In that reddit post, the argument you referenced makes the point that you often do favor left-to-right when working with a monad (because it inherently sequences your computation), but I tend to think the same argument can apply to more than just monads - it applies whenever an expression is easier to understand by thinking through the sequence of steps (a lot of times for me that happens when the expression grows pretty long).

Regarding function names, I pretty strongly favor verbs for Effects, but nouns for pure functions. That to me was part of the mind shift for functional programming. In imperative programming functions are very, very different things from values, but in functional programming, a function is just a value that isn’t quite there yet - it needs some extra bit of data passed in first. Another way of looking at it is pretty much every function could have “get” or “calculate” in front of it to make it a verb, but what’s the point? That’s just sort of implied and not very relevant. (getHead, getHeaders, getHandlers, …)

6 Likes

ok, let’s make an example: HTTP server: Api calls Application, Application returns an union type error, should be rendered to a string.

renderError :: ApplicationError -> Failure --(pure function)
renderError CREDENTIALS_NOT_FOUND = renderUnauthorizedMessage "username or password not found"
renderError USERNAME_EXISTS = renderUnprocessableEntity "username" "exists"
renderError EMAIL_EXISTS = renderUnprocessableEntity "email" "exists"
renderError USERNAME_NOT_FOUND = renderNotFoundMessage "username not found"
renderError UPDATE_FAILED = renderUnprocessableMessage "update failed"
renderError INSERT_FAILED = renderUnprocessableMessage "insert failed"

(renderUnauthorizedMessage etc. does some pure extra wrapping stuff from another module which does not fit in one line.)
@ntwilson If understand you correctly, you would just call it failure. And if there are ApplicationError and ApiError, it’s applicationFailure and apiFailure, right?

Hmmmm, I may have stated my position slightly too strongly. Maybe I would better state my point that I feel strongly that one shouldn’t have a problem with function names that are nouns, or shouldn’t try to coerce all function names into verbs. But the flip side is also kind of true for me. Sometimes it’s awkward if you try to coerce all function names into nouns. The distinction between (pure) functions and values is significantly less pronounced in functional programming than in imperative programming, and so it makes less sense to try to make a naming rule to distinguish them.

For the particular example, I might use failureFromApplication, and failureFromApi, but also I personally wouldn’t have a problem with renderFailure. If the only way to produce a Failure was via ApplicationError, I probably would call the function just failure. But if there was a more direct way to construct a Failure, I think renderFailure could work really well.

As far as precedent, there are plenty of examples of functions with names that are verbs, and that I think make a lot of sense (map, filter, groupBy, sort, …). But I think it would be a shame to take some of the functions that are nouns and try to make them verbs (getLength, getLast, getHead, …). Sorry, I guess I think there’s precedent for both, and I personally like using whichever expresses more naturally, though I suspect that’s not the answer you’re looking for :wink:

3 Likes

Regarding left-to-right vs right-to-left. I do both depending on what I think is more readable - sometimes I even use both in same expression! For short things I nearly always do right-to-left as I find it’s easier to read. For long things with many steps, I generally go for a multi-line approach with a single function application on each line. And for long things that are wrapped in a type constructor, I usually use both, like this

TypeConstructor
  $ value
  # function1
  # function2
  # function3
  # function4

Regarding how to name things, my advice is to not worry about finding the “correct” answer - it doesn’t really exist. Just name things in a way that seems right enough. You can always go back and change the names later if you find a better name.

1 Like