

Critiquing Clojure - sjbach
http://items.sjbach.com/567/critiquing-clojure

======
pmjordan
A lot of these are personal preference and borderline traditionalist, and as
pointed out by others, the dynamic binding one is in fact not strictly true.

One thing that _does_ however bother me about Clojure is the interaction of
dynamic binding and lazy evaluation. Allow me to illustrate:

    
    
      => (def *test* 0)
      #'user/*test*
      => (binding [*test* 2]
           (map #(* % *test*) '(1 2 3)))
      (0 0 0)
    

The call to _map_ is lazy, and the returned seq is only evaluated once it hits
the 'P' of REPL, which is outside the dynamic binding of _test_. If you
surround the _map_ call with _(doall_ and _)_ , the result is (2 4 6).

This is of course a trivial case, but I've been bitten by this in practice
where the binding, lazy sequence and var evaluation were a few call steps
apart, and it's always been very unpleasant. Most of the time, it's involved
vars which don't have a root binding, so it throws an error, but in other
cases it's a lot more serious.

I guess the same problem can be provoked in both Clojure and CL by returning a
closure evaluating that var to back outside the dynamic binding, but since
you'd be explicitly invoking the closure, it's a lot more obvious what's going
to happen than the "magic" that is lazy evaluation.

------
febeling
I was interested in Clojure before, but the critisism I found in the two
recent posts here on HN make it even more interesting. I feared something
substantial might turn up that really defies using it, but to the contrary: if
issues listed in the blog posts are the biggest problems people find with
Clojure, then that is quite promising.

~~~
habibur
I find the mention of obscure debug information where most of the time you
can't find line number of Clojure code where the error occurred --- as a big
problem.

------
radu_floricica
I don't have much experience in CL, but I started to really like the fact that
clojure is Lisp 1. When I have a hash map "client", I can use all three:

(get client :phone)

(:phone client)

(client :phone)

It takes some getting used to, but I already came to expect (and enjoy) that
any symbol I use can be a function.

~~~
jimbokun
I also think that reading lots of "funcall"s makes the intent of the code less
readily apparent.

~~~
utx00
it is definitely more verbose and uglier. but in practice it does not seem to
be a problem. for instance, comparing to arc, if you had notfn for ~, and
testify, you could write all as:

    
    
      (defun all (test seq)
        (funcall (notfn #'some) (complement (testify test)) seq))
    

doesn't seem that bad.

------
twopoint718
On #5, the code isn't doing what the author may think:

    
    
         (let [#^int a 2
               #^int b 3]
           (cons a b))
    

_a_ and _b_ are not hinted to be primitive types. Type hinting is purely for
objects. Use hinting to make it clear to the compiler what sort of _objects_
you are passing into something (e.g. a constructor that takes a String). If
there is confusion, clojure will do reflection.

The right thing to do in this case, is the second thing that the author
mentions:

    
    
       (let [a (int 2)
             b (int 3)]
         (cons a b))
    

a way to see that _a_ and _b_ are really primitive is:

    
    
        (let [x (int 5)]
          (.shortValue #^Integer x)) ; exception: "can't type hint primitive..."
    

There's a post on the mailing list that goes over this much better than I did
(and where I drew the examples):
[http://groups.google.com/group/clojure/browse_thread/thread/...](http://groups.google.com/group/clojure/browse_thread/thread/b0554f7381b8db96/74bf85fabc12761a)

I understand that this is really beside the point, that the author finds type
hinting syntax unattractive, but I just wanted to clear up confusion (if there
was any).

EDIT: clarity

------
mmcgrana
I'm not familiar with CL's "PROGV", but you can indeed dynamically rebind vars
at runtime with Clojure: <http://paste.lisp.org/display/73551>

------
plinkplonk
#2, "Functions must be declared before first call",is very irritating (to me)
It feels like I am back in Pascal land

~~~
swannodette
It also eliminates a very large class of errors- typos when referencing other
functions. Tradeoffs...

