diff --git a/examples/memoize.mal b/examples/memoize.mal index 9459f2a5..31648ba2 100644 --- a/examples/memoize.mal +++ b/examples/memoize.mal @@ -1,33 +1,8 @@ -;; FIXME lib/memoize.mal -;; Memoize any function. - -;; Implement `memoize` using an atom (`mem`) which holds the memoized results -;; (hash-map from the arguments to the result). When the function is called, -;; the hash-map is checked to see if the result for the given argument was already -;; calculated and stored. If this is the case, it is returned immediately; -;; otherwise, it is calculated and stored in `mem`. - -;; Adapted from http://clojure.org/atoms - -(def! memoize - (fn* [f] - (let* [mem (atom {})] - (fn* [& args] - (let* [key (str args)] - (if (contains? @mem key) - (get @mem key) - (let* [ret (apply f args)] - (do - (swap! mem assoc key ret) - ret)))))))) - -nil - ;; Benchmarks for memoize.mal (load-file "../lib/heavy_computations.mal") ; fib -;; FIXME (load-file "../lib/memoize.mal") -(load-file "../lib/perf.mal") ; time +(load-file "../lib/memoize.mal") ; memoize +(load-file "../lib/perf.mal") ; time (def! N 32) diff --git a/examples/pprint.mal b/examples/pprint.mal index e16a7caf..9b5ff7e0 100644 --- a/examples/pprint.mal +++ b/examples/pprint.mal @@ -1,50 +1,7 @@ -;; FIXME lib/pprint.mal -;; Pretty printer a MAL object. - -;; FIXME: hide these private routines in a private environment. - -(def! spaces- (fn* [indent] - (if (> indent 0) - (str " " (spaces- (- indent 1))) - ""))) - -(def! pp-seq- (fn* [obj indent] - (let* [xindent (+ 1 indent)] - (apply str (pp- (first obj) 0) - (map (fn* [x] (str "\n" (spaces- xindent) - (pp- x xindent))) - (rest obj)))))) - -(def! pp-map- (fn* [obj indent] - (let* [ks (keys obj) - kindent (+ 1 indent) - kwidth (count (seq (str (first ks)))) - vindent (+ 1 (+ kwidth kindent))] - (apply str (pp- (first ks) 0) - " " - (pp- (get obj (first ks)) 0) - (map (fn* [k] (str "\n" (spaces- kindent) - (pp- k kindent) - " " - (pp- (get obj k) vindent))) - (rest (keys obj))))))) - -(def! pp- (fn* [obj indent] - (cond - (list? obj) (str "(" (pp-seq- obj indent) ")") - (vector? obj) (str "[" (pp-seq- obj indent) "]") - (map? obj) (str "{" (pp-map- obj indent) "}") - :else (pr-str obj)))) - -(def! pprint (fn* [obj] - (println (pp- obj 0)))) - -nil - ;; Examples of the pretty printer. -;; FIXME (load-file "../lib/pprint.mal") and uncomment +(load-file "../lib/pprint.mal") ; pprint -;;(pprint '(7 8 9 "ten" [11 12 [13 14]] 15 16)) -;;(pprint '{:abc 123 :def {:ghi 456 :jkl [789 "ten eleven twelve"]}}) -;;(pprint '(7 8 {:abc 123 :def {:ghi 456 :jkl 789}} 9 10 [11 12 [13 14]] 15 16)) +(pprint '(7 8 9 "ten" [11 12 [13 14]] 15 16)) +(pprint '{:abc 123 :def {:ghi 456 :jkl [789 "ten eleven twelve"]}}) +(pprint '(7 8 {:abc 123 :def {:ghi 456 :jkl 789}} 9 10 [11 12 [13 14]] 15 16)) diff --git a/examples/protocols.mal b/examples/protocols.mal index bf950ae9..44d059e7 100644 --- a/examples/protocols.mal +++ b/examples/protocols.mal @@ -1,51 +1,6 @@ -;; FIXME lib/memoize.mal -;; A sketch of Clojure-like protocols, implemented in Mal -;; By chouser (Chris Houser) -;; Original: https://gist.github.com/Chouser/6081ea66d144d13e56fc - -(def! builtin-type (fn* [obj] - (cond - (list? obj) :mal/list - (vector? obj) :mal/vector - (map? obj) :mal/map - (symbol? obj) :mal/symbol - (keyword? obj) :mal/keyword - (atom? obj) :mal/atom - (nil? obj) nil - (true? obj) :mal/bool - (false? obj) :mal/bool))) - -(def! find-protocol-methods (fn* [protocol obj] - (let* [p @protocol] - (or (get p (get (meta obj) :type)) - (get p (builtin-type obj)) - (get p :mal/default))))) - -(def! satisfies? (fn* [protocol obj] - (if (find-protocol-methods protocol obj) true false))) - -(defmacro! defprotocol (fn* [proto-name & methods] - `(do - (def! ~proto-name (atom {})) - ~@(map (fn* [m] - (let* [name (first m), sig (first (rest m))] - `(def! ~name (fn* [this-FIXME & args-FIXME] - (apply (get (find-protocol-methods ~proto-name this-FIXME) - ~(keyword (str name))) - this-FIXME args-FIXME))))) - methods)))) - -(def! extend (fn* [type proto methods & more] - (do - (swap! proto assoc type methods) - (if (first more) - (apply extend type more))))) - -nil - ;; Examples for protocols. -;; FIXME (load-file "../lib/protocols.mal") +(load-file "../lib/protocols.mal") ; defprotocol extend satisfies (def! make-triangle (fn* [o a] ^{:type :shape/triangle} {:opposite o, :adjacent a})) diff --git a/lib/composition.mal b/lib/composition.mal new file mode 100644 index 00000000..b6d0aae8 --- /dev/null +++ b/lib/composition.mal @@ -0,0 +1,33 @@ +;; Composition of partially applied functions. + +(load-file "../lib/folds.mal") ; reduce + +;; Rewrite x (a a1 a2) .. (b b1 b2) as +;; (b (.. (a x a1 a2) ..) b1 b2) +;; If anything else than a list is found were `(a a1 a2)` is expected, +;; replace it with a list with one element, so that `-> x a` is +;; equivalent to `-> x (list a)`. +(defmacro! -> + (fn* (x & xs) + ;; FIXME define this only once + (let* [f (fn* [acc form] + (if (list? form) + `(~(first form) ~acc ~@(rest form)) + (list form acc)))] + (reduce f x xs)))) + +;; Like `->`, but the arguments describe functions that are partially +;; applied with *left* arguments. The previous result is inserted at +;; the *end* of the new argument list. +;; Rewrite x ((a a1 a2) .. (b b1 b2)) as +;; (b b1 b2 (.. (a a1 a2 x) ..)). +(defmacro! ->> + (fn* (x & xs) + ;; FIXME define this only once + (let* [f (fn* [acc form] + (if (list? form) + `(~(first form) ~@(rest form) ~acc) + (list form acc)))] + (reduce f x xs)))) + +nil diff --git a/lib/core.mal b/lib/core.mal deleted file mode 100644 index 6054e7ad..00000000 --- a/lib/core.mal +++ /dev/null @@ -1,139 +0,0 @@ -;; FIXME: trivial.mal -;; Trivial but convenient functions. - -;; Integer predecessor (number -> number) -(def! dec (fn* (a) (- a 1))) - -;; Integer nullity test (number -> boolean) -(def! zero? (fn* (n) (= 0 n))) - -;; FIXME: folds.mal -;; Left and right folds. - -;; Left fold (f (.. (f (f init x1) x2) ..) xn) -(def! reduce - (fn* (f init xs) - ;; f : Accumulator Element -> Accumulator - ;; init : Accumulator - ;; xs : sequence of Elements x1 x2 .. xn - ;; return : Accumulator - (if (empty? xs) - init - (reduce f (f init (first xs)) (rest xs))))) - -;; Right fold (f x1 (f x2 (.. (f xn init)) ..)) -;; The natural implementation for `foldr` is not tail-recursive, so we -;; rely on efficient `nth` and `count`. -(def! foldr - (fn* [f init xs] - ;; f : Element Accumulator -> Accumulator - ;; init : Accumulator - ;; xs : sequence of Elements x1 x2 .. xn - ;; return : Accumulator - ;; FIXME: pass f and xs and build this only once in a private env - (let* [rec (fn* [acc index] - (if (< index 0) - acc - (rec (f (nth xs index) acc) (dec index))))] - ;; FIXME stop using dec or load trivial.mal - (rec init (dec (count xs)))))) - -nil - -;; FIXME: lib/trivial.mal -;; Returns the unchanged argument. -(def! identity (fn* (x) x)) - -;; FIXME: test_cascade.mal -;; Iteration on evaluations interpreted as boolean values. - -;; Conjonction of predicate values (pred x1) and .. and (pred xn) -;; Evaluate `pred x` for each `x` in turn. Return `false` if a result -;; is `nil` or `false`, without evaluating the predicate for the -;; remaining elements. If all test pass, return `true`. -(def! every? - (fn* (pred xs) - ;; pred : Element -> interpreted as a logical value - ;; xs : sequence of Elements x1 x2 .. xn - ;; return : boolean - ;; FIXME: use cond - (if (empty? xs) - true - (if (pred (first xs)) - (every? pred (rest xs)) - false)))) - -;; Disjonction of predicate values (pred x1) or .. (pred xn) -;; Evaluate `(pred x)` for each `x` in turn. Return the first result -;; that is neither `nil` nor `false`, without evaluating the predicate -;; for the remaining elements. If all tests fail, return nil. -(def! some - (fn* (pred xs) - ;; pred : Element -> interpreted as a logical value - ;; xs : sequence of Elements x1 x2 .. xn - ;; return : boolean - (if (empty? xs) - nil - ;; FIXME use or - (let* (res (pred (first xs))) - (if res - res - (some pred (rest xs))))))) - -;; Search for first evaluation returning `nil` or `false`. -;; Rewrite `x1 x2 .. xn x` as -;; (let* [r1 x1] -;; (if r1 test1 -;; (let* [r2 x2] -;; .. -;; (if rn -;; x -;; rn) ..) -;; r1)) -;; Without arguments, returns `true`. -(defmacro! and - (fn* (& xs) - ;; Arguments and the result are interpreted as boolean values. - ;; FIXME: use cond - (if (empty? xs) - true - (if (= 1 (count xs)) - (first xs) - (let* (condvar (gensym)) - `(let* (~condvar ~(first xs)) - (if ~condvar (and ~@(rest xs)) ~condvar))))))) - -;; FIXME: composition.mal -;; Composition of partially applied functions. - -;; FIXME (load-file "../lib/folds.mal") ; reduce - -;; Rewrite x (a a1 a2) .. (b b1 b2) as -;; (b (.. (a x a1 a2) ..) b1 b2) -;; If anything else than a list is found were `(a a1 a2)` is expected, -;; replace it with a list with one element, so that `-> x a` is -;; equivalent to `-> x (list a)`. -(defmacro! -> - (fn* (x & xs) - ;; FIXME define this only once - (let* [f (fn* [acc form] - (if (list? form) - `(~(first form) ~acc ~@(rest form)) - (list form acc)))] - (reduce f x xs)))) - -;; Like `->`, but the arguments describe functions that are partially -;; applied with *left* arguments. The previous result is inserted at -;; the *end* of the new argument list. -;; Rewrite x ((a a1 a2) .. (b b1 b2)) as -;; (b b1 b2 (.. (a a1 a2 x) ..)). -(defmacro! ->> - (fn* (x & xs) - ;; FIXME define this only once - (let* [f (fn* [acc form] - (if (list? form) - `(~(first form) ~@(rest form) ~acc) - (list form acc)))] - (reduce f x xs)))) - -nil diff --git a/lib/folds.mal b/lib/folds.mal new file mode 100644 index 00000000..6393f9f6 --- /dev/null +++ b/lib/folds.mal @@ -0,0 +1,31 @@ +;; Left and right folds. + +;; Left fold (f (.. (f (f init x1) x2) ..) xn) +(def! reduce + (fn* (f init xs) + ;; f : Accumulator Element -> Accumulator + ;; init : Accumulator + ;; xs : sequence of Elements x1 x2 .. xn + ;; return : Accumulator + (if (empty? xs) + init + (reduce f (f init (first xs)) (rest xs))))) + +;; Right fold (f x1 (f x2 (.. (f xn init)) ..)) +;; The natural implementation for `foldr` is not tail-recursive, so we +;; rely on efficient `nth` and `count`. +(def! foldr + (fn* [f init xs] + ;; f : Element Accumulator -> Accumulator + ;; init : Accumulator + ;; xs : sequence of Elements x1 x2 .. xn + ;; return : Accumulator + ;; FIXME: pass f and xs and build this only once in a private env + (let* [rec (fn* [acc index] + (if (< index 0) + acc + (rec (f (nth xs index) acc) (dec index))))] + ;; FIXME stop using dec or load trivial.mal + (rec init (dec (count xs)))))) + +nil diff --git a/lib/memoize.mal b/lib/memoize.mal new file mode 100644 index 00000000..4df43646 --- /dev/null +++ b/lib/memoize.mal @@ -0,0 +1,23 @@ +;; Memoize any function. + +;; Implement `memoize` using an atom (`mem`) which holds the memoized results +;; (hash-map from the arguments to the result). When the function is called, +;; the hash-map is checked to see if the result for the given argument was already +;; calculated and stored. If this is the case, it is returned immediately; +;; otherwise, it is calculated and stored in `mem`. + +;; Adapted from http://clojure.org/atoms + +(def! memoize + (fn* [f] + (let* [mem (atom {})] + (fn* [& args] + (let* [key (str args)] + (if (contains? @mem key) + (get @mem key) + (let* [ret (apply f args)] + (do + (swap! mem assoc key ret) + ret)))))))) + +nil diff --git a/lib/pprint.mal b/lib/pprint.mal new file mode 100644 index 00000000..5c0b741c --- /dev/null +++ b/lib/pprint.mal @@ -0,0 +1,41 @@ +;; Pretty printer a MAL object. + +;; FIXME: hide these private routines in a private environment. + +(def! spaces- (fn* [indent] + (if (> indent 0) + (str " " (spaces- (- indent 1))) + ""))) + +(def! pp-seq- (fn* [obj indent] + (let* [xindent (+ 1 indent)] + (apply str (pp- (first obj) 0) + (map (fn* [x] (str "\n" (spaces- xindent) + (pp- x xindent))) + (rest obj)))))) + +(def! pp-map- (fn* [obj indent] + (let* [ks (keys obj) + kindent (+ 1 indent) + kwidth (count (seq (str (first ks)))) + vindent (+ 1 (+ kwidth kindent))] + (apply str (pp- (first ks) 0) + " " + (pp- (get obj (first ks)) 0) + (map (fn* [k] (str "\n" (spaces- kindent) + (pp- k kindent) + " " + (pp- (get obj k) vindent))) + (rest (keys obj))))))) + +(def! pp- (fn* [obj indent] + (cond + (list? obj) (str "(" (pp-seq- obj indent) ")") + (vector? obj) (str "[" (pp-seq- obj indent) "]") + (map? obj) (str "{" (pp-map- obj indent) "}") + :else (pr-str obj)))) + +(def! pprint (fn* [obj] + (println (pp- obj 0)))) + +nil diff --git a/lib/protocols.mal b/lib/protocols.mal new file mode 100644 index 00000000..1fd10aac --- /dev/null +++ b/lib/protocols.mal @@ -0,0 +1,44 @@ +;; A sketch of Clojure-like protocols, implemented in Mal + +;; By chouser (Chris Houser) +;; Original: https://gist.github.com/Chouser/6081ea66d144d13e56fc + +(def! builtin-type (fn* [obj] + (cond + (list? obj) :mal/list + (vector? obj) :mal/vector + (map? obj) :mal/map + (symbol? obj) :mal/symbol + (keyword? obj) :mal/keyword + (atom? obj) :mal/atom + (nil? obj) nil + (true? obj) :mal/bool + (false? obj) :mal/bool))) + +(def! find-protocol-methods (fn* [protocol obj] + (let* [p @protocol] + (or (get p (get (meta obj) :type)) + (get p (builtin-type obj)) + (get p :mal/default))))) + +(def! satisfies? (fn* [protocol obj] + (if (find-protocol-methods protocol obj) true false))) + +(defmacro! defprotocol (fn* [proto-name & methods] + `(do + (def! ~proto-name (atom {})) + ~@(map (fn* [m] + (let* [name (first m), sig (first (rest m))] + `(def! ~name (fn* [this-FIXME & args-FIXME] + (apply (get (find-protocol-methods ~proto-name this-FIXME) + ~(keyword (str name))) + this-FIXME args-FIXME))))) + methods)))) + +(def! extend (fn* [type proto methods & more] + (do + (swap! proto assoc type methods) + (if (first more) + (apply extend type more))))) + +nil diff --git a/lib/test_cascade.mal b/lib/test_cascade.mal new file mode 100644 index 00000000..5a028a3b --- /dev/null +++ b/lib/test_cascade.mal @@ -0,0 +1,59 @@ +;; Iteration on evaluations interpreted as boolean values. + +;; Conjonction of predicate values (pred x1) and .. and (pred xn) +;; Evaluate `pred x` for each `x` in turn. Return `false` if a result +;; is `nil` or `false`, without evaluating the predicate for the +;; remaining elements. If all test pass, return `true`. +(def! every? + (fn* (pred xs) + ;; pred : Element -> interpreted as a logical value + ;; xs : sequence of Elements x1 x2 .. xn + ;; return : boolean + ;; FIXME: use cond + (if (empty? xs) + true + (if (pred (first xs)) + (every? pred (rest xs)) + false)))) + +;; Disjonction of predicate values (pred x1) or .. (pred xn) +;; Evaluate `(pred x)` for each `x` in turn. Return the first result +;; that is neither `nil` nor `false`, without evaluating the predicate +;; for the remaining elements. If all tests fail, return nil. +(def! some + (fn* (pred xs) + ;; pred : Element -> interpreted as a logical value + ;; xs : sequence of Elements x1 x2 .. xn + ;; return : boolean + (if (empty? xs) + nil + ;; FIXME use or + (let* (res (pred (first xs))) + (if res + res + (some pred (rest xs))))))) + +;; Search for first evaluation returning `nil` or `false`. +;; Rewrite `x1 x2 .. xn x` as +;; (let* [r1 x1] +;; (if r1 test1 +;; (let* [r2 x2] +;; .. +;; (if rn +;; x +;; rn) ..) +;; r1)) +;; Without arguments, returns `true`. +(defmacro! and + (fn* (& xs) + ;; Arguments and the result are interpreted as boolean values. + ;; FIXME: use cond + (if (empty? xs) + true + (if (= 1 (count xs)) + (first xs) + (let* (condvar (gensym)) + `(let* (~condvar ~(first xs)) + (if ~condvar (and ~@(rest xs)) ~condvar))))))) + +nil diff --git a/lib/trivial.mal b/lib/trivial.mal new file mode 100644 index 00000000..8c4f6b6b --- /dev/null +++ b/lib/trivial.mal @@ -0,0 +1,12 @@ +;; Trivial but convenient functions. + +;; Integer predecessor (number -> number) +(def! dec (fn* (a) (- a 1))) + +;; Integer nullity test (number -> boolean) +(def! zero? (fn* (n) (= 0 n))) + +;; Returns the unchanged argument. +(def! identity (fn* (x) x)) + +nil