Hacker News new | past | comments | ask | show | jobs | submit login

Type checking is only part of it, the other part is encapsulation of both data and behavior together.

Basically if you have only a few different shapes of data, Clojure is great. But the moment that you have lots of different data types that are similar but require different behavior, Clojure starts to struggle.

Let's make a trivial example. Imagine that there are 4 different ways to identify an object in your system that are recognized industry wide, you cannot eliminate or choose not to support them. These ways are by the keys :foo, :bar, :baz, and :quux. They are not interchangeable, each is unique, and each one requires a different combination of database or API access to use correctly.

In Java you would probably create an empty interface for ObjectIdentifier with tiny-type classes at Foo, Bar, Baz, and Quux. Methods that only need an identifier of any type could accept/return ObjectIdentifier, but at the integration point you could specialize on each type to hit the correct APIs/Database Tables/Etc.

In Clojure you've got if cases, multimethods, and protocols. Frankly we've had big issues with multimethods and protocols. Without enumerating every single issue we've had, they tend to increase the WTFs per day of the system, and they easily confuse both the programmer and their tooling.

So you end up with if cases, probably related to key/values in a map somewhere. But now you've got two really big problems:

1) Methods that just need an arbitrary identifier of some type end up having to pass the whole map, or use select-keys all over the place, requiring that you modify every point that touches this map when you need to add/remove an identifier.

2) You end up with an implicit ordering between these identifiers. What do you do when your map contains {:foo "foo identifier" :bar "bar identifier"}? In Java you'd have a Foo instance or a Bar instance, never both. But in Clojure you need to pick which one to use which implicitly ranks these choices, or throw an exception or similar. Now obviously if you're deserializing json you will need to make that choice in any language, but in a static one you make that choice exactly once and you're provided a measure of safety by the compiler. In Clojure those decision points leak all over your system.

Ultimately there are ways out of this issue in Clojure, and we've actually tried several. But frankly a lot of the solutions start to feel like a half-baked implementation of plain old Java interfaces and objects.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: