You model your logic/domain as data using immutable values, and write functions that act on that data. There are a few good things that come from this design decision.
It means you can avoid getting into the situation where half your logic is encoded in the type system and enforced at compile time, and the other half as values at run-time. It's all values at run-time.
And for the same reason, you don't fall into the trap of "puzzle-solving" with a type system. Between run-time values and the compiler there is a world of infinite possibilities that some type systems (and I would also include some macro systems!) seem to encourage adding more and more layers to. Clojure tries to speak the language of data, which is a lot more grounded.
It means you can avoid getting into the situation where half your logic is encoded in the type system and enforced at compile time, and the other half as values at run-time. It's all values at run-time.
And for the same reason, you don't fall into the trap of "puzzle-solving" with a type system. Between run-time values and the compiler there is a world of infinite possibilities that some type systems (and I would also include some macro systems!) seem to encourage adding more and more layers to. Clojure tries to speak the language of data, which is a lot more grounded.