
Clojure Error Messages Are Accidental - bloat
https://lispcast.com/clojure-error-messages-accidental/
======
beders
It's unfortunately part of the learning curve. Being a beginner with clojure
myself, I was put off by some of the more arcane error messages.

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/](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.

~~~
kimi
BTW: for libraries, there is kind-of standard docs being developed:
[https://cljdoc.xyz/](https://cljdoc.xyz/) \- see e.g.
[https://cljdoc.xyz/d/cli-matic/cli-
matic/0.1.13/doc/readme](https://cljdoc.xyz/d/cli-matic/cli-
matic/0.1.13/doc/readme)

~~~
beders
To help with Clojure/ClojureScript adoption, I would love to see this,
clojure-docs and the official documentation in one place.

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?

------
james-mcelwain
Clojure is just so coupled with the host language. Which isn't a bad thing,
since it helps make interop effortless, but does make it difficult to
appreciate as a standalone language.

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".

~~~
kimi
I have been doing Java professionally for like 20 years now; and one of the
things I liked a lot were its stack traces and error messages. When I see a
Java stack trace I find it simple to get started with it to go understand the
issue; with Clojure, my head aches; I try staring at the code to see if
anything comes up. Same - or worse - for Spec errors - they are huge and you
need a cup of coffee to get started deciphering them.

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.

------
kimi
Alex Miller wrote a few days ago on this very topic:
[https://clojureverse.org/t/improving-error-messages-in-
cloju...](https://clojureverse.org/t/improving-error-messages-in-clojure-as-a-
library/1765/27)

------
gkya
That implementation for "keyword" is simply horrible. If it's correct that
most errors caused by bad input to functions come not from verification but
from an arbitrary place in code where a type-specific operation is applied is
correct, that's more horrible. This deeply discourages me from touching
Clojure ever.

~~~
jonstaab
I think that impulse is correct. As a clojure-lover, the state of error
handling, and particularly Rich Hickey's decision to neglect it for so long
and not code defensively, makes me a little sick.

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.

~~~
gkya
> it's only when you're abusing the language (or making mistakes) that you run
> into this kind of stuff.

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.

~~~
didibus
Yes, its true the docstrings are terse. And its true that Clojure follows a
pattern of garbage in, garbage out. And if the rest of Clojure was designed
like Java or Python, it would be a huge problem.

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.

~~~
james-mcelwain
As someone who has (mostly) struggled through, I'd (mostly) agree. I find that
when I am running into errors it is almost always because (a). I am moving too
quickly and have misplaced something simple like my parens (which parinfer
would probably solve) or (b). am not leaning enough on the REPL and am
spending too much time before re-evaluating code.

I find that the more I live in the REPL, the less frequent these errors are.

------
lbj
The overall conclusion is correct, most error messages happen by accident, but
a lot the examples look like expected behavior to me. Maybe because Ive been
using Clojure for too long, or maybe because its the expected trade-off when
using a dynamically typed language. You get more fluidity at the cost of type
checks - The way to mitigate this downside is adding checks of your own.
Clojure has supported this since way back when using pre/post conditions.

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.

~~~
puredanger
Before going down this path, it's important to understand the substantial
performance implications.

~~~
jonstaab
Checks are turned off by default. Are there performance implications even when
they're turned off?

Reference: [https://clojure.org/guides/spec](https://clojure.org/guides/spec)

~~~
ambulancechaser
spec is wholly independent of the pre/post conditions.

~~~
aperiodic
In this respect, they share the characteristic of being easy to wholly disable
(i.e. prevent them from being compiled at all). With pre & post conditions if
the var `clojure.core/ _assert_ ` is bound to false, then they're elided. The
usual pattern is to have them on for testing, and disable when compiling for
production.

------
alkonaut
> The worst sin of stack traces is that the most important information is
> printed first, then followed by less and less important information, usually
> a few screenfuls of it.

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.

------
al2o3cr
> But if the official spec doesn’t restrict get, Spec will let people redefine
> the spec for get for themselves.

> 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?

~~~
ajss
Specs are not scoped. There's a global registry. You may end up chasing other
people's bugs.

------
kuwze
Yeah Clojure's error messages pushed me to Common Lisp (although I am still
quite a neophyte in both).

I haven't tried it, but maybe Sayid[0] would be of help to Clojurians?

[0]: [http://bpiel.github.io/sayid/](http://bpiel.github.io/sayid/)

~~~
emidln
sayid is awesome. Spacemacs integrates it by default and it's a nice step up
from tools.trace.

------
hota_mazi
> It is more useful to think of error messages as non-existent than to think
> of them as bad.

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.

~~~
rboyd
That's quite a judgement call to make based on the utility of error messages.
I can't remember the last time I was blocked based on a Clojure error message
anyway. Errors are less frequent and usually pretty obvious.

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.

[1]
[https://www.youtube.com/watch?v=2V1FtfBDsLU](https://www.youtube.com/watch?v=2V1FtfBDsLU)

~~~
hota_mazi
The simple fact that Clojure is dynamically typed tells me that this language
is simply not suited for large scale programming.

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.

~~~
gkya
It's not really clear to me what you mean with "scale" here. Are you referring
to the scale of a program, i.e. size in LoC and/or complexity of components,
or to the scale of a team of developers, i.e. the number of persons working on
a given codebase?

~~~
hota_mazi
Both.

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.

------
jgalt212
Very good talk by Bozhidar Batzov at PartialConf 2017 that covers this point
as well.

Clojure, The Bad Parts

[https://www.youtube.com/watch?v=1YCkOo5Y4Oo](https://www.youtube.com/watch?v=1YCkOo5Y4Oo)

------
didibus
I think most of the reason for not having all these runtime checks in core is
performance.

Though I guess if there was an easy way to turn them off for production it
would maybe be okay.

~~~
aperiodic
Curiously, the language's features for doing runtime checks as a user--
core.spec and pre & post conditions--are both very easy to completely disable
for production. I don't know why they're not used in core: probably a
combination of the core team's feeling that _they_ don't need them, and not
wanting to make the language slower than necessary by default.

~~~
didibus
Maybe pre/post was added later?

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.

