diff --git a/mathics/builtin/functional/apply_fns_to_lists.py b/mathics/builtin/functional/apply_fns_to_lists.py index fc528c6e6..f5c7686d0 100644 --- a/mathics/builtin/functional/apply_fns_to_lists.py +++ b/mathics/builtin/functional/apply_fns_to_lists.py @@ -179,9 +179,21 @@ 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", "RuleDelayed"), 2): return Expression( - SymbolRule, + level.get_head(), level.elements[0], Expression(f, level.elements[1]), ) 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]), ) diff --git a/test/builtin/list/test_association.py b/test/builtin/list/test_association.py index 999b2b850..e0f2a6720 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 occurs 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 occurs 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 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 occurs 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, + )