Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Documentation improvements #2502

Merged
merged 4 commits into from
Aug 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 46 additions & 3 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,24 @@ base names, such that ``hy.core.macros.foo`` can be called as just ``foo``.

.. hy:macro:: (eval-and-compile [#* body])

``eval-and-compile`` takes any number of forms as arguments. The input forms are evaluated as soon as the ``eval-and-compile`` form is compiled, instead of being deferred until run-time. The input forms are also left in the program so they can be executed at run-time as usual. So, if you compile and immediately execute a program (as calling ``hy foo.hy`` does when ``foo.hy`` doesn't have an up-to-date byte-compiled version), ``eval-and-compile`` forms will be evaluated twice. The return value is the final argument, as in ``do``.
``eval-and-compile`` takes any number of forms as arguments. The input forms are evaluated as soon as the ``eval-and-compile`` form is compiled, then left in the program so they can be executed at run-time as usual; contrast with :hy:func:`eval-when-compile`. So, if you compile and immediately execute a program (as calling ``hy foo.hy`` does when ``foo.hy`` doesn't have an up-to-date byte-compiled version), ``eval-and-compile`` forms will be evaluated twice. For example, the following program ::

(eval-when-compile
(print "Compiling"))
(print "Running")
(eval-and-compile
(print "Hi"))

prints

.. code-block:: text

Compiling
Hi
Running
Hi

The return value of ``eval-and-compile`` is its final argument, as for :hy:func:`do`.

One possible use of ``eval-and-compile`` is to make a function available both at compile-time (so a macro can call it while expanding) and run-time (so it can be called like any other function)::

Expand All @@ -469,9 +486,35 @@ base names, such that ``hy.core.macros.foo`` can be called as just ``foo``.

Had the ``defn`` not been wrapped in ``eval-and-compile``, ``m`` wouldn't be able to call ``add``, because when the compiler was expanding ``(m 3)``, ``add`` wouldn't exist yet.

While ``eval-and-compile`` executes the same code at both compile-time and run-time, bear in mind that the same code can have different meanings in the two contexts. Consider, for example, issues of scoping::

(eval-when-compile
(print "Compiling"))
(print "Running")
(eval-and-compile
(setv x 1))
(defn f []
(setv x 2)
(eval-and-compile
(setv x 3))
(print "local x =" x))
(f)
(eval-and-compile
(print "global x =" x))

The form ``(setv x 3)`` above refers to the global ``x`` at compile-time, but the local ``x`` at run-time, so the result is:

.. code-block:: text

Compiling
global x = 3
Running
local x = 3
global x = 1

.. hy:macro:: (eval-when-compile [#* body])

As ``eval-and-compile``, but the code isn't executed at run-time, and ``None`` is returned. Hence, ``eval-when-compile`` doesn't directly contribute any code to the final program, although it can still change Hy's state while compiling (e.g., by defining a function). ::
``eval-when-compile`` executes the given forms at compile-time, but discards them at run-time and simply returns :data:`None` instead; contrast :hy:func:`eval-and-compile`. Hence, while ``eval-when-compile`` doesn't directly contribute code to the final program, it can change Hy's state while compiling, as by defining a function::

(eval-when-compile
(defn add [x y]
Expand Down Expand Up @@ -796,7 +839,7 @@ base names, such that ``hy.core.macros.foo`` can be called as just ``foo``.
.. hy:macro:: (unquote [model])
.. hy:macro:: (unquote-splice [model])

``quasiquote`` is like :hy:func:`quote` except that it treats the model as a template, in which certain special :ref:`expressions <expressions>` indicate that some code should evaluated and its value substituted there. The idea is similar to C's ``sprintf`` or Python's various string-formatting constructs. For example::
``quasiquote`` is like :hy:func:`quote` except that it treats the model as a template, in which certain special :ref:`expressions <expressions>` indicate that some code should be evaluated and its value substituted there. The idea is similar to C's ``sprintf`` or Python's various string-formatting constructs. For example::

(setv x 2)
(quasiquote (+ 1 (unquote x))) ; => '(+ 1 2)
Expand Down
67 changes: 28 additions & 39 deletions hy/core/hy_repr.hy
Original file line number Diff line number Diff line change
Expand Up @@ -8,39 +8,26 @@

(setv _registry {})
(defn hy-repr-register [types f [placeholder None]]
"``hy.repr-register`` lets you set the function that ``hy.repr`` calls to
represent a type.

Examples:
::

=> (hy.repr-register the-type fun)

=> (defclass C)
=> (hy.repr-register C (fn [x] \"cuddles\"))
=> (hy.repr [1 (C) 2])
\"[1 cuddles 2]\"

If the type of an object passed to ``hy.repr`` doesn't have a registered
function, ``hy.repr`` falls back on ``repr``.

Registered functions often call ``hy.repr`` themselves. ``hy.repr`` will
automatically detect self-references, even deeply nested ones, and
output ``\"...\"`` for them instead of calling the usual registered
function. To use a placeholder other than ``\"...\"``, pass a string of
your choice to the keyword argument ``:placeholder`` of
``hy.repr-register``.

=> (defclass Container [object]
... (defn __init__ (fn [self value]
... (setv self.value value))))
=> (hy.repr-register Container :placeholder \"HY THERE\" (fn [x]
... (+ \"(Container \" (hy.repr x.value) \")\")))
=> (setv container (Container 5))
=> (setv container.value container)
=> (print (hy.repr container))
'(Container HY THERE)'
"
#[[``hy.repr-register`` lets you set the function that :hy:func:`hy.repr` calls to
represent a type::

(defclass C)
(hy.repr-register C (fn [x] "cuddles"))
(hy.repr [1 (C) 2]) ; => "[1 cuddles 2]"

Registered functions often call ``hy.repr`` themselves. ``hy.repr`` will
automatically detect self-references, even deeply nested ones, and
output ``"..."`` for them instead of calling the usual registered
function. To use a placeholder other than ``"..."``, pass a string of
your choice as the ``placeholder`` argument::

(defclass Container)
(hy.repr-register Container :placeholder "HY THERE"
(fn [x] f"(Container {(hy.repr x.value)})"))
(setv container (Container))
(setv container.value container)
(hy.repr container) ; => "(Container HY THERE)"]]

(for [typ (if (isinstance types list) types [types])]
(setv (get _registry typ) #(f placeholder))))

Expand All @@ -50,16 +37,14 @@
#[[This function is Hy's equivalent of Python's :func:`repr`.
It returns a string representing the input object in Hy syntax. ::

=> (hy.repr [1 2 3])
"[1 2 3]"
=> (repr [1 2 3])
"[1, 2, 3]"
(hy.repr [1 2 3]) ; => "[1 2 3]"
(repr [1 2 3]) ; => "[1, 2, 3]"

Like ``repr`` in Python, ``hy.repr`` can round-trip many kinds of
values. Round-tripping implies that given an object ``x``,
``(hy.eval (hy.read (hy.repr x)))`` returns ``x``, or at least a
value that's equal to ``x``. A notable exception to round-tripping
is that if a :class:`hy.models.Object` contains a non-model, the
is that if a model contains a non-model, the
latter will be promoted to a model in the output::

(setv
Expand All @@ -68,7 +53,11 @@
y (hy.eval (hy.read output)))
(print output) ; '[5]
(print (type (get x 0))) ; <class 'int'>
(print (type (get y 0))) ; <class 'hy.models.Integer'>]]
(print (type (get y 0))) ; <class 'hy.models.Integer'>

When ``hy.repr`` doesn't know how to represent an object, it falls
back on :func:`repr`. Use :hy:func:`hy.repr-register` to add your
own conversion function for a type instead.]]

(setv [f placeholder] (.get _registry (type obj) [_base-repr None]))

Expand Down
27 changes: 7 additions & 20 deletions hy/reader/mangling.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,32 +71,19 @@ def mangle(s):

def unmangle(s):
"""Stringify the argument and try to convert it to a pretty unmangled
form. See :ref:`Hy's mangling rules <mangling>`.
form. See :ref:`Hy's mangling rules <mangling>`. ::

(hy.unmangle "hyx_XsquidX") ; => "🦑"

Unmangling may not round-trip, because different Hy symbol names can mangle
to the same Python identifier. In particular, Python itself already
considers distinct strings that have the same normalized form (according to
NFKC), such as ``hello`` and ``𝔥𝔢𝔩𝔩𝔬``, to be the same identifier.

Examples:
::

=> (hy.unmangle 'foo_bar)
"foo-bar"

=> (hy.unmangle 'hyx_XasteriskX)
"*"

=> (hy.unmangle 'hyx_XhyphenHminusX_XgreaterHthan_signX)
"-->"

=> (hy.unmangle 'hyx_XlessHthan_signX__)
"<--"

=> (hy.unmangle '__dunder_name__)
"__dunder-name__"

"""
It's an error to call ``hy.unmangle`` on something that looks like a
properly mangled name but isn't. For example, ``(hy.unmangle
"hyx_XpizzazzX")`` is erroneous, because there is no Unicode character
named "PIZZAZZ" (yet)."""

s = str(s)

Expand Down