Clojure Error Messages Are Accidental
lispcast.com
lispcast.com
But compared to 'undefined is not a function' it's about 10 times better ;) Glad to see this being improved.
Don't get discouraged by this. Even if you never plan to use Clojure in a project, dive in and take a good long gulp from the functional tea of enlightenment. It will make you a better programmer.
My advice for people complaining about docs: http://clojuredocs.org/ is much much nicer.
The standard docs are very very terse and you'll need to learn the typical abbreviations and idioms.
People give up easily if they can't find decent getting started and docs. It adds to that initial hurdle.
Any chance the authors of these initiatives can get together?
I first tried to learn Clojure before I knew anything about Java and things like error messages made it really difficult to get started.
Coming back after having written Java professionally for a year, things like random Java exceptions showing up or long stack traces suddenly became a lot more tractable, although still not "beautiful".
This said, Clojure is a lovely language, I have used it for 3+ years now and is my favourite. It has its drawbacks, as with everything else.
That said, clojure remains my favorite language despite this flaw, and I can't wait to spend more time in it. It's beautifully designed. The design communicates well how to use it well, and it's only when you're abusing the language (or making mistakes) that you run into this kind of stuff. Not an excuse really, I'm just excited that errors are going to get somewhat taken care of by incorporating spec into the core.
Not really. My experience has been that most of the time the docstrings leave a lot of guesswork to be done when deciding what to pass to a function and how to pass it through, which causes errors and time lost because there's no other authority on a functions I/O (Common Lisp has a spec, for example). This behaviour has spread to the community too. So in Clojure one passes "things" around and Clojure will do "stuff" to those "things" until either one or both the "stuff" or the "things" break, in which case you either get nothing or somethings that's big but worth nothing. TBH it's been quite some time since I tried to do sth. w/ Clojure so my impressions could be outdated, but the article suggests otherwise.
But its not. And if you take the time, like 30 days, to learn Clojure to intermediate proficiency levels, these issues disapear.
Its hard for me to convince you of this, because its definitly asking for a leap of faith. But think of it like starting Kick Boxing. At first, your knuckles hurt, your chin hurts, but spend 30 days at it, and you just adapt and those pain points disapear. All of a sudden, you feel great, stronger, faster, more agile. That was my experience with Clojure. And ya, just like Kick Boxing, once in a while, your knuckle might hurt again, or your chin, but unlike in the beginning, its very rare, and goes away really quickly.
EDIT: To add more concrete weight to my argument. Here's some of the things I believe contributes to making those issues disapear. The consistency and simplicity of the syntax. Most docstrings call the same things with the same names, and have a similar writing style. The REPL driven development means you can quickly explore and self learn how a function behaves. The immutable data structures avoid a lot of mistakes and simplify a lot of logic. The higher level loops and branching do a similar thing. The easy composability of data transforms become second nature. The functional first style makes writing tests easy. You always end up with little code, so there's just less that can go wrong. And I think most importantly, the Clojure source is easy to read and very accessible, so its often a better documentation then any docstring could be.
I find that the more I live in the REPL, the less frequent these errors are.
The author points out a stray "@" symbol in his code throwing a confusing message about Futures. Well futures are a pretty first class language feature, so that would be pretty easy for figure out. Their complaint should be more about the sugary syntax of derefing using "@" than the bad error message.
Clojure could use better stack traces, but I don't think throwing out a otherwise very elegant language is merited.
WRT keyword, I think that's a bug, not a nuance. This is the doc:
> Returns a Keyword with the given namespace and name. Do not use : in the keyword strings, it will be added automatically.
Nowhere does it say it returns sth. else than a keyword. Following is the doc for "intern" from Emacs Lisp, which does a similar job and signals "wrong-type-argument" with improper input:
---
intern is a built-in function in ‘C source code’.
(intern STRING &optional OBARRAY)
Return the canonical symbol whose name is STRING. If there is none, one is created by this function and returned. A second optional argument specifies the obarray to use; it defaults to the value of ‘obarray’.
---
So it does what it says or fails otherwise. This sort of error handling and streamlined, nice debugging is the best part of interactive programming; otherwise it's just Java with a lot of parentheses.
Handling non-standard arguments in intuitive ways can be useful indeed, but not so if they are accidental, possibly working due only to implementation details, and undocumented.
Example:
core> (defn mydiv [n d] {:pre [(pos? d)]} (/ n d))
#'mydiv
core> (mydiv 10 2)
5
core> (mydiv 10 0)
AssertionError Assert failed: (pos? d) core/mydiv
As opposed to the standard core> (/ 10 0)
ArithmeticException Divide by zero
clojure.lang.Numbers.divide (Numbers.java:158)
Chasing down Numbers.java will take time, but with a pre-condition you get the exact name of the function and the test which failed.Reference: https://clojure.org/guides/spec
Spec assertions (not pre/post checks) can actually be compiled completely out of production code so those can really be nothing (but not if you're adding them in wrapper layers).
No. It’s at the top of the web browser window. At the top of the unit test out window in the IDE and so on. Flipping stack traces to help console reading would be a mistake. Should be made optional in that case.
> They can choose whatever subset of the type they want.
Total clojure-spec noob question: is this type scoped somehow, or does using your own type mean chasing down bugs in libraries that assume a looser version?
I haven't tried it, but maybe Sayid[0] would be of help to Clojurians?
If this is true, my respect for Clojure just completely dropped.
Error messages are a crucial part of a language and they are instrumental to the robustness of the programs it helps create.
The fact they have been so severely neglected by the Clojure teams makes me think the authors of the language don't really understand modern large scale programming.
I think, I may be wrong here, clojure works very well for people with a little bit of experience and those who think things through before they write code.
I have my guards when I write clojure though. I always write tests, and in the beginning I used to make small git commits, though I don’t need to do that now anymore.
I'm happy for you but your experience doesn't invalidate my claim.
> clojure works very well for people with a little bit of experience and those who think things through before they write code.
All languages work well for people who think before they write code.
Clojure makes that harder than necessary, though, being dynamically typed.
Clojure has potential, but the lack of good feedback when things go wrong makes it _very_ hostile to users of the language, especially when first trying to pick it up.
Tooling in Clojure is actually pretty great. Generative testing, linters and IDEs I find work better then in Java personally.
Where I agree with you is learning curve. Clojure is definitly difficult for new users to pick up. The community and core team I think are more focused on improving that now then ever though. So hopefully it'll change. Also, I'm not sure hostile is a good word, if anything, the best part about Clojure is the community is actually really nice, helpful and supportive, even to new users. There's no trolls, and a lot of great open dialogue, active members ready to help you on chat and forums at all times, etc.
And depending on my editor, I might use Joker for syntax checking as I type. Cursive has pretty good inbuilt linting too.
There is 3-5x difference in productivity between java and clojure most of the time, just because I don’t have to read a lot of code to make a change. Clojure, written right, feels like math equation on a board. My eyes don’t dart around up and down figuring out the code. It is terse, to the point and immutable.
It is subjective, but clojure vs Java is like doing math with Arabic numerals vs doing them with Roman numerals. I’m sure we need lot more help when doing math with Roman numerals, but that level of help is simply not necessary, and is more about the medium than the message
There is a vast amount of evidence that shows the opposite. Most of the code bases you use on a daily basis today, such as Google, Amazon, eBay, etc... are built on Java.
Very few (if any) are built on Clojure.
I think you're wrong to say core devs don't understand modern programming, but you'd probably be right to say that they didn't prioritize making it approachable and stooping to the lowest common denominator. And it's not just Clojure, but datomic, core.async, spec -- all solving real problems in modern large scale programming with elegance.
Watch Rich's 'Effective Programs' talk [1] and lmk if you still feel that he's out of touch with modern large scale programming. I think he's pretty in tune.
Why do people assume that being dev-friendly is the "lowest common denominator"?
Devs are not stupid.
Beginners are not stupid.
Moreover, the more senior you are, the more you begin to appreciate clear precise error messages that directly point to the problem.
There's only so much brainpower, and I'd rather spend it on solving a problem, and not on figuring out/memorizing yet another cryptic error message.
And yes, no amount of fluffy talks or "large scale apps built with datomic" will convince me that those devs are in touch with modern programming. They are just used to what they work with.
> Moreover, the more senior you are, the more you begin to appreciate clear precise error messages that directly point to the problem.
Hopefully the more senior you are, the less you encounter errors in the first place.
> There's only so much brainpower, and I'd rather spend it on solving a problem, and not on figuring out/memorizing yet another cryptic error message.
And I'm saying that's not the Clojure experience once you're up and rolling. You iterate over program construction in small manageable chunks, with immediate feedback from an editor jacked into a cider repl, and your repl experiments evolve into long-standing tests.
Also, even if the Clojure language doesn't necessarily cater to the beginner, the Clojure community definitely does. There are constant efforts to evolve better docs, extend tooling, and build user groups that reach out to beginners.
You keep saying that people are stupid. And since you keep iterating it, I believe this is the point you are making, not some speculation about what Rich Hickey does or does not.
> if the Clojure language doesn't necessarily cater to the beginner
A dev-frienndly language does not cater just to beginners. Everyone benefits.
> There are constant efforts to evolve better docs, extend tooling, and build user groups that reach out to beginners.
So, they do stoop to the lowest common denominator and coddle? Why can't Clojure?
I didn't say that, because I don't feel that way.
I think that you're doing it right as long as you're using a language that helps you be productive and brings you joy. Sounds like you're already good where you're at.
Enjoy the rest of your weekend.
Yet your language betrays your feelings.
I would never in my life use words like "stoop to lowest common denominator" or "coddle beginners" when talking about beginners (or other developers in general).
The rest of your message is a thinly veiled contempt at best. So, good weekend to you, too.
And if Rich likes Clojure, he can go nuts with it.
But a significant chunk of Hackernews believes that Clojure is the greatest thing since sliced bread, or even the obvious successor/replacement to all other Lisps. As an experienced programmer myself, if someone told me that Clojure is better suited to my application needs than CL or Scheme, I would tell them no, and fuck you. And this is part of why; there are other deficiencies and quibbles I have with Clojure's design.
The trend in programming languages is pretty clear, we are moving more and more strongly toward languages that are statically typed and with type inference.
And for good reasons, in my opinion.
The truth is, the issues that come out of your current model of programming makes you seek something that trades them out for other issues. Right now, those other issues are unknown to you, and you're not feeling the pain they bring. 10 years from now, they'll be all you complain about, and the trend will go back.
Now, maybe I'm wrong, but its rare that a truly better paradigm shows up. It happened before though, so might happen again. Maybe that'll be inferred static types, but we've had those for so long already that I'm doubtful. For example, Clojure has infered static types, a lot of people tried it and felt it was more trouble then worth. So in the small Clojure world, the trend to infered static types already happened and passed by back to runtime contracts.
Scale in performance, since dynamically typed languages have fundamental hurdles that will always make programs slower than when written in a statically typed language.
And scale in code base and developer strain. The fact that it's mathematically impossible to reliably refactor code automatically if the types are absent encourage spaghetti code bases that developers are afraid to modify because they can never be sure they're not breaking anything.
Such hesitations never happen in statically typed languages.
Nowhere have I said that if you see an error message you don't know how to program.
I face errors every day, but I don't face errors that are so cryptic that they block my productivity. We agree errors are useful even if we disagree that Clojure's errors are bad.
I did find the errors very confusing when I was getting started, but I think that was because the errors use Clojure terminology that I had not yet learned.
> making it approachable and stooping to the lowest common denominator.
> We agree errors are useful even if we disagree that Clojure's errors are bad.
I would assume that you disagree with the article then, in which case I can't make any other point to emphasize that
And the underlying JVM errors are normally very noisy and often a little indirect to exactly what you did wrong.
As someone who knew Java very well though, I've never had much trouble connecting the underlying JVM error to my Clojure code. That said, for beginners or people unfamiliar with Java, its an extra burden to have to decipher. And I myself wouldn't mind errors that point me more directly to my Clojure code, instead of towards its implementation.
The problem is the errors aren't easy to understand what you did wrong exactly. As in, the message isn't very precise. But the errors are there. Well, except with get I guess, but that seems like it was on purpose, the implementation would have to specifically check for and return nil.
Idiomatic Clojure code is written in atomic, easily tested units. If you're throwing exceptions, you should be using ex-info and creating the error message yourself when its thrown (so a bad message is your own damn fault). If the error is thrown by clojure.spec, it's a type error and the source is obvious. If the error is thrown by the JVM, the stack trace often points to the exact line of code causing the error because your code is written in small, atomic functions in the first place.
In short, if you approach Clojure like Java, yes, the error messages are a problem. If you approach it like Clojure, it's really just not a problem.
I don't think any of us would agree with the characterization of Clojure errors in the article.
Clojure, The Bad Parts
Though I guess if there was an easy way to turn them off for production it would maybe be okay.
They're now in the process of speccing all of core. Hopefully next release will have it.
I do think slow by default is part of it. I've seen a lot of builds deployed to prod without prod optimizations in my experience. That's why I think spec in not instrumented by default. Too worried people will keep it on at all times, and then Clojure would get a bad rep for being slow.