Prepare move of reusable code to lib/.
authorNicolas Boulenguez <nicolas.boulenguez@free.fr>
Wed, 15 May 2019 03:05:26 +0000 (05:05 +0200)
committerNicolas Boulenguez <nicolas.boulenguez@free.fr>
Fri, 17 May 2019 23:52:13 +0000 (01:52 +0200)
Changes creating huge diffs, like file splits, have been delayed for
readability .
Also fix description of `and`.

14 files changed:
docs/TODO
examples/memoize.mal
examples/pprint.mal
examples/protocols.mal
lib/README.md [new file with mode: 0644]
lib/core.mal [moved from core.mal with 82% similarity]
lib/equality.mal [moved from examples/equality.mal with 84% similarity]
lib/heavy_computations.mal [new file with mode: 0644]
lib/perf.mal [moved from perf.mal with 75% similarity]
tests/perf1.mal
tests/perf2.mal
tests/perf3.mal
tests/step8_macros.mal
tests/stepA_mal.mal

index a12321c..8be2f02 100644 (file)
--- a/docs/TODO
+++ b/docs/TODO
@@ -5,9 +5,7 @@ Then refresh the .png files.
 
 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
index 0f9a882..9459f2a 100644 (file)
@@ -1,16 +1,14 @@
-;;
-;; 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'
index 5039215..e16a7ca 100644 (file)
@@ -1,3 +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)
 (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"]}})
index 4c31160..bf950ae 100644 (file)
@@ -1,3 +1,4 @@
+;; 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}))
diff --git a/lib/README.md b/lib/README.md
new file mode 100644 (file)
index 0000000..ba02e11
--- /dev/null
@@ -0,0 +1,21 @@
+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.
similarity index 82%
rename from core.mal
rename to lib/core.mal
index dd8bd3a..6054e7a 100644 (file)
--- a/core.mal
@@ -1,7 +1,5 @@
-;; 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)))
@@ -9,6 +7,9 @@
 ;; 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
@@ -47,6 +56,7 @@
     ;; 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
similarity index 84%
rename from examples/equality.mal
rename to lib/equality.mal
index 39dfdba..98ceea9 100644 (file)
@@ -1,22 +1,30 @@
-;;
 ;; 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]
@@ -76,3 +84,5 @@
   (do
     (def! = mal-equal?)
     (println "equality.mal: Replaced = with pure mal implementation")))
+
+nil
diff --git a/lib/heavy_computations.mal b/lib/heavy_computations.mal
new file mode 100644 (file)
index 0000000..24dc00c
--- /dev/null
@@ -0,0 +1,19 @@
+;; 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
similarity index 75%
rename from perf.mal
rename to lib/perf.mal
index 55f9409..c192013 100644 (file)
--- a/perf.mal
@@ -1,3 +1,6 @@
+;; Mesure performances.
+
+;; Evaluate an expression, but report the time spent
 (defmacro! time
   (fn* (exp)
     `(let* (start_FIXME (time-ms)
@@ -6,6 +9,7 @@
         (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)
@@ -18,6 +22,8 @@
         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
@@ -25,3 +31,5 @@
       (run-fn-for* fn 1000 0 0)
       ;; Now do the test
       (run-fn-for* fn (* 1000 max-secs) 0 0))))
+
+nil
index 73488f8..d8b0744 100644 (file)
@@ -1,5 +1,5 @@
-(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")
 
index c525baf..c7239bb 100644 (file)
@@ -1,11 +1,8 @@
-(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)))
index 1a4fbad..ecfe164 100644 (file)
@@ -1,5 +1,5 @@
-(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")
 
index b0eef84..d078841 100644 (file)
@@ -150,8 +150,7 @@ x
 ;=>"yes"
 
 ;;
-;; Loading core.mal
-(load-file "../core.mal")
+(load-file "../lib/core.mal")
 
 ;; Testing reduce
 (reduce + 7 [])
index 8755acd..a2687ad 100644 (file)
 (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