Clojure newbie question regarding compile time type checking.

69 views
Skip to first unread message

ajay gopalakrishnan

unread,
Dec 12, 2009, 11:24:55 PM12/12/09
to clo...@googlegroups.com

It tried the following in REPL and got no error. Personally, I feel that I should get an error because calling square on strings is wrong in both cases.

Is there a way out of this in Clojure?

(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"]))

Thanks,
Ajay

Baishampayan Ghose

unread,
Dec 15, 2009, 7:23:58 AM12/15/09
to clo...@googlegroups.com
Ajay,

> It tried the following in REPL and got no error. Personally, I feel that
> I should get an error because calling square on strings is wrong in both
> cases.
>
> Is there a way out of this in Clojure?
>
> |(defn square[n] (* n n))
>
> (if (= 0 0) (println"hello") (map square["a" "b"]))
>

What did you expect from Clojure? In the above form the `map` is a part
of the else form and that's why it's not executed.

If you don't know already, the syntax for `if` in Clojure is like this -

(if expression
(then form)
(else form))

If you have multiple then or else forms, you can wrap them inside a `do`
form like this -

(if expression
(do
(then form)
(more then form))
(else form))

> The following gives error (because it gets evaluated):
>
> |(defn square[n] (* n n))
>
> (if (= 0 1) (println"hello") (map square["a" "b"]))

In the above form, the map is a part of the else form and since 1 is not
equal to 0, the `map` is executed.

I hope your confusion is cleared.

PS - If you are worried about "compile time type checking", I think it's
prudent to mention now that Clojure is a dynamically typed programming
language where types are checked at run-time and not compile time.

Regards,
BG

--
Baishampayan Ghose <b.g...@ocricket.com>
oCricket.com

ajay gopalakrishnan

unread,
Dec 15, 2009, 7:51:18 AM12/15/09
to clo...@googlegroups.com
Oh ... I know all that. What I wanted to know is that is there any way to FORCE compile time checking by providing some flag or the other to Clojure compiler. If not, I guess a good set of test cases is the only way to fix it. (Good test cases are always recommended, it's just that in this case it becomes unavoidable)

Ajay

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

Laurent PETIT

unread,
Dec 15, 2009, 8:43:14 AM12/15/09
to clo...@googlegroups.com


2009/12/15 ajay gopalakrishnan <ajgo...@gmail.com>

Oh ... I know all that. What I wanted to know is that is there any way to FORCE compile time checking by providing some flag or the other to Clojure compiler. If not, I guess a good set of test cases is the only way to fix it. (Good test cases are always recommended, it's just that in this case it becomes unavoidable)

Not yet, and - just guessing - neither in a short nor middle term.

But I remember Rich mentioning being interested in type systems "a la" Qi language ( http://en.wikipedia.org/wiki/Qi_(programming_language) ). This allows to enable/disable type checking, but maybe what seemed more interesting was the way to define the type via sequent calculus ( http://en.wikipedia.org/wiki/Qi_(programming_language)#Type_Checking ).

HTH,

--
Laurent
 

Garth Sheldon-Coulson

unread,
Dec 15, 2009, 9:11:30 AM12/15/09
to clo...@googlegroups.com
OTOH, when Rich gave a talk at MIT recently he mentioned he wasn't terribly interested in type systems for Clojure apart from (potentially) a simple binary type system to distinguish I/O-side-effecty things from non-I/O-side-effecty things. He also mentioned something extremely interesting about wanting to make Clojure amenable to analysis by external static correctness checking tools, rather than build types and type-checking into the language itself.

I should let him characterize his views himself, though, because I imagine I'm getting it at least part wrong.

Either way, Ajay, Clojure is a dynamic and dynamically typed system. Cons: No compile-time type checking, more runtime bug-catching. Pros: Easy, module-by-module, REPL-based interactive development, expressiveness, somewhat easier code reuse, faster development, and easier interaction with external systems that may change under your nose. I was born and bred in QBasic, Perl, and later Ruby, so this is what feels most natural to me.

Tayssir John Gabbour

unread,
Dec 15, 2009, 9:20:43 AM12/15/09
to Clojure
On Dec 15, 1:23 pm, Baishampayan Ghose <b.gh...@ocricket.com> wrote:
> PS - If you are worried about "compile time type checking", I think it's
> prudent to mention now that Clojure is a dynamically typed programming
> language where types are checked at run-time and not compile time.

Actually, there are Common Lisp compilers (like SBCL) which signal a
warning if they infer a type error. I think it's handy, and doesn't do
anything drastic like refuse to compile your program because of it.


All the best,
Tayssir
> Baishampayan Ghose <b.gh...@ocricket.com>
> oCricket.com

Baishampayan Ghose

unread,
Dec 15, 2009, 9:37:07 AM12/15/09
to clo...@googlegroups.com
On Tuesday 15 December 2009 06:21 PM, ajay gopalakrishnan wrote:
> Oh ... I know all that. What I wanted to know is that is there any way
> to FORCE compile time checking by providing some flag or the other to
> Clojure compiler. If not, I guess a good set of test cases is the only
> way to fix it. (Good test cases are always recommended, it's just that
> in this case it becomes unavoidable)

Clojure right now does not have any facility to do type checking at
compile time (and I doubt if it will ever). Languages like Python, Ruby,
Perl, etc. are similar in this regard.

Even though Clojure is hosted on the JVM, it has a completely different
ancestry; it is more different that similar to Java.

Luc Préfontaine

unread,
Dec 15, 2009, 12:24:32 PM12/15/09
to clo...@googlegroups.com
If Clojure type checking becomes a frequent request/complaint, please build a lint type tool, not some twisted logic embedded in the compiler :)))

C did not have any decent type checking when it came out and we had to use lint to find bad parameter on fn calls and other similar errors.
They were no function prototypes at the time so passing a pointer address instead of the pointer value did not bother the compiler at all.
In fact you could pass crap in most places and end up at run time with a bad pointer address or corrupted stack.

You cannot call C a dynamic language, a semi-assembler that evolved to a higher status over time,
however it lacked basic type checking in it's early incarnations... Quite a challenge for a typed language (byte, short, int, long, 8? 16? 32 bits ?).
Debugging stack corruptions daily is not really an efficient way to program so rules where added to the compiler to eventually
flag data type improper uses.

I never saw a dynamically typed language that had type checking at compile time :))) In my mind it's mutually exclusive...

