tags:

views:

278

answers:

2

It tried the following in REPL and got no error (because of lazy evaluation):

(defn square [n] (* n n))
  (if (= 0 0) 
    (println "hello")
    (map square ["a" "b"]))

The following gives error (because it gets evaluated):

(defn square [n] (* n n))
  (if (= 0 1)
    (println "hello")
    (map square ["a" "b"]))

My concern is that if in a large piece of code, there are some parts which my test cases could never touch, then for example, a code like above will crash in production! Is there a way to check these things at compile time?

Thanks

A: 

Nope. Then again, if code behaves bad in cases not tested by test cases, it will not be caught by the test cases.

You could add a type system on top of Clojure using macros, but then you run into limitations that the type system has and the limitations it would impose.

(Side note: this isn't really lazy evaluation; this is the behavior of the if special form)

Brian
+7  A: 

It's not lazy evaluation. It's conditional evaluation.

Special form (if condition expr1 expr2) evaluates expr2 only if condition was false.

Your concern is not related to Clojure nor laziness.

Try this in Ruby:

irb(main):003:0> raise "wannabe error" if false
=> nil

[edit: answering your question more precisely]

You can hardly have such error detection at compile time in a dynamic language.

At runtime you can evaluate tests with different values of conditions to reach possibly all revelant code branches. I guess the situation is the same in all dynamic languages. It's the reason we write the tests for.

So you'd want to cover both cases, true and false, for the condition you're concerned about in your tests.

Personally I'm a fan of solutions like QuickCheck which generate a wide range of possible values (guarded by some conditions if you need) for the purpose of testing. In the case above you'd only want to test 2 cases, true and false, but imagine other tests triggered by a wide range of input values. Unfortunately there are not many implementations of such generators. The only QuickCheck clones for dynamic typing languages I know are for Erlang (proprietary and quite expensive) and RushCheck for Ruby. The Clojure implementation is in its infancy.

Wojciech Kaczmarek
Is it possible to force the Clojure Compiler to compile the entire code? Then when it converts the entire code into Bytecode, wont it figure out the error?
ajay
But the entire code *is* compiled. It's just the condition evaluation happening at runtime, not compile time. Even if your condition seems as simple as (= 0 1), remember that '=' is a normal function call and 0 1 are its arguments. The evaluation of this function if left to runtime.
Wojciech Kaczmarek
What I hoped for is that regardless of whether the code block is executed or not in the language, it at-least did a Type checking. Of course if the type is unknown then the type cannot be checked at compile time statically, but this is a a very simple code and I was hoping that such a simple case would be caught. I understand that it is a dynamic language and all that ... but providing such a simple check would not harm the language in any way and cant understand why Rich decided to leave it out.
ajay
Yeah that'd be cool if done automagically.. but you seem to answer your own question by saying "if the type is unknown then the type cannot be checked at compile time statically". The the DT vs ST debate is quite old... you just can't eat the cake and have the cake.
Wojciech Kaczmarek
Btw some typing information can be introduced by using metadata, but it doesn't do inferring AFAIK and is generally used to hint the compiler to get rid of reflection, esp. when interfacing with Java code. See description of type hints at http://clojure.org/java_interop#toc35
Wojciech Kaczmarek