koka-lang / koka

Koka language compiler and interpreter
http://koka-lang.org
Other
3.16k stars 151 forks source link

Suggestion: `rcontext.resume-shallow` should not run the `return` clauses #510

Open anfelor opened 2 months ago

anfelor commented 2 months ago

Unlike Frank, Koka does not remove the return clause when resuming a shallow handler (this was originally spotted by @jasigal). This is currently necessary since resume-shallow has the same return type as resume (which runs the potentially-type-changing return clause): https://koka-lang.github.io/koka/doc/std_core_hnd.html#type_space_resume_context

fun resume( r : resume-context<a,e,e1,b>, x : a ) : e b
fun resume-shallow( r : resume-context<a,e,e1,b>, x : a ) : e1 b

However, this is quite annoying in practice. For example, we could attempt to get Frank-style pattern matching on shallow handlers by writing:

effect send<a>
  ctl send(x : a) : unit

type ssend<a,e,b> { SDone(b : b); Send(x : a, k : () -> <send<a>|e> b) }

fun canthappen() : a
  fun go() { go() }
  unsafe-total(go)

fun ssend(f : () -> <send<a>|e> b) : e ssend<a,e,b>
  with handler
    return(x) { SDone(x) }
    raw ctl send(x)
      Send(x, fn()
        match rcontext.resume-shallow(()) // we would expect to get 'b' but get 'ssend<a,e,b>'
          SDone(y) -> y                   // unwrap the return clause
          Send(_, _) -> canthappen()      // we removed the effect handler so no Send constructor can happen
      )
  f()

This is not just a bit ugly, but also less efficient since resume-shallow(()) should really be a tail-call here.

I would guess that to fix this the resume-context should carry two different types: The return type before running the return() clause and the type after running the return() clause. Then in https://koka-lang.github.io/koka/doc/std_core_hnd-source.html#prompt the case:

  Shallow x ->
    yield-bind( cont({x}), fn(y) ret(y) )

should really be:

  Shallow x ->
    cont( {x} )