From 4e3b6fd4cf6d36302bbc7f80c11ca3aa5ac5961b Mon Sep 17 00:00:00 2001 From: Bob Tolbert Date: Mon, 30 Dec 2013 14:42:55 -0700 Subject: [PATCH] Add some docs for gensym and siblings --- docs/language/api.rst | 71 +++++++++++++++++++++++++++ docs/language/internals.rst | 97 ++++++++++++++++++++++++++++++++++--- 2 files changed, 162 insertions(+), 6 deletions(-) diff --git a/docs/language/api.rst b/docs/language/api.rst index 9934b4d..12a7e06 100644 --- a/docs/language/api.rst +++ b/docs/language/api.rst @@ -357,6 +357,8 @@ Parameters may have following keywords in front of them: => (zig-zag-sum 1 2 3 4 5 6) -3 +.. _defmacro: + defmacro -------- @@ -378,6 +380,24 @@ between the operands. => (infix (1 + 1)) 2 +.. _defmacro/g!: + +defmacro/g! +------------ + +.. versionadded:: 0.9.12 + +`defmacro/g!` is a special version of `defmacro` that is used to +automatically generate :ref:`gensym` for any symbol that +starts with ``g!``. + +So ``g!a`` would become ``(gensym "a")``. + +.. seealso:: + + Section :ref:`using-gensym` + + del --- @@ -494,6 +514,28 @@ normally. If the execution is halted with `break`, the `else` does not execute. loop finished +.. _gensym: + +gensym +------ + +.. versionadded:: 0.9.12 + +`gensym` form is used to generate a unique symbol to allow writing macros +without accidental variable name clashes. + +.. code-block:: clj + + => (gensym) + u':G_1235' + + => (gensym "x") + u':x_1236' + +.. seealso:: + + Section :ref:`using-gensym` + get --- @@ -983,6 +1025,35 @@ values that are incremented by 1. When decorated `addition` is called with value 4 +.. _with-gensyms: + +with-gensyms +------------- + +.. versionadded:: 0.9.12 + +`with-gensym` form is used to generate a set of :ref:`gensym` for use +in a macro. + +.. code-block:: clojure + + (with-gensyms [a b c] + ...) + +expands to: + +.. code-block:: clojure + + (let [[a (gensym) + [b (gensym) + [c (gensym)]] + ...) + +.. seealso:: + + Section :ref:`using-gensym` + + yield ----- diff --git a/docs/language/internals.rst b/docs/language/internals.rst index ccf8263..cf4f99c 100644 --- a/docs/language/internals.rst +++ b/docs/language/internals.rst @@ -2,26 +2,111 @@ Internal Hy Documentation ========================= -.. info:: - These bits are for folks who hack on Hy it's self, mostly! +.. note:: + These bits are for folks who hack on Hy itself, mostly! Hy Models ========= -.. TODO:: +.. todo:: Write this. Hy Macros ========= -.. TODO:: - Write this. +.. _using-gensym: + +Using gensym for safer macros +------------------------------ + +When writing macros, one must be careful to avoid capturing external variables +or using variable names that might conflict with user code. + +We will use an example macro ``nif`` (see http://letoverlambda.com/index.cl/guest/chap3.html#sec_5 +for a more complete description.) ``nif`` is an example, something like a numeric ``if``, +where based on the expression, one of the 3 forms is called depending on if the +expression is positive, zero or negative. + +A first pass might be someting like: + +.. code-block:: clojure + + (defmacro nif [expr pos-form zero-form neg-form] + `(let [[obscure-name ~expr]] + (cond [(pos? obscure-name) ~pos-form] + [(zero? obscure-name) ~zero-form] + [(neg? obscure-name) ~neg-form]))) + +where ``obsure-name`` is an attempt to pick some variable name as not to +conflict with other code. But of course, while well-intentioned, +this is no guarantee. + +The method :ref:`gensym` is designed to generate a new, unique symbol for just +such an occasion. A much better version of ``nif`` would be: + +.. code-block:: clojure + + (defmacro nif [expr pos-form zero-form neg-form] + (let [[g (gensym)]] + `(let [[~g ~expr]] + (cond [(pos? ~g) ~pos-form] + [(zero? ~g) ~zero-form] + [(neg? ~g) ~neg-form])))) + +This is an easy case, since there is only one symbol. But if there is +a need for several gensym's there is a second macro :ref:`with-gensyms` that +basically expands to a series of ``let`` statements: + +.. code-block:: clojure + + (with-gensyms [a b c] + ...) + +expands to: + +.. code-block:: clojure + + (let [[a (gensym) + [b (gensym) + [c (gensym)]] + ...) + +so our re-written ``nif`` would look like: + +.. code-block:: clojure + + (defmacro nif [expr pos-form zero-form neg-form] + (with-gensyms [g] + `(let [[~g ~expr]] + (cond [(pos? ~g) ~pos-form] + [(zero? ~g) ~zero-form] + [(neg? ~g) ~neg-form])))) + +Finally, though we can make a new macro that does all this for us. :ref:`defmacro/g!` +will take all symbols that begin with ``g!`` and automatically call ``gensym`` with the +remainder of the symbol. So ``g!a`` would become ``(gensym "a")``. + +Our final version of ``nif``, built with ``defmacro/g!`` becomes: + +.. code-block:: clojure + + (defmacro/g! nif [expr pos-form zero-form neg-form] + `(let [[~g!res ~expr]] + (cond [(pos? ~g!res) ~pos-form] + [(zero? ~g!res) ~zero-form] + [(neg? ~g!res) ~neg-form])))) + + + +Checking macro arguments and raising exceptions +----------------------------------------------- + Hy Compiler Builtins ==================== -.. TODO:: +.. todo:: Write this.