Hacker News new | ask | show | jobs
by gavinray 1581 days ago
How do people who write Clojure deal with lack of type checking?

And auto-complete when it comes to Java library interop?

I tried Clojure, to build a tool on top of a popular Java library that I was new to.

And compared to other JVM languages it was not good because there was no type-checking or intellisense/autocomplete popups for the methods.

9 comments

> How do people who write Clojure deal with lack of type checking?

The short answer is that you don't. Rich Hickey, the creator of clojure, made the language *very* opinionated by design. And one of the strong options is that folks should be aiming for simplicity in their code, checking their code as they go along using the repl in real time. Dynamic typing makes it much easier to do this.

Whether or not this is a good approach (I happen to like it) is up for debate (and likely a matter of taste too), however that's at least some of the rationale at a high-level.

To really enjoy using clojure, you have to do things the way the language wants you to. If you try to bring your java style workflow into clojure, it's not going to be a lot of fun.

I've found "cider" for emacs to be a really good clojure IDE and repl. The workflow is a bit different from Java - I find that I am looking up documentation within my editor more often (which explains the arguments and return values), and then trying out little snippets locally using the extremely powerful repl. You slowly build up your program from smaller programs that you have tested to work, so by the time you run the whole thing, you only have a couple typos/issues.

I think the other key to working with clojure is to set up your program as a set of transforms on simple data structures as much as possible such that complex class interactions aren't used much, and that cuts down on the need for great intellisense and static typing. Ymmv, of course, but I've found this way of working more productive than when I was working in Java.

You can get autocomplete for Java methods in Cider (Emacs) and probably Cursive (Intellij) afaik. Type checking in intro code can be a bit difficult, but REPL/live development really helps. You are going to catch mismatched type at first call, so you can usually get there by reading the Javadoc, but it does take some practice. Most Clojure code also works with Java through the smallest interface possible at the edge of your system, so it is very rare to have to deal with those issues in your "main" code.
Depending on your IDE, you could use different tools to provide the intellisense-like functionality for clojure. For Jetbrains, there is Cursive, for emacs, Cider, etc. I won't disagree that the beginning experience can be lacking even when coming from Java - for instance a good coding environment also includes setting up a hosted repl, but the extra effort does pay off in being able to code interactively as your program runs.

The lack of type checking is due to the entirely different paradigm that Clojure is built around. There are many benefits to it, but I can understand the frustration if you expect to use the language in a way that goes against that, especially when dealing with the interop layers. The fact that it is built upon the JVM is powerful but it's not necessarily an easy language switching from a language like Java.

> How do people who write Clojure deal with lack of type checking

If you've worked in Python, Ruby or JS it's pretty similar.

What I do is I use variable names that make it more obvious what type things are. I also make use of destructuring and Clojure Spec to indicate what keys a map has or what values in a tuple are supposed to be.

Also the REPL can help you quickly explore the state and functions which you can use to try and inspect the types, that's useful when trying to understand someone else's code base which might not have had the most readable code.

And since you do REPL driven development, as I code I run the code constantly in the REPL which will throw type errors when I make one. It has the bonus of catching logical errors as well as helps me figure out how to implement what I want more quickly.

> And auto-complete when it comes to Java library interop

You should get some auto-complete here depending on your tooling. It should lost all possible methods of an object, just not constrained to the direct type. For me that's often enough as I know kinda what I'm looking for, so I can find it and auto-complete.

If you want the auto-complete to a specific type, you can type hint the object and then the auto-complete will list only methods of that type.

Finally, I do rely on the Javadoc a lot.

> How do people who write Clojure deal with lack of type checking?

Runtime asserts, typically.

It ends up being somewhat less of an issue in practice than someone coming from Java or Kotlin would assume, I think, because clojure really only has a single datatype: a sprawling, immutable soup of nested maps and vectors. The design of the standard library is such that your standard data manipulation functions will basically always work on every data structure you get passed, so you end up designing your internal APIs so that they take in a blob of data, perform an operation if the soup has the right components (and probably throw an exception if not), and then spit out that changed blob of data.

Is this better (or at minimum no worse) than having a type system? That's a broader question of philosophy and taste that I'm still undecided myself (for instance, I think the type systems in Haskell and Rust are really quite valuable, but the ones in Java and Go don't really pull their weight), but it's not something I really miss on a day-to-day basis writing clojure code--it's just a different way of doing things.

> And auto-complete when it comes to Java library interop?

My experience as both a professional and hobbyist clojure user is that this is not a huge issue in practice:

- Java interop tends to get wrapped in clojure defns so you can get your auto-complete pop-up off the namespace alias.

- At least with my setup (emacs+cider), adding a (:import (...)) clause to a ns form allows me to autocomplete on all method names of classes in scope (and provides type-annotated signatures), so I can still auto-complete when performing interop. I believe IntelliJ+cursive is even better about that sort of thing, and if I were working directly with Java libraries/APIs a lot I would probably consider switching IDEs for that reason.

> there was no type-checking (...) intellisense/autocomplete popups for the methods.

First of all, yes there is. Maybe your editor didn't support it, but e.g. IntelliJ IDEA with the Cursive plugin does.

Secondly: https://www.youtube.com/watch?v=aSEQfqNYNAc

For intellisense / auto-complete, that is just down to your IDE.

See here, I have autocomplete and intellisense using VSCode and Calva, but I can also hover a function and see the clojure docs for it, then copy them to my IDE, and run the example code all within my editor. Then remove it when I'm done playing. The doc examples go into a rich comment form.

https://www.youtube.com/watch?v=LeNECtkJw6s

Clojure is dynamically typed, this is different from untyped or lacking type checking.

Also, you can have autocomplete, it's orthogonal to static typing.