diff --git a/NEWS.rst b/NEWS.rst index 348b48aa..a21863ed 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -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) ====================================================== diff --git a/hyrule/macrotools.hy b/hyrule/macrotools.hy index 227b98f0..b8416913 100644 --- a/hyrule/macrotools.hy +++ b/hyrule/macrotools.hy @@ -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 ` 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 `, 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 `, then ``x`` is used as the result instead. :: (defn f [x] (when (= x 'b) @@ -266,14 +266,15 @@ 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} diff --git a/tests/test_macrotools.hy b/tests/test_macrotools.hy index 30afbba1..abcb06f4 100644 --- a/tests/test_macrotools.hy +++ b/tests/test_macrotools.hy @@ -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.)