From c489245ffd11042d0b2ab23f79664c8701e6d6f9 Mon Sep 17 00:00:00 2001 From: Juan Mauricio Matera Date: Thu, 12 Feb 2026 08:55:25 -0300 Subject: [PATCH 1/4] handle RuleDelayed in Map over Association expressions. Adding a comment --- .../builtin/functional/apply_fns_to_lists.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/mathics/builtin/functional/apply_fns_to_lists.py b/mathics/builtin/functional/apply_fns_to_lists.py index fc528c6e6..67e15f71e 100644 --- a/mathics/builtin/functional/apply_fns_to_lists.py +++ b/mathics/builtin/functional/apply_fns_to_lists.py @@ -179,9 +179,22 @@ def callback(level): Map $f$ onto each element (denoted by 'level' here) at this level. With exception for expr as Association, which is mapped on values only. """ - if is_association and level.has_form("Rule", 2): + # TODO: This special behavior applies when the whole expression + # is of the form Association[__(Rule|RuleDelayed)], i.e., when + # the expression is a well-formatted Association expression. + # For example, + # `Map[F, Association[a->1,b->2, NotARule]` + # produces in WMA + # `Association[F[a->1], F[b->2], F[NotARule]` + # instead of + # `Association[a->F[1], b->F[2], F[NotARule]`] + # + # Fixing this would require a different implementation of this eval_ method. + # + + if is_association and level.has_form(("Rule", "RuleDeyaled"), 2): return Expression( - SymbolRule, + level.get_head(), level.elements[0], Expression(f, level.elements[1]), ) From 6c07659284cb4f11825dc351d999446799b72cbe Mon Sep 17 00:00:00 2001 From: Juan Mauricio Matera Date: Thu, 12 Feb 2026 12:43:17 -0300 Subject: [PATCH 2/4] fix typo. Add tests --- .../builtin/functional/apply_fns_to_lists.py | 3 +- test/builtin/list/test_association.py | 72 +++++++++++++++++++ 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/mathics/builtin/functional/apply_fns_to_lists.py b/mathics/builtin/functional/apply_fns_to_lists.py index 67e15f71e..f5c7686d0 100644 --- a/mathics/builtin/functional/apply_fns_to_lists.py +++ b/mathics/builtin/functional/apply_fns_to_lists.py @@ -191,8 +191,7 @@ def callback(level): # # Fixing this would require a different implementation of this eval_ method. # - - if is_association and level.has_form(("Rule", "RuleDeyaled"), 2): + if is_association and level.has_form(("Rule", "RuleDelayed"), 2): return Expression( level.get_head(), level.elements[0], diff --git a/test/builtin/list/test_association.py b/test/builtin/list/test_association.py index 999b2b850..88d43401c 100644 --- a/test/builtin/list/test_association.py +++ b/test/builtin/list/test_association.py @@ -193,3 +193,75 @@ def test_associations_private_doctests( failure_message=assert_message, expected_messages=expected_messages, ) + + +@pytest.mark.parametrize( + ("str_expr", "expected_messages", "str_expected", "assert_message"), + [ + ( + "Map[F, Q[a->1, b:>Association[p->3,q->4]]]", + None, + "Q[F[a->1], F[b:>Association[p->3, q->4]]]", + "Acting on a nested association, the inner association is treated as normal.", + ), + ( + "Map[F, Q[a->1, b:>Association[p->3,q->4]],{2}]", + None, + "Q[F[a]->F[1], F[b]:>F[Association[p->3, q->4]]]", + "Acting on a nested association, the inner association is treated as normal.", + ), + ( + "Map[F, Association[a->1, b:>Association[p->3,q->4]], {0}]", + None, + "F[Association[a->1, b:>Association[p->3, q->4]]]", + "Special behavior happends at the first level.", + ), + ( + "Map[F, Association[a->1, b:>2]]", + None, + "Association[a->F[1], b:>F[2]]", + "Over associations, Map acts on the values", + ), + ( + "Map[F, Association[a->1, b:>Association[p->3,q->4]]]", + None, + "Association[a->F[1], b:>F[Association[p->3, q->4]]]", + "Acting on a nested association, the inner association is treated as normal.", + ), + ( + "Map[F, Association[a->1, b:>Association[p->3,q->4]], {1}]", + None, + "Association[a->F[1], b:>F[Association[p->3, q->4]]]", + "Special behavior happends at the first level.", + ), + # FIXME + ( + "Map[F, Association[a->1,b:>2,q]]", + None, + "Association[F[a->1], F[b:>2], F[q]]", + "Acting on an invalid association expression, works as in a normal expression.", + ), + ( + "Map[F, Association[a->1, b:>Association[p->3,q->4]], {2}]", + None, + "Association[a->1, b:>Association[F[p->3],F[q->4]]]", + "Special behavior happends at the first level.", + ), + ( + "Map[F, Association[a->1, b:>Q[p->3, q->4]], {2}]", + None, + "Association[a->1, b:>Q[F[p->3],F[q->4]]]", + "Special behavior happends at the first level.", + ), + ], +) +@pytest.mark.xfail +def test_map_over_associations( + str_expr, expected_messages, str_expected, assert_message +): + check_evaluation( + str_expr, + str_expected, + failure_message=assert_message, + expected_messages=expected_messages, + ) From 0741f67149091d93399e64e447efe40607f47643 Mon Sep 17 00:00:00 2001 From: Juan Mauricio Matera Date: Thu, 12 Feb 2026 12:45:37 -0300 Subject: [PATCH 3/4] merge --- mathics/eval/functional/apply_fns_to_lists.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mathics/eval/functional/apply_fns_to_lists.py b/mathics/eval/functional/apply_fns_to_lists.py index ba1decec2..67493153c 100644 --- a/mathics/eval/functional/apply_fns_to_lists.py +++ b/mathics/eval/functional/apply_fns_to_lists.py @@ -38,9 +38,9 @@ def map_at_replace_one(elements: Iterable, index: ListExpression, i: int) -> lis raise PartRangeError new_elements = list(elements) replace_element = elements[j] - if hasattr(replace_element, "head") and replace_element.head is SymbolRule: + if replace_element.has_form(("Rule", "RuleDelayed"), 2): new_elements[j] = Expression( - SymbolRule, + replace_element.get_head(), replace_element.elements[0], Expression(f, replace_element.elements[1]), ) From 0d3b9962d5c3ac6009bbf4c3b4ecfcf316757b8a Mon Sep 17 00:00:00 2001 From: Juan Mauricio Matera Date: Thu, 12 Feb 2026 13:29:55 -0300 Subject: [PATCH 4/4] fix typos --- test/builtin/list/test_association.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/builtin/list/test_association.py b/test/builtin/list/test_association.py index 88d43401c..e0f2a6720 100644 --- a/test/builtin/list/test_association.py +++ b/test/builtin/list/test_association.py @@ -214,7 +214,7 @@ def test_associations_private_doctests( "Map[F, Association[a->1, b:>Association[p->3,q->4]], {0}]", None, "F[Association[a->1, b:>Association[p->3, q->4]]]", - "Special behavior happends at the first level.", + "Special behavior occurs at the first level.", ), ( "Map[F, Association[a->1, b:>2]]", @@ -232,7 +232,7 @@ def test_associations_private_doctests( "Map[F, Association[a->1, b:>Association[p->3,q->4]], {1}]", None, "Association[a->F[1], b:>F[Association[p->3, q->4]]]", - "Special behavior happends at the first level.", + "Special behavior occurs at the first level.", ), # FIXME ( @@ -245,13 +245,13 @@ def test_associations_private_doctests( "Map[F, Association[a->1, b:>Association[p->3,q->4]], {2}]", None, "Association[a->1, b:>Association[F[p->3],F[q->4]]]", - "Special behavior happends at the first level.", + "Special behavior occurs at the first level.", ), ( "Map[F, Association[a->1, b:>Q[p->3, q->4]], {2}]", None, "Association[a->1, b:>Q[F[p->3],F[q->4]]]", - "Special behavior happends at the first level.", + "Special behavior occurs at the first level.", ), ], )