user=> (+ 1 1)
2
user=> (1 + 1)
ClassCastException java.lang.Long cannot be cast to clojure.lang.IFn user/eval17 (NO_SOURCE_FILE:10)
user=> (print-stack-trace *e 20)
java.lang.ClassCastException: java.lang.Long cannot be cast to clojure.lang.IFn
at user$eval9.invokeStatic (NO_SOURCE_FILE:6)
user$eval9.invoke (NO_SOURCE_FILE:6)
clojure.lang.Compiler.eval (Compiler.java:6978)
clojure.lang.Compiler.eval (Compiler.java:6941)
IFn fn = (IFn) fexpr.eval();
return fn.invoke();
Which of course throws a class cast exception.
But there's so much information right there. Imagine this (in pseudo-code) as
Object f = fexpr.eval();
if (f instanceof IFn) return (IFn)f.invoke()
else throw ClojureNotAFunctionEvaluationError "First position in form at line " ... " of environment " ... " is not a function object. You handed me " + fexpr.first() " in form " ... " which has type " fexpr.first().class() " and value " fexpr.first().toString()
or whatever (like maybe don't toString values - that's why you'd want a plan. And maybe use a string table so you can internationalize. Again, plan :) )
so I almost want to whack this sort of stuff into a local copy as I hit error messages which bug me, but that seems foolish. And anyway I'm new here.
Sorry if I have accidentally stepped into some sort of landmine or if this has been rehashed a million times.
But I figured, in the spirit of being willing to help if there's help to be applied, I would ask if there's some sort of thinking on this topic for 1.9.1 or 1.10 or something?
Thanks
Paul
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
And the error messages are not good.So I was wondering: Is this a philosophical thing? Or is it an effort thing? And if it is an effort thing, is there some sort of plan for what effort to put in? And if so, can I help by closing tickets?
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to a topic in the Google Groups "Clojure" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/clojure/ANKq6XD1nW8/unsubscribe.
To unsubscribe from this group and all its topics, send an email to clojure+u...@googlegroups.com.
> I cherrypicked a case where the runtime difference would be tiny
I wouldn’t be so sure of that. Adding any additional code to the function call logic is going to impact almost every single expression in Clojure and that “tiny” difference is going to add up pretty fast.
As others have noted, runtime performance is a Big Deal™ for Clojure and that means that some things are traded off for that speed.
Also, as others have noted, this topic comes up repeatedly, probably a handful of times a year in some form or other. After using Clojure in production for just over five and a half years now, all I can say is: you get used to the error messages and you develop an intuition for what most of them mean. Several of the Clojure/core folks have said at various times over the years that Clojure is deliberately not optimized for novices, on the grounds that whilst everyone starts out as a novice, most of your time spent with Clojure is beyond that stage (and an optimization for novices can be a de-optimization for everyone else).
My pet peeve is that clojure.string is not nil-safe. (str nil) produces “” but if you pass nil to pretty much any clojure.string function, you get a NPE instead. You might think “Hey, it would only be a tiny difference if clojure.string functions accepted nil and treated it as an empty string!” … until you create a fork of Clojure with that modification and run some benchmarks … J
Sean Corfield -- (970) FOR-SEAN -- (904) 302-SEAN
An Architect's View -- http://corfield.org/
"If you're not annoying somebody, you're not really alive."
-- Margaret Atwood
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
On 6 December 2016 at 01:28, <pa...@pwjw.com> wrote:And the error messages are not good.So I was wondering: Is this a philosophical thing? Or is it an effort thing? And if it is an effort thing, is there some sort of plan for what effort to put in? And if so, can I help by closing tickets?This is an issue that's been discussed often.The fundamental problem is that in a dynamically typed language, good error messages are often at odds with runtime performance. The more checks we add to catch specific scenarios, or to provide more descriptive scenarios, the more performance tends to be impacted.
Boy I really think you've all done a nice job with Clojure. I've used quite a few environments over the years and clojure + CIDER + etc is a great experience. The immutability and threading are smart. I've been able to finish a hobby project in clojure which I've been mulling for a long time and never found the right environment. Super stuff.And the error messages are not good.
So I was wondering: Is this a philosophical thing?
Or is it an effort thing? And if it is an effort thing, is there some sort of plan for what effort to put in? And if so, can I help by closing tickets?
Here's one, for instance, which bugs me (using a build of branch master in clojure from github just now)user=> (+ 1 1)
2
user=> (1 + 1)
ClassCastException java.lang.Long cannot be cast to clojure.lang.IFn user/eval17 (NO_SOURCE_FILE:10)
OK but what sort of noob would actually type in 1 + 1? (That's a joke. Lots of people would. And I do sometimes when switching between java and clojure) But that same error pops up if you misalign in parenthesis or confuse an argument order or do a variety of other things.And that error message is terrible. I mean I know why it is there. Clojure needs the first element of a list be the function position and 1 is not a function, it is a long. But you have to squint rather generously at that message to get to the problem. Or just learn what that message "really" means, which seems a bit unfriendly.And so when I go look inside the sourceuser=> (print-stack-trace *e 20)
java.lang.ClassCastException: java.lang.Long cannot be cast to clojure.lang.IFn
at user$eval9.invokeStatic (NO_SOURCE_FILE:6)
user$eval9.invoke (NO_SOURCE_FILE:6)
clojure.lang.Compiler.eval (Compiler.java:6978)
clojure.lang.Compiler.eval (Compiler.java:6941)
Compiler.java hasIFn fn = (IFn) fexpr.eval();
return fn.invoke();
Which of course throws a class cast exception.
But there's so much information right there. Imagine this (in pseudo-code) as
Object f = fexpr.eval();
if (f instanceof IFn) return (IFn)f.invoke()
else throw ClojureNotAFunctionEvaluationError "First position in form at line " ... " of environment " ... " is not a function object. You handed me " + fexpr.first() " in form " ... " which has type " fexpr.first().class() " and value " fexpr.first().toString()
or whatever (like maybe don't toString values - that's why you'd want a plan. And maybe use a string table so you can internationalize. Again, plan :) )
so I almost want to whack this sort of stuff into a local copy as I hit error messages which bug me, but that seems foolish. And anyway I'm new here.
Sorry if I have accidentally stepped into some sort of landmine or if this has been rehashed a million times.
But I figured, in the spirit of being willing to help if there's help to be applied, I would ask if there's some sort of thinking on this topic for 1.9.1 or 1.10 or something?
Several of the Clojure/core folks have said at various times over the years that Clojure is deliberately not optimized for novices, on the grounds that whilst everyone starts out as a novice, most of your time spent with Clojure is beyond that stage (and an optimization for novices can be a de-optimization for everyone else).
Wow thank you for the fulsome responsesA couple of thoughts. I brashly said> I cherrypicked a case where the runtime difference would be tinyand I appreciate the clarifications - especially that my naive add-one-bytecode calculation in my head (the code I pasted has a pretty meaty “analyze” call called before it every time which is why I thought I cherry-picked) didn’t think about things like byte code inlining or, really, the pile-of-sand one-more-bytecode syndrome. So thanks for that!
And my favorite response from Alex:> If I can make a request, it would be that instead of saying "errors are bad" (which is honestly, not helpful by itself), but to step back and either take a broader approach towards systemic issuesThis is so reasonable I’m embarrassed I was a bit glib in my first note. I mean it’s not like they are C++ template errors from 1998 (my gold standard for bad errors). So let me expand on “bad” a little.I think the error messages are “bad” in 3 particular but related ways. I’d be happy to expand on these, but the themes are:1: They are “large” and “far from my code”. This is partly an issue with java stack traces, but the intermixing of java and clojure and the presentation of clojure names in java name-like things in the stacks is obscuring. The line numbers are obscured instead of displayed prominently. And so on.
2: Similarly, they expose internals of the runtime implementation to the user. Stacks intertwine java and clojure (CIDER error screen has a toggle to supress the stack partially for this). They show java exceptions as clojure errors when they aren’t part of a clojure program. etc…
3: These, plus a few other things, mean the error messages are semantically far away from the program you write. You stay happily in LISP-y land until you get a stack, then you realize you are in java. There are lots of examples of this, but take the dumb one I picked - (1 + 1) in a REPL. That shows two concepts (java.lang.Long and clojure.lang.IFn) which you don’t deal with in lisp. I’ve written quite a lot of clojure in the last 5 months and never typed clojure.lang.IFn once. It’s a java error message but I’m a clojure programmer. Instant semantic break. And since many error messages are generated at the runtime level, which is java / JVM, I experience this semantic break often. Add the excellent comments in this thread on macro expand non-locality of error and the problem expands.
So if you imagine a guide like “the error should be as close to the error causing code as possible and presented in the semantics of the problem” then I think I conclude clojure messages are “bad”. (But I love clojure)It seems spec, inasmuch as it acts as a sort of optional type system which is in the space of the clojure language and produces information in that space, would address many of these.And as to why I mentioned this: I mean I haven’t had a problem. I just built a dual mental model of clojure and the clojure runtime while I learned the language and swapped between when an error popped.
And I’ve written a lot of java (and interpreters and compilers too) so that was easy enough for me to do. And I have been hugely productive in your kit. I love clojure!But indeed for teaching it’s an obstacle, but also for teaching’s adjacent problem, using a language or toolset in a group of programmers of mixed abilities, I was thinking errors would be something I’d improve.