If you came from an object oriented or statically typed programming language
before coming to Clojure, you might have been disoriented at first. Where are
the classes? Where are the types?
Clojure deliberately stepped away from the “kingdom of nouns” found in other
languages. Instead of having many different types, each with their own custom
interface, Clojure code tends to use a small number of general purpose data
structures. This way the focus shifts from how the program is structured, to
what the program does.
This works well because Clojure’s data types are built on a small number of
abstractions, like Seqable, Associative, or Indexed.
“Abstraction” in this case refers to having shared functions that work across a
number of concrete types. Take for example an equality check. Comparing two
vectors is very different from comparing two numbers, you would need a separate
implementation for each. And yet we have a single function that does both. The
equality check automatically adjusts itself to its inputs. This is called
“polymorphism”, from the Greek for “many shapes”. You can also say that it
“dispatches” to the right implementation based on the type of its arguments.
(= [1 2 3] [1 2 3])
(= 5 7)