From 0544b52f1aa5cec4827ee7bd8a24efb336c7e5e0 Mon Sep 17 00:00:00 2001 From: Nicolas Boulenguez Date: Tue, 7 May 2019 22:59:57 +0200 Subject: [PATCH] Draft exercise suggesting native implementations of some built-in functions. --- docs/exercise.md | 49 ++++++++++++++++++++++++++++++++++++++++++ examples/exercises.mal | 48 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 docs/exercise.md create mode 100644 examples/exercises.mal diff --git a/docs/exercise.md b/docs/exercise.md new file mode 100644 index 00000000..6e811de2 --- /dev/null +++ b/docs/exercise.md @@ -0,0 +1,49 @@ +Once you have a working implementation, you may want to implement +parts of the process inside the MAL language itself. This has no other +purpose than learning the MAL language. Once it exists, a built-in +implementation will always be more efficient than a native +implementation. Also, the functions described in MAL process are +selected for educative purposes, so portability accross +implementations does not matter much. + +You may easily check your answers by passing them directly to the +interpreter. They will hide the built-in functions carrying the same +names, and the usual tests (with REGRESS=1) will check them. The +`runtest.py` script provide a convenient command-line parameter to +pass a command like 'load-file' before running the testsuite. + +Some solutions are given in the `examples` directory. Feel free to +submit new solutions, or new exercises. + + +- Implement the following functions with other built-in functions. + - `nil?`, `true?` and `false?` + - `empty?` + - `sequential?` + +- Implement `>`, `<=` and `>=` with `<`. + +- Implement the following non-recursive functions. + - `hash-map` + - `list` + - `prn` + - `swap!` + +- Implement `map` with a recursion. + +- Implement the `do` special as a non-recursive function. The special + form will hide your implementation, so in order to test it, you will + need to give it another name and adapt the test accordingly. + +- Implement `let*` as a macro that uses `fn*` and recursionn. The same + remark applies. + +- Implement `apply` as a macro. + +- Implement maps using lists. + FIXME: Is dissoc use anywhere? It makes this implememtation and the + process more complex. + +- Implement quoting within MAL. + +- Implement macros within MAL. diff --git a/examples/exercises.mal b/examples/exercises.mal new file mode 100644 index 00000000..6a6606fe --- /dev/null +++ b/examples/exercises.mal @@ -0,0 +1,48 @@ +;; These are the answers to the questions in ../docs/exercise.md. + +(def! nil? (fn* [x] (= x nil ))) +(def! true? (fn* [x] (= x true ))) +(def! false? (fn* [x] (= x false))) + +(def! empty? (fn* [xs] (= 0 (count xs)))) + +(def! sequential? (fn* [x] (if (list? x) true (if (vector? x) true false)))) + +(def! > (fn* [a b] (< b a) )) +(def! <= (fn* [a b] (if (< b a) false true))) +(def! >= (fn* [a b] (if (< a b) false true))) + +(def! hash-map (fn* [& xs] (apply assoc {} xs))) +(def! list (fn* [& xs] xs)) +(def! prn (fn* [& xs] (println (apply pr-str xs)))) +(def! swap! (fn* [a f & xs] (reset! a (apply f (deref a) xs)))) + +(def! map + (fn* [f xs] + (if (empty? xs) + () + (cons (f (first xs)) (map f (rest xs)))))) + +(def! do2 (fn* [& xs] (nth xs (- (count xs) 1)))) + +(defmacro! let2 + ;; Must be a macro because the first argument must not be evaluated. + (fn* [binds form] + (if (empty? binds) + form + ;; This let* increases the readability, but the values could + ;; easily be replaced below. + (let* [key (nth binds 0) + val (nth binds 1) + more (rest (rest binds))] + `((fn* [~key] (let2 ~more ~form)) ~val))))) + +(defmacro! apply + (fn* [& xs] + (;; Rewrite (f a b [c d]) to (f a b c d). + (def! rec + (fn* [lst] + (if (= 1 (count lst)) + (first lst) ; last argument must be a sequence + (cons (first lst) (rec (rest lst)))))) + xs)))