evhub / coconut

Simple, elegant, Pythonic functional programming.
http://coconut-lang.org
Apache License 2.0
4.09k stars 125 forks source link

Miscompilation of `in` operator in pattern matching code #725

Closed yggdr closed 1 year ago

yggdr commented 1 year ago

It seems the in operator compiles to the wrong order of its parameters in pattern matching. I have code like this

a={'Test'}
addpattern def f({'t': t `(. in a)`}):
 print t

or alternatively

a={'Test'}
addpattern def f({'t': t `(in)` a}):
 print t

In both cases I'd expect the call f({'t': 'Test'}) to pass the pattern matching and reach the print statement. Instead, TypeError: 'in <string>' requires string as left operand, not set is raised.

It seems that the (. in a) gets compiled with the arguments switched around to:

>>> import coconut.convenience as c
>>> "`(. in a)`" |> c.parse$(mode="block") |> print
(_coconut_partial(_coconut.operator.contains, {1: a}, 2, ())))()
>>> ((\_coconut_partial(\_coconut.operator.contains, {1: a}, 2, ())))
<built-in function contains>$(?, {'Test'})

In the last line you can see the problem: the contains function takes the container as the first arguments, and the test subject as the second, so it needs its parameters switched compared to other operator functions. Interestingly. the not in variant doesn't suffer this problem as the _coconut_not_in function it gets compiled to takes the container as its second argument.

Coconut: Version 3.0.0-a_dev19 running on Python 3.11.2 and Cython cPyparsing v2.4.7.1.2.0

evhub commented 1 year ago

Thanks for the bug report! Fixed on coconut-develop>=3.0.0-a_dev20.

Temporary workaround:

operator in_
(in_) = flip(in)
assert (1 in_ .)([1])