Hacker News new | past | comments | ask | show | jobs | submit login
How to Name Clojure Functions (stuartsierra.com)
120 points by henrik_w on Jan 11, 2016 | hide | past | favorite | 34 comments



I think one issue that pops up in many languages is the distinction between functions that have side effects and those that don't. In C++ you see standard library higher-order-functions that accept lambdas or other functions, but the documentation says "the function passed should not produce side-effects." You also see this in Clojure functions, and functions you might write might also expect only side-effect-free functions. The problem is that the functions you pass may not be functions you wrote, they might come from another library and then to be most confident, you must go and look up the source or docs to feel sure. I don't think a naming convention is enough, since many may not use the convention. For languages where this is important, I wish there was a compiler or runtime check for these things so the user didn't have to take responsibility. Almost as if functions were split into two "function types", those with and those without side effects. Maybe starting at the lowest level of the core language library, some functions would be noted as causing side-effects, and then any function built that called those functions would automatically be categorized as such, and on up to the top, so that side-effects-beget-side-effects and function parameters that require non-side-effect-functions wouldn't allow those to pass.

Anyway, it is a problem that is not solved by a naming convention.


Haskell for example has exactly that with e.g. the IO monad.


I don't know Haskell, but assumed it would probably tackle this problem based on all the praise I've seen it get for things like this and its general type system.


Yup. Here's an example.

There is no function

    print :: String -> ()
(The type () implies "no interesting return value").

This function could only be written via deliberate subversion of the type system. Instead it must be

    print :: String -> IO ()
Which is similar, but the return is marked by this IO type. Essentially this restricts you to ensure that side effecting functions are executed in explicit sequence and cannot "poison" pure types.


It gets all the praise but the use of Haskell is still very low. For me the package system drove me into the Arms of Racket and Clojure.


You should revisit it, then! Stack, a new build tool, far surpasses the Cabal hell that drove people away in the past.


I have read many times that you need a solid understanding of the Haskell implementation as well as various extension to make the code as performant as it is elegant. True?


Right now, that's truer than many would like. However, when GHC 8 (the new version of the compiler) drops in the next couple months, most of the performance tuning will involve putting a single line of code at the top of each performance-critical file. That line will make all evaluation within the file strict instead of lazy.

A lot of the performance and predictability problems in Haskell come from its laziness, so the ability to toggle that on and off on a per-module (read: per-file) basis will be a game changer.


That does sound significant. I will be curious to hear more about that. Can you recommend a good go-to resource for all the latest news and discussions on Haskell?


Mostly I just read various people's blog posts and follow links (for example, I think I first found Stack on [1]). I also spend occasional time in the IRC, which can be useful but isn't a comprehensive news source by any means.

I hear some of the mailings lists are informative - iirc there's a haskell-beginners one which will probably cover major updates in tooling, feature releases, and so on. Good luck!

[1]: https://www.fpcomplete.com/blog/2015/08/new-in-depth-guide-s...


One hundred percent agree. Having come to places like this several times in my career I'm ready to start looking into lead. I'm getting bored of the deficiences of programming and would love someone else to deal with this crap.


Jai proposed a solution where like c++ you have to name the variables you're closing over but optionally require that all functions called by the function not access any other variables


I haven't written any C++ in a while, but isn't that how const methods work? One would think there might be a similar concept of a const lambda.


You are talking about object methods, in which const has meaning limited to the object's data, and nothing else -- you can still do side-effects. I'm talking about also anonymous lambdas (modern c++ feature) and non-object functions. There is no way to say in C++ (or even a more functional language like Clojure) if a function has side-effects or not.


I suspect I overuse the x->y naming convention for functions that take an x and return a y. Esspecially when there is only one kind of thing that that function ever takes.

So instead of:

    (-> x x->y y->z)
I'd have:

    (-> x y z)
Makes sense. Thanks for the post Stu!

P.S. Another useful tome about naming functions is: https://github.com/bbatsov/clojure-style-guide


