Two of the subsystems in my app project might usefully be packaged and
distributed as libraries, if people want them. I thought I'd describe
them, so as to discover whether I should be planning for that.

Why not post this to Rich's "Got a Clojure Library?" thread? Because
that thread is presently a nice catalog of available libraries, and I
don't want to clutter it with descriptions of things that might or
might not be of interest.

Anyhow, below are the descriptions; I'll let the level of interest
guide me in whether I package them for more general distribution.

1. model
-------------

model is a subsystem for creating maps based on other maps.They are
similar in purpose to structs, but provide some additional facilities
for defining constraints on values allowed in fields, for combining
models to yield composite models, and for testing whether a given map
is an instance of one or more models.

For example, my application code contains these definitions:

(def <node>
     (model :port {:class java.lang.Integer}
            :input-stream {:class java.io.Reader}
            :output-stream {:class java.io.Writer}
            :error-output-stream
               {:class java.io.Writer,
                :default *out*}))

(def <client-node>
     (combine-models <node>
                     (model :client-socket nil)
                     :strictly))

(def <server-node>
     (combine-models <node>
                     (model
                       :server-socket nil
                       :server-function {:criterion ifn?})
                     :strictly))

The "<...>" notation is just a naming convention: models that get
reused a lot are stored on Vars whose names are bracketed with "<" and
">".

The function model returns a model. A model is just a map that is
intended to be used for creating other maps, similar to s struct, but
with some additional features:

- the values stored on a model are descriptions of the values that are
allowed in an instance of the model; you can see some of the possible
value constraints above. You can declare that a field allows any
value, or that it restricts values by class, by model, or by some
functional criterion.

- make-instance creates a map that is an instance of a model, ensuring
that any initial values conform to the declared value constraints

- combine-models creates a new model that contains the keys and
associated value constraints of the input models. When the input
models have a key in common, there are two ways the ambiguity can be
resolved:
   "permissively" means the key from the leftmost model is used
   "strictly" means that the two constraints must be non-
contradictory, and are combined in the output model
   The default moethod is permissively; a :strictly keyword elects the
strict rules

- (model? x m strict?) returns true if x is a map, m is a model, and
the keys of m are all present in x. If strict? is true, then model?
also checks whether the values for those keys all satisfy the
corresponding value constraints in m.



2. generic functions
-----------------------------

Generic functions are functions that dispatch to methods based on
certain tests against their arguments. They are conceptually similar
to Clojure MultiFns, but use a different dispatching mechanism: given
a set of arguments, a generic function compares them to the methods
defined on the generic function. It collects all defined methods that
can be applied to the given arguments (that is, all methods whose
declared parameters are compatible with the actual parameters
supplied); it orders the found methods by specificity, and it then
applies the most specific method to the actual arguments, in an
environment where the remaining applicable methods are accessible
through a function called next-method.

In other words, you can create a generic function:

(def frob (make-generic-function))

Then you can add some methods to it:

(add-gf-method frob [java.lang.Number java.lang.Number] (fn [x y] (+ x
y)))

(add-gf-method frob [java.lang.String java.lang.String] (fn [x y] (str
x " " y)))

(add-gf-method frob [java.lang.String java.lang.Integer] (fn [s i]
(apply str (take i (cycle [s])))))

(add-gf-method frob [{:test (fn [x] (= x "swordfish"))}] (fn [s] (str
"You said the secret word, you win a hundred dollars!")))

user> (frob "hello" "world")
"hello world"

user> (frob 2 3)
5

user> (frob "foo" 5)
"foofoofoofoofoo"

user> (frob "swordfish")
"You said the secret word, you win a hundred dollars!"

Generic functions dispatch on class, model, equality to a specific
value, and satisfaction of a user-supplied predicate. That list is
from least- to most-specific (so, for example. a match that satisfies
a user predicate will always override one that matches a class).


I'm using the model code fairly extensively now; the generic functions
satisfy some tests and are in modest use, but would require additional
work to be more generally useful.
--~--~---------~--~----~------------~-------~--~----~
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