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 clojure@googlegroups.com
To unsubscribe from this group, send email to 
clojure+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/clojure?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to