Libraries? model and generic-functions

1 view
Skip to first unread message

mikel

unread,
Feb 17, 2009, 9:56:57 AM2/17/09
to Clojure
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.

Raffael Cavallaro

unread,
Feb 17, 2009, 10:20:32 AM2/17/09
to Clojure


On Feb 17, 9:56 am, mikel <mev...@mac.com> wrote:

> I'll let the level of interest
> guide me in whether I package them for more general distribution.



I am very interested in both of these subsystems and would love to see
you package them as clojure.contrib libraries. Hopefully others feel
the same and we'll see an announcement for them here soon.

Best of luck with model and generic functions, and with clojure in
general.

warmest regards,

Ralph

mikel

unread,
Feb 17, 2009, 1:35:35 PM2/17/09
to Clojure


On Feb 17, 9:20 am, Raffael Cavallaro <raffaelcavall...@gmail.com>
wrote:
Good to know; thanks. I think model just needs the packaging done, but
I should probably allow a little more time in use to shake out any
unanticipated problems.

Generic functions work, but probably want a reimplementation to
improve their efficiency and to add some amenities. Probably they want
a little syntactic sugar, and likely they want to be built on a Java
Class like MultiFn rather than on closures. Also, their dispatch
mechanism knows about models, and I think I want to change that for
two reasons: first, dispatching on models can be costly, so it should
probably be optional, which entails an extension to the current
generic functions that would allow users to specify the dispatch
protocol; second, the use of models in the dispatch is the only
dependency between the two subsystems, and I just think it would be
nice to completely separate them. If I can devise a suitably
straightforward way to parameterize the dispatch algorithm for generic
functions that would then become easy to accomplish.

I will say that I am quite enjoying having generic functions and a
composable object model that are completely orthogonal to one another.

wlr

unread,
Feb 18, 2009, 10:17:40 AM2/18/09
to Clojure
On Feb 17, 10:20 am, Raffael Cavallaro <raffaelcavall...@gmail.com>
wrote:
> I am very interested in both of these subsystems and would love to see
> you package them as clojure.contrib libraries. Hopefully others feel
> the same and we'll see an announcement for them here soon.

+1

Rich Hickey

unread,
Feb 18, 2009, 2:18:33 PM2/18/09
to Clojure
I'd like to see a general predicate dispatch based upon Chambers/Chen;

http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.47.4553

http://portal.acm.org/citation.cfm?id=320407

utilizing Clojure's ad-hoc hierarchies.

Rich
Reply all
Reply to author
Forward
0 new messages