After a lot of experimentation with types and interfaces, I have
somewhat redesigned clojure.contrib.types. The new version contains
some breaking changes, but the changes are minor.
There are now two different ways to define types, one of them being
algebraic data types, which are now defined by defadt rather than
deftype. The other change concerning algebraic types is that the
first argument to defadt is the type tag (a namespace-qualified
keyword) rather than a symbol from which the keyword is constructed.
This is much simpler.
There is still a deftype, but it now defines something more basic,
which in fact I ended up using more often. In its simplest form,
(deftype ::foo foo)
defines a constructor function foo that simply adds {:type ::foo}
metadata to its argument. It also defines print-method such that an
object made with (foo x) is printed as (foo x). It is up to the
client code to ensure that x is an object that can take metadata,
i.e. in practice a collection.
If you want more control over what goes inside your data structure,
you can provide a constructor function as a third argument:
(deftype ::foo foo (fn [x] {:value x}))
With this definition, foo will call the provided constructor and
attach the type tag to the result. (foo 42) thus yields {:value 42}
with the type tag ::foo. The data representation still must be a
collection, to allow metadata, but the constructor can ensure that it
has specific properties, or verify that the input data satisfies some
conditions.
One inconvenience with the constructor approach is that object
constructed as shown above now print as
(foo {:value 42})
which is not the syntax of the constructor. This can be avoided by
providing a deconstructor function as a fourth argument. We thus have:
(deftype ::foo foo
(fn [x] {:value x})
(comp list value))
The deconstructor function should return the list of arguments that
needs to be given to the constructor in order to create an equivalent
object. It is at the moment used only for printing, but it will be
used in a future extended version of clojure.contrib.types/match as
well.
These basic type definition actually cover most situations in which I
had used algebraic data types before. Algebraic types are needed only
when multiple constructors or argument-free constructors are required.
Future plans: I intend to follow Rich's advice and implement
algebraic types as maps rather than vectors. Client code that does
not depend on the representation should not be affected. I also
intend to extend the match function to non-algebraic data types,
though I won't make promises about the details yet.
Konrad.
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"Clojure" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
-~----------~----~----~----~------~----~------~--~---