6 ms·
> An aspect I don't like. This makes debugging of larger systems more difficult, since maps are maps. Whereas objects have a type tag built in, have a list of a
by weavejester 9y ago
> An aspect I don't like. This makes debugging of larger systems more difficult, since maps are maps. Whereas objects have a type tag built in, have a list of allowed fields, etc. etc. They simply have more more explicit and standardized structure to them.
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.
- lispm 9y ago> standardised structures for maps Then there are defstruct and deftype. So it has maps, struct-maps, records, deftypes, Java classes, ... a whole bunch. 'simplicity'? > This ties in with Clojure's idea of simplicity; a function shouldn't know about data it doesn't intend to use. I would use a class for that, since something like CLOS supports multiple-inheritance and all the necessary mechanisms for mixins. Finding these methods then is supported by the organization in generic functions and hierarchical classes.
- weavejester 9y ago> Then there are defstruct and deftype. So it has maps, struct-maps, records, deftypes, Java classes, ... a whole bunch. defstruct has been deprecated for years. deftype and Java classes are primarily for JVM interop and language extensions. Outside of calling Java libraries there are just maps, which you're going to be using 95% of the time, and records, which are maps with efficient polymorphism. > 'simplicity'? In Clojure parlance, "simplicity" refers to interconnectedness. So a box of tools is "simple", because they're not connected, whereas a Swiss army knife would be "complex", even if it might be easier for a novice to use. Having a bunch of similar tools isn't complex, from Clojure's point of view, as long as they're separate. It may make things more difficult to learn, but that's another problem entirely :) > I would use a class for that, since something like CLOS supports multiple-inheritance and all the necessary mechanisms for mixins. Doesn't that mean you'd need a separate class for each way you access the data if you wanted to type that? My knowledge of CLOS is, I'm afraid, rudimentary, but can you say to a method: take as as argument an object that has the "id" field from the "Student" class, and the "email" field from the "User" class. Or would you have to explicitly pre-define mixins like HasStudentId and HasUserEmail?
- lispm 9y ago> In Clojure parlance, "simplicity" refers to interconnectedness Clojure tends to redefine words describing concepts with a new or restricted meaning. Typically a general concept like 'simplicity' would have more than one dimension. > take as as argument an object that has the "id" field from the "Student" class, and the "email" field from the "User" class. Or would you have to explicitly pre-define mixins like HasStudentId and HasUserEmail? I would not do this at all. Having slots or not are low-level artefacts and writing methods which are fine-grained to slots of an objects don't expose the domain-level. I would use methods for domain level classes which have the necessary slots either local or inherited.
- weavejester 9y ago> Clojure tends to redefine words describing concepts with a new or restricted meaning. Typically a general concept like 'simplicity' would have more than one dimension. Sure, but that happens all the time in programming. When we talk about "objects" we don't mean "a material thing that can be seen and touched". > I would not do this at all. Having slots or not are low-level artefacts and writing methods which are fine-grained to slots of an objects don't expose the domain-level. I guess because it's not at all idiomatic, even in an object system as rich as CLOS. But in Clojure it is idiomatic, and to my mind it leads to a more precise definition of what a function wants. We don't say, "this is a method that operates on a Student object"; we say, "this is a function that takes data that includes a student's email and enrolment date". Again, I hasten to add that my experience with CLOS is virtually non-existent, but compared to the OOP languages I am familiar with, Clojure has a far richer way of describing the structure of data.
- lispm 9y agoI understand that, but as I said I don't like that at all. It binds methods to low-level adhoc features and not to class based domain ontologies. There are other pattern directed invocation systems, which also support that, but I usually prefer the more systematic approach of CLOS. In the tradition of symbolic programming I want to have symbolic classes as my anchors for functionality, not adhoc patterns.