egel-lang/egel

`[ (X Y) -> X ] (T b)` should reduce to `T` for any term `T`.

Closed this issue · 3 comments

Context: [ (X Y) -> Y ] (a T) reduces to T for any term T. This is expected behavior.

Expected behavior: Similarly, [ (X Y) -> X ] (T b) should reduce to T for any term T.

Actual behavior: [ (X Y) -> X ] ((a b) c) does not reduce at all, because ((a b) c) expands to (a b c) and we can't match against a 3-application term with a 2-application pattern.

We can write the pattern [ ((X Y) Z -> (X Y) ], but as far as I know, it isn't possible to match against an arbitrary n-application. In my opinion, the current behavior is unexpected and unhelpful.

Use case: I'm trying to implement delimited continuations, and I need to be able to match against terms which look like (shift E) X..., but this expands to (shift E X...), so the pattern match fails. I can implement ((X Y) Z) patterns as a special case, but this results in code duplication and prevents shift from returning variadic functions.

using System
def double = \X -> 2 * X

data shift
def reset = resetK (\X -> X)
# accumulates the continuation in an extra argument K
def resetK = \K -> [ (shift E) -> E (\X -> reset (K X))
                   | (X Y) -> resetK (\Y2 -> resetK (\X2 -> K (X2 Y2)) X) Y
                   # | ((X Y) Z) -> resetK (\Z2 -> resetK (\XY2 -> K (XY2 Z2)) (X Y)) Z
                   | X -> K X ]

# works
def test1 = reset (double (shift (\K -> K (K 2))))
# requires the extra `((X Y) Z)` pattern
def test2 = reset ((shift (\K -> K (\X -> X + 2))) 6)
# doesn't work without adding yet another `(((W X) Y) Z)` pattern
def test3 = reset ((shift (\K -> K (\X Y -> X + Y)) 3 4))

Oh hi James, nice to see you've taken an interest, I didn't notice so far. You are correct, according to Egel's (operational) semantics (a b) c equals a b c. As I've been busy writing a new emitter, this could be important. I will get back to you in some days.

Off the cuff, I haven't looked at your example in detail. You did notice that you can write [X Y -> X] a b c and that does rewrite to a c?

I rewrote your example as

using System
def double = \X -> 2 * X

data shift
def reset = resetK [X -> X]
def resetK =
    [ K (shift E) -> E K
    | K F -> resetK [X -> F (K X)] ]

def test1 = reset double (shift (\K -> K (K 2)))
def test2 = reset (shift (\K -> K (\X -> X + 2))) 6
def test3 = reset id(shift (\K -> K (\X Y -> X + Y))) 3 4

def main = (test1, test2, test3)