1
votes

The following Clojure code is actually erroneous:

(defn divv [x y z] (if (< x y) z (divv ((- x y) y (+ z 1)))))

as the correct one should be:

(defn divv [x y z] (if (< x y) z (divv (- x y) y (+ z 1))))

but it passes the Clojure REPL, and returns a function. But when calling it (e.g. as (divv 3 2 0), the error will show up

ClassCastException java.lang.Long cannot be cast to clojure.lang.IFn user/divv (NO_SOURCE_FILE:1)

The question is, why isn't the error detected when divv is defined? Since divv is already defined as a function take 3 arguments, why can (divv ((- x y) y (+ z 1))) pass the test?

1
Why can't it, or why isn't it? Are you asking why Clojure doesn't do more static analysis? - Dave Newton
Because Clojure isn't a static language and it doesn't do that kind of checking until runtime. There is a notion of gradual typing with add-ons, but I don't know if that extends to method signatures, or how it integrates into the REPL. - Dave Newton
It can be added that you can pass in special designed arguments that make this function work just fine. You can design a type that allows - and IFn for the invoke. - ClojureMostly
The first version of your code didn't fail because only a single argument was passed to divv. If failed because (- x y) returned a Long, which isn't an IFn. It never got as far as trying to invoke divv. - Bob Jarvis - Reinstate Monica

1 Answers

3
votes

In my experience it seems that there are two basic schools of thought in language design:

  • School #1 (the "static analysis", "Deus Ex Compiler", or "Mad Megalomaniac" school) believes that it is both possible and desirable to detect many/most/all possible conditions that could conceivably cause issues at runtime, and is characterized by A) masses of static typing information (for what fun is static analysis without static data upon which to operate?), B) a compiler of massive size and complexity which is required to digest, sift, and sort all that static data, and C) masses of compile-time error messages which purport to head off run-time errors.

  • School #2 (the "dynamic analysis", "Nutty Professor", or "Meh - we'll figure it out at runtime" school) believes that because static analysis can't determine everything that can possibly go wrong at runtime (for there are things which are beyond the ken even of the most static of static analyses :-) that it's not worth the bother, and that it'll all come out in the wash anyways. These languages/systems are characterized by A) small compilers which operate only on limited information, B) more complex runtime support in order to detect errors which static analysis might otherwise detect, C) minimal or NO typing information, and D) more runtime errors than you can shake a compiler manual at.

Myself, I favor languages from the "dynamic analysis" school if only because it makes developing software a lot more fun. If you've never worked in a language where variables do not have a data type, and where compiling something need not be an occasion for a coffee break, you might want to give a dynamically typed language (such as Smalltalk or Clojure) a try. Flexibility is fun.