General:
- add chat bot for #mal
- - move tokenizer.mal and reader.mal from malc along with
- ./examples/{equality,memoize,pprint,protocols}.mal and
- ./core.mal to ./lib directory
+ - move tokenizer.mal and reader.mal from malc to ./lib directory
- Finish guide.md
- mention that identifier names are suggested. some have run
-;;
-;; memoize.mal
-;;
+;; 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
-;;
-;; Memoize any function
(def! memoize
(fn* [f]
(let* [mem (atom {})]
(swap! mem assoc key ret)
ret))))))))
-;; Naive (non-memoized) Fibonacci function
-(def! fib
- (fn* [n]
- (if (<= n 1)
- n
- (+ (fib (- n 1)) (fib (- n 2))))))
+nil
+;; Benchmarks for memoize.mal
-;; -----------------------------------------------
-;; Benchmarks
+(load-file "../lib/heavy_computations.mal") ; fib
+;; FIXME (load-file "../lib/memoize.mal")
+(load-file "../lib/perf.mal") ; time
-(load-file "../perf.mal") ; for the 'time' macro
(def! N 32)
;; Benchmark naive 'fib'
+;; 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)
(def! pprint (fn* [obj]
(println (pp- obj 0))))
+nil
+
+;; Examples of the pretty printer.
+
+;; FIXME (load-file "../lib/pprint.mal") and uncomment
;;(pprint '(7 8 9 "ten" [11 12 [13 14]] 15 16))
;;(pprint '{:abc 123 :def {:ghi 456 :jkl [789 "ten eleven twelve"]}})
+;; FIXME lib/memoize.mal
;; A sketch of Clojure-like protocols, implemented in Mal
;; By chouser (Chris Houser)
;; Original: https://gist.github.com/Chouser/6081ea66d144d13e56fc
(if (first more)
(apply extend type more)))))
-;;----
-;; Example:
+nil
+
+;; Examples for protocols.
+
+;; FIXME (load-file "../lib/protocols.mal")
(def! make-triangle (fn* [o a]
^{:type :shape/triangle} {:opposite o, :adjacent a}))
--- /dev/null
+This directory contains general-purpose reusable code that does not
+fit in the process like `cond`, `gensym`, `inc`, `not` and `or`.
+
+The split in small files is motivated by implementations too limited
+to load a single big file, but MAL has no proper module management.
+
+However, here are some guidelines.
+
+- Begin with an one-line ;; short description
+
+- End with `nil`, so that the result of `load-file` is conveniently
+ short when loading manually and predictilbe for automatic testing
+
+- Describe the restrictions on each parameter in comments.
+
+- Define private symbols in hidden environments when possible. If this
+ is not possible, for example for macros, give them a name starting
+ with an underscore.
+
+- Support successive imports safely by giving the same definitions
+ again.
-;; Kind of standard library for MAL for tests and examples.
-
-;; `cond` `gensym` `inc` `not` `or` would make sense here but are
-;; already defined in the step files as part of the process.
+;; 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)
;; 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
;; 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))
;; return : boolean
(if (empty? xs)
nil
+ ;; FIXME use or
(let* (res (pred (first xs)))
(if res
res
(some pred (rest xs)))))))
-;; Search for first successful evaluation.
+;; 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
-;; rn
-;; x) ..)))
+;; 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))
`(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,
;; 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))
;; (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))))
-;; This `nil` is intentional so that the result of doing `load-file` is
-;; `nil` instead of whatever happens to be at the end of `core.mal`.
nil
-;;
;; equality.mal
-;;
+
;; This file checks whether the `=` function correctly implements equality of
;; hash-maps and sequences (lists and vectors). If not, it redefines the `=`
;; function with a pure mal (recursive) implementation that only relies on the
;; native original `=` function for comparing scalars (integers, booleans,
;; symbols, strings).
-;;
;; Save the original (native) `=` as scalar-equal?
(def! scalar-equal? =)
+;; FIXME: test this
+;; A faster `and` macro which doesn't use `=` internally.
+;; (defmacro! and2 ; `true` or `nil`
+;; (fn* [& xs] ; interpreted as logical values
+;; (if (empty? xs)
+;; true
+;; `(if ~(first xs) (and2 ~(rest xs))))))
;; A simple `and` macro for two argument which doesn't use `=` internally
(defmacro! and2
(fn* [a b]
`(let* (and2_FIXME ~a)
(if and2_FIXME ~b and2_FIXME))))
+;; FIXME: in the following, replace (and2 a (and2 b c)) with (and2 a b c),
+;; replace (if a b false) with (and2 a b).
+
;; Implement `=` for two sequential arguments
(def! sequential-equal?
(fn* [a b]
(do
(def! = mal-equal?)
(println "equality.mal: Replaced = with pure mal implementation")))
+
+nil
--- /dev/null
+;; Some inefficient arithmetic computations for benchmarking.
+
+;; Unfortunately not yet available in tests of steps 4 and 5.
+
+;; Compute n(n+1)/2 with a non tail-recursive call.
+(def! sumdown
+ (fn* [n] ; non-negative number
+ (if (= n 0)
+ 0
+ (+ n (sumdown (- n 1))))))
+
+;; Compute a Fibonacci number with two recursions.
+(def! fib
+ (fn* [n] ; non-negative number
+ (if (<= n 1)
+ n
+ (+ (fib (- n 1)) (fib (- n 2))))))
+
+nil
+;; Mesure performances.
+
+;; Evaluate an expression, but report the time spent
(defmacro! time
(fn* (exp)
`(let* (start_FIXME (time-ms)
(prn (str "Elapsed time: " (- (time-ms) start_FIXME) " msecs"))
ret_FIXME))))
+;; FIXME: hide this private routine in a private environment
(def! run-fn-for*
(fn* [fn max-ms acc-ms last-iters]
(let* [start (time-ms)
last-iters
(run-fn-for* fn max-ms new-acc-ms iters)))))
+;; Count evaluations of a function during a given time frame.
+;; FIXME: max-secs is a number.
(def! run-fn-for
(fn* [fn max-secs]
(do
(run-fn-for* fn 1000 0 0)
;; Now do the test
(run-fn-for* fn (* 1000 max-secs) 0 0))))
+
+nil
-(load-file "../core.mal")
-(load-file "../perf.mal")
+(load-file "../lib/core.mal") ; or ->
+(load-file "../lib/perf.mal") ; time
;;(prn "Start: basic macros performance test")
-(load-file "../core.mal")
-(load-file "../perf.mal")
+(load-file "../lib/heavy_computations.mal") ; fib sumdown
+(load-file "../lib/perf.mal") ; time
;;(prn "Start: basic math/recursion test")
-(def! sumdown (fn* (N) (if (> N 0) (+ N (sumdown (- N 1))) 0)))
-(def! fib (fn* (N) (if (= N 0) 1 (if (= N 1) 1 (+ (fib (- N 1)) (fib (- N 2)))))))
-
(time (do
(sumdown 10)
(fib 12)))
-(load-file "../core.mal")
-(load-file "../perf.mal")
+(load-file "../lib/core.mal") ; or ->
+(load-file "../lib/perf.mal") ; run-fn-for
;;(prn "Start: basic macros/atom test")
;=>"yes"
;;
-;; Loading core.mal
-(load-file "../core.mal")
+(load-file "../lib/core.mal")
;; Testing reduce
(reduce + 7 [])
(let* [or_FIXME 23] (or false (+ or_FIXME 100)))
;=>123
+
;;
;; Testing time-ms function
+(load-file "../lib/heavy_computations.mal")
(def! start-time (time-ms))
(= start-time 0)
;=>false
-(let* [sumdown (fn* (N) (if (> N 0) (+ N (sumdown (- N 1))) 0))] (sumdown 10)) ; Waste some time
+(sumdown 10) ; Waste some time
;=>55
(> (time-ms) start-time)
;=>true