132 | 132 |
|
133 | 133 |
(abort (inapplicable-object (abort (unbound-identifier fun))))
|
134 | 134 |
|
135 | |
You might conclude from this that [`fun`][] is not a built-in — and you'd
|
136 | |
be right! Unlike almost every other Lisp-like language, in Robin,
|
137 | |
`fun` is implemented as a macro. It can be imported from the standard
|
138 | |
library.
|
|
135 |
You might conclude from this that [`fun`][] is not built-in to the
|
|
136 |
language — and you'd be right! Unlike almost every other Lisp-like
|
|
137 |
language, in Robin, `fun` is defined in the standard library, which
|
|
138 |
must be imported before it can be used.
|
|
139 |
|
|
140 |
It's defined as a so-called "fexpr" (which is like a macro but
|
|
141 |
even more general — more on them in a minute.)
|
139 | 142 |
|
140 | 143 |
As you saw above, you can ask the Robin reference interpreter to
|
141 | 144 |
load in the standard library before it runs your program:
|
|
172 | 175 |
|
173 | 176 |
bin/robin --enable-builtins pkg/stdlib.robin fact.robin
|
174 | 177 |
|
175 | |
Macros
|
|
178 |
Fexprs
|
176 | 179 |
------
|
177 | 180 |
|
178 | 181 |
As we mentioned above, functions aren't intrinsic to Robin — the
|
179 | |
`fun` operator that creates a function is defined as a macro in the
|
180 | |
standard library.
|
|
182 |
`fun` operator that creates a function is defined as a so-called "fexpr"
|
|
183 |
in the standard library.
|
181 | 184 |
|
182 | 185 |
You're quite free to simply import the standard library and use `fun`
|
183 | |
without knowing or caring that it's defined as a macro.
|
184 | |
|
185 | |
However, you can write your own macros as well, using [`macro`][].
|
186 | |
|
187 | |
The main difference between a function and a macro is that a macro
|
|
186 |
without knowing or caring that it's defined as a fexpr, whatever that
|
|
187 |
is.
|
|
188 |
|
|
189 |
However, you can write your own fexprs as well, using [`fexpr`][].
|
|
190 |
|
|
191 |
The main difference between a fexpr and a function is that a fexpr
|
188 | 192 |
does *not* evaluate the arguments that are passed to it. It receives
|
189 | 193 |
them as an unevaluated S-expression. What it does with this unevaluated
|
190 | 194 |
S-expression is completely up to it.
|
|
193 | 197 |
This is what the [`literal`][] operator in the standard library does,
|
194 | 198 |
and this is how it's defined:
|
195 | 199 |
|
196 | |
(define literal (macro (args env)
|
|
200 |
(define literal (fexpr (args env)
|
197 | 201 |
args))
|
198 | 202 |
|
199 | 203 |
With this definition in place you can run
|
|
206 | 210 |
|
207 | 211 |
So `literal` is essentially the same as `quote` in Lisp or Scheme.
|
208 | 212 |
Except, of course, it's not intrinsic to the language. We wrote a
|
209 | |
macro to do it instead.
|
210 | |
|
211 | |
A macro defined this way also has access to the environment in which
|
|
213 |
fexpr to do it instead.
|
|
214 |
|
|
215 |
A fexpr defined this way also has access to the environment in which
|
212 | 216 |
it was called (the `env` parameter). There is also an intrinsic
|
213 | 217 |
operator called [`eval`][] which evaluates a given S-expression in a
|
214 | |
given environment. With these tools, we can write macros that *do*
|
|
218 |
given environment. With these tools, we can write fexprs that *do*
|
215 | 219 |
evaluate their arguments, just like functions.
|
216 | 220 |
|
217 | |
(define id (macro (args env) (eval env (head args))))
|
|
221 |
(define id (fexprs (args env) (eval env (head args))))
|
218 | 222 |
(display (id (subtract 80 4)))
|
219 | 223 |
|
220 | 224 |
If you run this you should see 76.
|
221 | 225 |
|
222 | |
This distinction between "functions" and "macros" is rather minor,
|
223 | |
and often we don't care to distinguish between them, so we call them
|
224 | |
all "operators".
|
225 | |
|
226 | |
### Recursive macros
|
|
226 |
When we don't care to distinguish between "functions" and "fexprs"
|
|
227 |
and "macros" we just call them all "operators".
|
|
228 |
|
|
229 |
### Recursive fexprs
|
227 | 230 |
|
228 | 231 |
(To be written).
|
|
232 |
|
|
233 |
Macros
|
|
234 |
------
|
|
235 |
|
|
236 |
Just like a function, in Robin, is a kind of fexpr, so too is a
|
|
237 |
macro a kind of fexpr. A macro is a fexpr whose return value is
|
|
238 |
expected to be a piece of syntax which will be further evaluated
|
|
239 |
as a Robin expression.
|
|
240 |
|
|
241 |
(To be continued).
|
229 | 242 |
|
230 | 243 |
Referential transparency
|
231 | 244 |
------------------------
|
|
346 | 359 |
Here is an example reactor.
|
347 | 360 |
|
348 | 361 |
(reactor (line-terminal) 0
|
349 | |
(macro (args env)
|
|
362 |
(fexpr (args env)
|
350 | 363 |
(bind-vals ((event-type event-payload) state)
|
351 | 364 |
(if (equal? event-type (literal init))
|
352 | 365 |
(list state
|
|
362 | 375 |
that the state does not change and does not make any difference to the
|
363 | 376 |
program, but an initial state still must be given.
|
364 | 377 |
|
365 | |
After that is the transducer, given as a `macro`. It receives, as its
|
|
378 |
After that is the transducer, given as a `fexpr`. It receives, as its
|
366 | 379 |
arguments, an event, which consists of an event type and an event payload,
|
367 | 380 |
and the current state. It extracts these.
|
368 | 381 |
|
|
385 | 398 |
[`head`]: ../stdlib/head.robin
|
386 | 399 |
[`if`]: ../stdlib/if.robin
|
387 | 400 |
[`list?`]: ../stdlib/list-p.robin
|
388 | |
[`macro`]: ../stdlib/macro.robin
|
|
401 |
[`fexpr`]: ../stdlib/fexpr.robin
|
389 | 402 |
[`number?`]: ../stdlib/number-p.robin
|
390 | 403 |
[`operator?`]: ../stdlib/operator-p.robin
|
391 | 404 |
[`prepend`]: ../stdlib/prepend.robin
|