Skip to content

Commit

Permalink
Merge pull request #103 from Kodiologist/map-less-rec
Browse files Browse the repository at this point in the history
Avoid extra calls to `hy.as-model` in `map-model`
  • Loading branch information
Kodiologist authored Oct 13, 2024
2 parents 0b0da75 + 1af094b commit c29fc47
Show file tree
Hide file tree
Showing 4 changed files with 23 additions and 5 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python: [3.8, 3.9, '3.10', 3.11, 3.12, 3.13-dev, pypy-3.10]
python: [3.9, '3.10', 3.11, 3.12, 3.13, pypy-3.10]

name: ${{ matrix.python }}
runs-on: ubuntu-latest
Expand Down
8 changes: 8 additions & 0 deletions NEWS.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
.. default-role:: code

Unreleased
======================================================

Bug Fixes
------------------------------
* `map-model` now calls `as-model` only once (before its own recursion),
and it does so unconditionally.

0.7.0 (released 2024-09-22; uses Hy ≥ 1)
======================================================

Expand Down
10 changes: 6 additions & 4 deletions hyrule/macrotools.hy
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@


(defn map-model [x f]
#[[Recursively apply a callback to some code. The unary function ``f`` is called on the object ``x``, converting it to a :ref:`model <hy:models>` first if it isn't one already. If the return value isn't ``None``, it's converted to a model and used as the result. But if the return value is ``None``, and ``x`` isn't a :ref:`sequential model <hy:hysequence>`, then ``x`` is used as the result instead. ::
#[[Recursively apply a callback to some code. The unary function ``f`` is called on the object ``x``, calling :hy:func:`hy.as-model` first. If the return value isn't ``None``, it's converted to a model and used as the result. But if the return value is ``None``, and ``x`` isn't a :ref:`sequential model <hy:hysequence>`, then ``x`` is used as the result instead. ::
(defn f [x]
(when (= x 'b)
Expand All @@ -259,21 +259,23 @@
(hy.I.hyrule.map-model `(do ~@body) (fn [x]
(when (isinstance x hy.models.Symbol)
(hy.models.Symbol (.lower (str x)))))))
(lowercase-syms
(SETV FOO 15)
(+= FOO (ABS -5)))
(print foo) ; => 20
That's why the parameters of ``map-model`` are backwards compared to ``map``: in user code, ``x`` is typically a symbol or other simple form whereas ``f`` is a multi-line anonymous function.]]

(when (not (isinstance x hy.models.Object))
(setv x (hy.as-model x)))
(_map-model (hy.as-model x) f))

(defn _map-model [x f]
(cond
(is-not (setx value (f x)) None)
(hy.as-model value)
(isinstance x hy.models.Sequence)
((type x)
(gfor elem x (map-model elem f))
(gfor elem x (_map-model elem f))
#** (cond
(isinstance x hy.models.FString)
{"brackets" x.brackets}
Expand Down
8 changes: 8 additions & 0 deletions tests/test_macrotools.hy
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,14 @@
(= x "hello") "wrong"
(= x '"hello") "right")))
'["right"]))
; Even if the outermost layer of the input is already a model.
(setv seen [])
(map-model
(hy.models.List ["hello"])
(fn [x]
(.append seen (= x '["hello"]))
1))
(assert (= seen [True]))

; String and byte models aren't recursed into. (They're iterable,
; but not sequential models.)
Expand Down

0 comments on commit c29fc47

Please sign in to comment.