diff --git a/doc/.vuepress/config.js b/doc/.vuepress/config.js index 1d137bed8..c3efeea4b 100644 --- a/doc/.vuepress/config.js +++ b/doc/.vuepress/config.js @@ -16,7 +16,7 @@ module.exports = { '/guide/': [ { title: 'Guide', - children: ['', 'intro', 'getting-started', 'ffi', 'emacs', 'shell', 'env-vars', 'package-manager', 'docker', 'macos', 'nix', 'r7rs', 'hacking-on-the-stdlib'] + children: ['', 'intro', 'getting-started', 'eval', 'ffi', 'emacs', 'shell', 'env-vars', 'package-manager', 'docker', 'macos', 'nix', 'r7rs', 'hacking-on-the-stdlib'] } ], '/tutorials/': [ diff --git a/doc/guide/eval.md b/doc/guide/eval.md new file mode 100644 index 000000000..0d8e10342 --- /dev/null +++ b/doc/guide/eval.md @@ -0,0 +1,93 @@ +# Eval + +## Overview + +The `eval` function in Gerbil Scheme is a powerful tool for dynamic code execution. However, its behavior and usage differ between runtime and compile-time, and it has some key differences compared to Gambit Scheme. This document outlines how to use `eval` effectively in various scenarios. + +## Runtime Behavior + +### Interactive Environment + +In the Gerbil interpreter (`gxi`), `eval` works similarly to other Scheme implementations: + +```scheme +> (def greeting "hello world") +> (eval '(displayln greeting)) +hello world +``` + +### Compiled Binaries + +When using `eval` in compiled binaries, additional steps are necessary. +To demonstrate this let's slightly modify code from previous example, save it into the file: + +> `eval-example.ss` + +```scheme +(export main) +(def greeting "hello world") +(def (main . args) + (eval '(displayln greeting))) +``` + +Then compile into executable and run: + +```console +$ gxc -exe eval-example.ss +... +$ ./eval-example +*** ERROR -- Unbound variable: greeting +--- continuation backtrace: +[0] ##primordial-exception-handler-hook +``` + +An error is reported because eval operates on top-level namespace while `greeting` is defined a module namespace which is added implicitly. + +This may be fixed by adding a namespace to `greeting`: + +```scheme +(export main) +(def greeting "hello world") +(def (main . args) + (eval '(displayln eval-example#greeting)) +``` + +```console +$ gxc -exe eval-example.ss +... +$ ./eval-example +hello world +``` + +Adding namespace to each symbol referenced under `eval` may be a bit tedious, there is a better way to do this with `(extern ...)` form. +Eval expression may be modified to look like this: + +```scheme +(begin + (extern namespace: eval-example + greeting) + (displayln greeting)) +``` + +Using `extern` will make `eval-example#greeting` available as `greeting` at runtime. But there is one more thing to do before it will work without errors, because `extern` relies on `:gerbil/expander` which should be loaded. In interactive mode this happens implicitly, while inside compiled binaries expander need to be loaded manually. Here is the final version: + +```scheme +(import :gerbil/expander) +(export main) +(def greeting "hello world") +(def (main . args) + (gerbil-load-expander!) + (eval '(begin + (extern namespace: eval-example + greeting) + (displayln greeting)))) +``` + +Which works as expected: + +```console +$ gxc -exe eval-example.ss +... +$ ./eval-example +hello world +```