1
1
mirror of https://github.com/kanaka/mal.git synced 2024-08-16 17:20:23 +03:00

Draft exercise suggesting native implementations of some built-in functions.

This commit is contained in:
Nicolas Boulenguez 2019-05-07 22:59:57 +02:00
parent a003046c8d
commit 0544b52f1a
2 changed files with 97 additions and 0 deletions

49
docs/exercise.md Normal file
View File

@ -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.

48
examples/exercises.mal Normal file
View File

@ -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)))