Skip to content
This repository has been archived by the owner on Sep 9, 2020. It is now read-only.

Commit

Permalink
feat: Changes to macros.
Browse files Browse the repository at this point in the history
- Macro-expansions don't evaluate in a dedicated scope.
- Add `let` macro.
  • Loading branch information
jameshaydon committed Jul 11, 2019
1 parent 3514987 commit 887dca2
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 2 deletions.
2 changes: 2 additions & 0 deletions rad/prelude.rad
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
;; required for the extended `match` to work in the modules loaded
;; after.
(import prelude/patterns :unqualified)
(file-module! "prelude/macros.rad")
(file-module! "prelude/bool.rad")
(file-module! "prelude/seq.rad")
(file-module! "prelude/list.rad")
Expand Down Expand Up @@ -35,6 +36,7 @@
(file-module! "prelude/track-newness.rad")

(import prelude/basic :unqualified)
(import prelude/macros :unqualified)
(import prelude/bool :unqualified)
(import prelude/seq :unqualified)
(import prelude/strings :as 'string)
Expand Down
4 changes: 4 additions & 0 deletions rad/prelude/basic.rad
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
(fn [c tt ff]
(list 'cond c tt #t ff))))

(test "if"
[ (if #t :a :b) ==> :a ]
[ (if #f :a :b) ==> :b ])

(def head
"Backwards compatible alias for `first`."
first)
Expand Down
76 changes: 76 additions & 0 deletions rad/prelude/macros.rad
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
{:module 'prelude/macros
:doc "Basic macros."
:exports '[defn let]}

(import prelude/basic :unqualified)
(import prelude/patterns :unqualified)

(def lamb
(fn [args body]
(cons 'fn (cons args body))))

(def expand-defn
(fn [name args docstring? body]
(match docstring?
:nothing (list 'def name (lamb args body))
[:just 'ds] (list 'def name ds (lamb args body)))))

(def defn
"A macro for defining functions. Takes a symbol, function argument(s), an optional doc-string, and the body for the function.

Example usages:
`(def f [x] (+ x 1))`
`(def f xs (print! (reverse xs)))`
`(def f [x] \"Increment function.\" (+ x 1))`
`(def f xs \"Reverse and print.\" (print! (reverse xs)))`"
(macro
(fn args
(match (vec-to-list args)
(/cons (/as 'name (/? atom?)) 'rest)
(match rest
(/cons 'args (/cons (/as 'docstring (/? string?)) 'body)) (expand-defn name args [:just docstring] body)
(/cons 'args 'body) (expand-defn name args :nothing body))
_ (throw 'macro-error "The `defn` macro requires at least the function name (a symbol) and some arguments.")))))

(test "defn"
[:setup
(defn foo [x]
"foo does doo"
(+ x 1))
(defn bar [x] (+ x 2))]
[ (foo 1) ==> 2 ]
[ (bar 1) ==> 3 ])

(def-rec expand-let
(fn [bindings body]
(match bindings
(/cons (/as 'x (/? atom?)) (/cons 'y 'rest-bindings))
(list (list (lamb [x] (expand-let rest-bindings body)) y))
(/cons _ /nil) (throw 'macro-error "The bindings vector in a `let` must have an even number nof forms.")
_ body)))

(def let
"A macro for defining local variables.

Used as `(let [x1 e1 x2 e2 ... xn en] body..)` where the `xi` are symbols and
the `ei` are expressions. Each pair `xi ei` establishes a binding of the
symbol `xi` to the result of evaluating `ei`. The binding has effect on all
the expressions that follow it, and the body (unless shadowed by another
binding/def).

Evaluation of the body takes place with the bindings in place, and its value
becomes the value of the whole let-expression."
(macro
(fn args
(match (vec-to-list args)
(/cons 'bindings (/as 'body (/cons _ _))) (first (expand-let bindings body))
_ (throw 'macro-error "The `let` macro needs a bindings vector and a body.")))))

(test "let"
[ (let [] 42) ==> 42 ]
[ (let [x (+ 42 1)] (+ x 1)) ==> 44 ]
[ (let [x 42 y (+ x 1)] [x y]) ==> [42 43] ]
[ (let [x 0] x x) ==> 0 ])

(test "multiple-macro-expansion"
[ (let [x (if #t :a :b) y (if #f :f x)] (if 42 [x y] :boo)) ==> [:a :a]])
3 changes: 1 addition & 2 deletions src/Radicle/Internal/Eval.hs
Original file line number Diff line number Diff line change
Expand Up @@ -248,8 +248,7 @@ f $$ vs = case f of
case f' of
Macro g -> do
e <- callFn g vs
-- The expansion of a macro is evaluated in its own scope:
withEnv identity $ baseEval e
baseEval e
_ -> do
vs' <- traverse baseEval vs
callFn f' vs'

0 comments on commit 887dca2

Please sign in to comment.