Lisp compilers came out in the 1970s if I recall. To compile Lisp you needed special declarations to define unbound variables and other similar things
to get the compiler to create a runnable file.
Symbols where not bound to lexical scope so the compiler needed some hints from time to time telling it that these things would eventually be available at run time.
This did not apply to symbols within a function, they could appear without an explicit declaration. It was assumed they were
somewhere on the stack at run time. That's even more dynamic that what Clojure allows... maybe a bit too much.

The Clojure compiler is in the same vein, it checks for undeclared stuff BUT mandates that everything used be visible in the lexical
scope. No run time bindings not lexically visible are allowed, that's a very good compromise, debugging for missing dynamic bindings on the stack
at run time is not very funny.

This compiler has nothing to do with a data typed compiler that validates that the "proper" typed variables are used in an acceptable manner...

These are two unrelated things. We call them both "compilers" but its not required for a compiler to do data type checking.
The only thing they may have in common is that they both generate code.


Luc

ajay gopalakrishnan

unread,
Dec 15, 2009, 9:41:49 AM12/15/09
to clo...@googlegroups.com
On Tue, Dec 15, 2009 at 9:11 AM, Garth Sheldon-Coulson <ga...@mit.edu> wrote:
OTOH, when Rich gave a talk at MIT recently he mentioned he wasn't terribly interested in type systems for Clojure apart from (potentially) a simple binary type system to distinguish I/O-side-effecty things from non-I/O-side-effecty things. He also mentioned something extremely interesting about wanting to make Clojure amenable to analysis by external static correctness checking tools, rather than build types and type-checking into the language itself.

external static correctness checking tools, I am definitely looking forward for this one.
 

Daniel Werner

unread,
Dec 24, 2009, 5:29:37 AM12/24/09
to Clojure
On Dec 13, 5:24 am, ajay gopalakrishnan <ajgop...@gmail.com> wrote:
> It tried the following in REPL and got no error. Personally, I feel that I
> should get an error because calling square on strings is wrong in both
> cases.
>
> Is there a way out of this in Clojure?

I hope you don't mind me bumping this old thread, but only yesterday I
stumbled upon the "assert-args" macro in the master branch's core.clj
that does something similar to what you want. Example usage:

(defmacro when-let
"bindings => binding-form test

When test is true, evaluates body with binding-form bound to the
value of test"
[bindings & body]
(assert-args when-let
(vector? bindings) "a vector for its binding"
(= 2 (count bindings)) "exactly 2 forms in binding vector")
... snip body ...

I haven't tested it, but it will probably be able throw a compile time
exception if used in a macro definition (with it's checking ability
being limited by what is known at compile/macro expansion time), and a
runtime exception if used in a function definition. So if you make use
of assert-args in the function that will later call square, the error
can at least be caught at runtime and generate a useful exception
message, regardless of whether (map square ...) will be evaluated or
not.

assert-args is a private var though, so a more officially idiomatic
way to check function arguments is probably to use the new pre/post
conditions slated for 1.1.

(Only when I had finished writing this post did I realize that you
really want static type inferrence, which assert-args doesn't provide,
but hopefully this information will be useful anyway :-)

Reply all
Reply to author
Forward
0 new messages