I'm really confused about what is the author intent with this article. At first read, it is pointless. The first paragraph pretty much resumes it: "These are my rules, but they're not good because I break them more often than I adhere to them". If this is intended as a starting point for a discussion, as the author claims, I think a forum is more appropriate format, and at least start from some previous existing convention discussion. This just looks like trying to reinvent the wheel from scratch.


Blogs tend to be the way a lot of advice, guidelines etc are disseminated in the software world, so that's the main reason. I don't know of any major forums in the Clojure world, for example, that everyone is aware of and contributing knowledge to. It seems to blogs, irc, the mailing list, stackoverflow.


This is on Stuart Sierra's personal blog, so it's just him sort of thinking out loud. He's got a lot of that on his blog, and a lot of it is really valuable. I refer to it often when I want to get some new ideas on different ways to do things in Clojure.


They are not good as rules, but they are excellent ideas to have in mind when making decisions on this topic.


I think it would be more valuable to actually define the ! convention, than propose yet another one for side effects.


Not sure why he says the ! is not for side effects, i've always encountered it used in that way (and changing the state is also a side effect)


I can't think off the top of my head where it's been abused. I do know that one question that often arises is "should I end with an ! if my method logs?" I've had this conversation on IRC with the author. The consensus was to only apply such a name when a global was involved.

The trick is trying to telegraph to the consumer that your function logs. They need to know because it brings with it dependencies both by way of code and runtime requirements. Logging may fail for a variety of reasons. These would throw any consumer off. They have to plan for it.


The clojure convention for ! is to use it for functions that are not safe to execute in a transaction - that is, functions that cannot safely be retried. That means that most side-effecting functions should end in !, but not necessarily all: it is possible to have side-effecting yet transaction-safe functions as long as the side-effects are idempotent.

When will functions be called in a transaction? STM ref, agent and atom updates.


> If name-clashes become a problem, add prefixes to the function names

Or, find better names for local variables, like customer-address. In the given example, there is in fact no need to add local variables: simply calling the functions would be enough.


Changing function names after the fact is a versioning problem. I'd much rather not have to rename functions. Changing global names because you're having a hard time with local names seems like a developer anti-pattern.


> Or, find better names for local variables, like customer-address. In the given example, there is in fact no need to add local variables: simply calling the functions would be enough.

Or require the function using a namespace alias.


>If I have a function to compute a user’s age based on their birthdate, it is called age, not calculate-age or get-age.

Ah, but isn't Clojure Lisp-1? Then you can't have function `age` and variable `age` in the same scope despite `age` being a great variable name in many circumstances. Hence having to come up with silly variable names like "lst" instead of "list", because all the good names are taken by the functions.


He discusses that in a later pargraph "Local name clashes", basically advocating the use of namespace prefixes.


When I write Clojure I tend not to worry too much about this. Most of my variable names will be declared with `let` and will have a very limited scope. I can go ahead and clash some builtin function name and not worry about it unless I happen to need that builtin function within the `let` block. It usually is the case that I don't need to.


Locally overriding builtin/global functions seems like a bug waiting to happen. There's no way to figure out if you actually intended to use the local variable inside of scope, or the function it overshadowed. It seems to me that in Lisp-1 all local variables should be prefixed with some sort of symbol so that they can never override globals.


I am always baffled at how individuals present their opinions and methods as the industry standard.


I'm glad you made this comment because it's important to understand why this attitude is harmful. If people believe what you're saying, then they're less likely to attempt to steer their communities in helpful ways. Strong opinions that are widely distributed act as a frame of reference that lets people start from a common base of knowledge and explain where their opinions differ. I can now say, "I name my Clojure functions like Stuart Sierra, except for two differences..."

More people need to present their opinions as the best way to do things.

Some useful, widely-cited examples:

- https://github.com/airbnb/javascript

- http://12factor.net/


Clojure is a small "industry" with a few key players offering a lot of the guidance and advice. Nothing wrong with that, but often the community does in fact adopt the guidelines of just a few people, so that is probably why the author continues to contribute in this way. This author, for example, also wrote one of the tiny but widely-used functions used for unit testing in Clojure, for example. Almost everyone uses it at some point (the "is" macro).


"This guide is just a starting point for thinking about how to name things."

That's a novel way to introduce an "industry standard".




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: