> Clojure is a dynamic language. Dynamically typed. As a dynamic language Clojure is less capable than many other Lisp implementations/languages. See for example 'late binding'. In Clojure you either need to define a function before its use or need declare it. Not that 'dynamic'. In a typical Lisp dialect the order of definitions makes much less a difference, since functions can be called late-bound. For an interpret…
You can create explicit and standardised structures for maps in Clojure, or indeed any other data structure:
(s/def :foo.student/name string?)
(s/def :foo.student/score nat-int?)
(s/def :foo/student (s/keys :req [:foo.student/name :foo.student/score]))
(def example-student
#:foo.student{:name "Alice", :score 30})
However, Clojure takes a somewhat divergent philosophy, as it encourages writing schema for individual fields, rather than a schema for a grouping of fields (such as an object).For example:
{:foo.person/name "Alice"
:foo.student/id "xyz123456"}
The namespaces of each keyword are different, but they're grouped in the same map as they happen to refer to the same entity.So rather than operating on a fixed type like `Student`, a function would request that it requires a data structure that has a person's name and a student ID:
(s/fdef enrol
:args (s/cat :student (s/keys :req [:foo.person/name :foo.student/id])))
We're still validating (albeit dynamically), but we can be more flexible in what data we ask for. This ties in with Clojure's idea of simplicity; a function shouldn't know about data it doesn't intend to use.