diff options
-rw-r--r-- | build.xml | 1 | ||||
-rw-r--r-- | src/clojure/contrib/gen_html_docs.clj | 1 | ||||
-rw-r--r-- | src/clojure/contrib/generic/collection.clj | 140 | ||||
-rw-r--r-- | src/clojure/contrib/load_all.clj | 1 | ||||
-rw-r--r-- | src/clojure/contrib/stream_utils.clj | 12 | ||||
-rw-r--r-- | src/clojure/contrib/stream_utils/examples.clj | 28 | ||||
-rw-r--r-- | src/clojure/contrib/types/examples.clj | 50 |
7 files changed, 177 insertions, 56 deletions
@@ -85,6 +85,7 @@ <arg value="clojure.contrib.except"/> <arg value="clojure.contrib.fcase"/> <arg value="clojure.contrib.generic.arithmetic"/> + <arg value="clojure.contrib.generic.collection"/> <arg value="clojure.contrib.generic.math-functions"/> <arg value="clojure.contrib.import-static"/> <arg value="clojure.contrib.javadoc.browse"/> diff --git a/src/clojure/contrib/gen_html_docs.clj b/src/clojure/contrib/gen_html_docs.clj index 7a587fdf..c00d3513 100644 --- a/src/clojure/contrib/gen_html_docs.clj +++ b/src/clojure/contrib/gen_html_docs.clj @@ -483,6 +483,7 @@ emits the generated HTML to the path named by path." 'clojure.contrib.except 'clojure.contrib.fcase 'clojure.contrib.generic.arithmetic + 'clojure.contrib.generic.collection 'clojure.contrib.generic.math-functions 'clojure.contrib.import-static 'clojure.contrib.javadoc diff --git a/src/clojure/contrib/generic/collection.clj b/src/clojure/contrib/generic/collection.clj new file mode 100644 index 00000000..53e316a3 --- /dev/null +++ b/src/clojure/contrib/generic/collection.clj @@ -0,0 +1,140 @@ +;; Generic interfaces for collection-related functions + +;; by Konrad Hinsen +;; last updated March 16, 2009 + +;; Copyright (c) Konrad Hinsen, 2009. All rights reserved. The use +;; and distribution terms for this software are covered by the Eclipse +;; Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) +;; which can be found in the file epl-v10.html at the root of this +;; distribution. By using this software in any fashion, you are +;; agreeing to be bound by the terms of this license. You must not +;; remove this notice, or any other, from this software. + +(ns clojure.contrib.generic.collection + "Generic collection function interface + + NOTE: This library is VERY experimental. It WILL change significantly + with future release. + + This library defines generic versions of common collection-related functions + such as map or conj as multimethods that can be defined for any type." + (:refer-clojure :exclude [assoc conj dissoc empty get into map seq])) + +; +; assoc +; +(defmulti assoc + "Returns a new collection in which the values corresponding to the + given keys are updated by the given values. Each type of collection + can have specific restrictions on the possible keys." + {:arglists '([coll & key-val-pairs])} + (fn [coll & items] (type coll))) + +(defmethod assoc :default + [map & key-val-pairs] + (apply clojure.core/assoc map key-val-pairs)) + +; assoc-in + +; +; conj +; +(defmulti conj (fn [coll & xs] (type coll))) + +(defmethod conj :default + [coll & xs] + (apply clojure.core/conj coll xs)) + +; +; diassoc +; +(defmulti dissoc + "Returns a new collection in which the entries corresponding to the + given keys are removed. Each type of collection can have specific + restrictions on the possible keys." + {:arglists '([coll & keys])} + (fn [coll & keys] (type coll))) + +(defmethod dissoc :default + [map & keys] + (apply clojure.core/dissoc map keys)) + +; +; empty +; +(defmulti empty + "Returns an empty collection of the same kind as the argument" + {:arglists '([coll])} + type) + +(defmethod empty :default + [coll] + (clojure.core/empty coll)) + +; +; get +; +(defmulti get + "Returns the element of coll referred to by key. Each type of collection + can have specific restrictions on the possible keys." + {:arglists '([coll key] [coll key not-found])} + (fn [coll & args] (type coll))) + +(defmethod get :default + ([coll key] + (clojure.core/get coll key)) + ([coll key not-found] + (clojure.core/get coll key not-found))) + +; +; into +; +; This is a literal copy of into from clojure.core, but it is +; evaluated with the functions from this namespace here! +(declare seq) +(defn into + "Returns a new coll consisting of to-coll with all of the items of + from-coll conjoined." + [to from] + (let [ret to items (seq from)] + (if items + (recur (conj ret (first items)) (next items)) + ret))) + +; +; map +; +(defmulti map + "Applies function f to each element of coll and returns a collection + of the same kind as coll." + {:arglists '([f coll])} + (fn [f coll] (type coll))) + +(defmethod map clojure.lang.ISeq + [f coll] + (clojure.core/map f coll)) + +(defmethod map clojure.lang.IPersistentVector + [f v] + (clojure.core/into (clojure.core/empty v) (clojure.core/map f v))) + +(defmethod map clojure.lang.IPersistentMap + [f m] + (clojure.core/into (clojure.core/empty m) (for [[k v] m] [k (f v)]))) + +(defmethod map clojure.lang.IPersistentSet + [f s] + (clojure.core/into (clojure.core/empty s) (clojure.core/map f s))) + +; +; seq +; +(defmulti seq + "Returns a seq on the object s." + {:arglists '([s])} + type) + +(defmethod seq :default + [s] + (clojure.core/seq s)) diff --git a/src/clojure/contrib/load_all.clj b/src/clojure/contrib/load_all.clj index 9593e2ce..9890803d 100644 --- a/src/clojure/contrib/load_all.clj +++ b/src/clojure/contrib/load_all.clj @@ -43,6 +43,7 @@ error-kit except fcase generic.arithmetic +generic.collection generic.math-functions import-static javadoc.browse diff --git a/src/clojure/contrib/stream_utils.clj b/src/clojure/contrib/stream_utils.clj index 7ce9933f..15761771 100644 --- a/src/clojure/contrib/stream_utils.clj +++ b/src/clojure/contrib/stream_utils.clj @@ -1,7 +1,7 @@ ;; Stream utilities ;; by Konrad Hinsen -;; last updated March 12, 2009 +;; last updated March 16, 2009 ;; Copyright (c) Konrad Hinsen, 2009. All rights reserved. The use ;; and distribution terms for this software are covered by the Eclipse @@ -56,7 +56,8 @@ (:use [clojure.contrib.types :only (deftype deftype-)]) (:use [clojure.contrib.monads :only (defmonad with-monad)]) (:use [clojure.contrib.def :only (defvar defvar-)]) - (:require [clojure.contrib.seq-utils])) + (:require [clojure.contrib.seq-utils]) + (:require [clojure.contrib.generic.collection])) ; @@ -108,7 +109,8 @@ (defn stream-seq "Return a lazy seq on the stream. Also accessible via - clojure.contrib.seq-utils/seq-on for streams." + clojure.contrib.seq-utils/seq-on and + clojure.contrib.generic.collection/seq for streams." [s] (lazy-seq (let [[v ns] (stream-next s)] @@ -120,6 +122,10 @@ [s] (stream-seq s)) +(defmethod clojure.contrib.generic.collection/seq stream-type + [s] + (stream-seq s)) + ; ; Stream transformers ; diff --git a/src/clojure/contrib/stream_utils/examples.clj b/src/clojure/contrib/stream_utils/examples.clj index 10207fff..5b98c65f 100644 --- a/src/clojure/contrib/stream_utils/examples.clj +++ b/src/clojure/contrib/stream_utils/examples.clj @@ -13,8 +13,8 @@ stream-type defstream stream-drop stream-map stream-filter stream-flatten)]) (:use [clojure.contrib.monads :only (domonad)]) - (:use [clojure.contrib.seq-utils :only (seq-on)]) - (:use [clojure.contrib.types :only (deftype)])) + (:use [clojure.contrib.types :only (deftype)]) + (:require [clojure.contrib.generic.collection :as gc])) ; ; Define a stream of Fibonacci numbers @@ -28,7 +28,7 @@ (def fib-stream (last-two-fib [0 1])) -(take 10 (seq-on fib-stream)) +(take 10 (gc/seq fib-stream)) ; ; A simple random number generator, implemented as a stream @@ -43,15 +43,15 @@ next (rem (+ 54773 (* 7141 seed)) m)] [value (rng-seed next)])) -(take 10 (seq-on (rng-seed 1))) +(take 10 (gc/seq (rng-seed 1))) ; ; Various stream utilities ; -(take 10 (seq-on (stream-drop 10 (rng-seed 1)))) -(seq-on (stream-map inc (range 5))) -(seq-on (stream-filter odd? (range 10))) -(seq-on (stream-flatten (partition 3 (range 9)))) +(take 10 (gc/seq (stream-drop 10 (rng-seed 1)))) +(gc/seq (stream-map inc (range 5))) +(gc/seq (stream-filter odd? (range 10))) +(gc/seq (stream-flatten (partition 3 (range 9)))) ; ; Stream transformers @@ -74,7 +74,7 @@ (let [[v5 s] (stream-next s)] [v1 v2 v3 v4 v5]))))) -(seq-on s) +(gc/seq s) ; Map (for a single stream) written as a stream transformer (defst my-map-1 [f] [xs] @@ -82,7 +82,7 @@ [x (pick xs)] (f x))) -(seq-on (my-map-1 inc [1 2 3])) +(gc/seq (my-map-1 inc [1 2 3])) ; Map for two stream arguments (defst my-map-2 [f] [xs ys] @@ -91,7 +91,7 @@ y (pick ys)] (f x y))) -(seq-on (my-map-2 + '(1 2 3 4) '(10 20 30 40))) +(gc/seq (my-map-2 + '(1 2 3 4) '(10 20 30 40))) ; Map for any number of stream arguments (defst my-map [f] [& streams] @@ -99,8 +99,8 @@ [vs pick-all] (apply f vs))) -(seq-on (my-map inc [1 2 3])) -(seq-on (my-map + '(1 2 3 4) '(10 20 30 40))) +(gc/seq (my-map inc [1 2 3])) +(gc/seq (my-map + '(1 2 3 4) '(10 20 30 40))) ; Filter written as a stream transformer (defst my-filter [p] [xs] @@ -108,5 +108,5 @@ [x (pick xs) :when (p x)] x)) -(seq-on (my-filter odd? [1 2 3])) +(gc/seq (my-filter odd? [1 2 3])) diff --git a/src/clojure/contrib/types/examples.clj b/src/clojure/contrib/types/examples.clj index 8f4f98e4..214f7e1c 100644 --- a/src/clojure/contrib/types/examples.clj +++ b/src/clojure/contrib/types/examples.clj @@ -8,7 +8,8 @@ (ns clojure.contrib.types.examples (:use [clojure.contrib.types - :only (deftype defadt match)])) + :only (deftype defadt match)]) + (:require [clojure.contrib.generic.collection :as gc])) ; ; Multisets implemented as maps to integers @@ -19,19 +20,13 @@ (deftype ::multiset multiset) ; Some set operations generalized to multisets -; Note that the multiset constructor is not called anywhere, as the +; Note that the multiset constructor is nowhere called explicitly, as the ; map operations all preserve the metadata. -(defmulti my-conj (fn [& args] (type (first args)))) - -(defmethod my-conj :default - [& args] - (apply clojure.core/conj args)) - -(defmethod my-conj ::multiset +(defmethod gc/conj ::multiset ([ms x] (assoc ms x (inc (get ms x 0)))) ([ms x & xs] - (reduce my-conj (my-conj ms x) xs))) + (reduce gc/conj (gc/conj ms x) xs))) (defmulti union (fn [& sets] (type (first sets)))) @@ -51,8 +46,8 @@ (reduce union (union ms1 ms2) mss))) ; Let's use it: -(my-conj #{} :a :a :b :c) -(my-conj (multiset {}) :a :a :b :c) +(gc/conj #{} :a :a :b :c) +(gc/conj (multiset {}) :a :a :b :c) (union #{:a :b} #{:b :c}) (union (multiset {:a 1 :b 1}) (multiset {:b 1 :c 2})) @@ -79,38 +74,15 @@ (depth empty-tree) (depth a-tree) -; -; Algebraic data types with multimethods: Haskell-style functors -; -(defmulti fmap (fn [f s] (type s))) - -; Sequences -(defmethod fmap clojure.lang.ISeq - [f s] - (map f s)) - -; Vectors -(defmethod fmap clojure.lang.IPersistentVector - [f v] - (into [] (map f v))) - -; Maps -(defmethod fmap clojure.lang.IPersistentMap - [f m] - (into {} (for [[k v] m] [k (f v)]))) - -; Trees -(defmethod fmap ::tree +; Algebraic data types with multimethods: map on a tree +(defmethod gc/map ::tree [f t] (match t empty-tree empty-tree (leaf v) (leaf (f v)) - (node l r) (node (fmap f l) (fmap f r)))) + (node l r) (node (gc/map f l) (gc/map f r)))) -(fmap str '(:a :b :c)) -(fmap str [:a :b :c]) -(fmap str {:a 1 :b 2 :c 3}) -(fmap str a-tree) +(gc/map str a-tree) ; ; Nonsense examples to illustrate all the features of match |