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 -~----------~----~----~----~------~----~------~--~---