Closed anqurvanillapy closed 4 months ago
Polymorphic effects.
Koka:
foo : ∀ u . (() → <exn | u> (), () → <exn | u> ()) → <exn | u> ()
RowScript:
foo
: {u : row}
→ (args : ((args: unit) → u unit) * ((args : unit) → u unit) * unit)
→ {auto p : (exn: effect) keyof u}
→ u unit
Duplicate effects.
Koka:
catch : ∀ a u . (() → <exn | u> a, exception → u a) → u a
u
could be <>
or <exn>
RowScript:
catch
: {a : type}
→ {u : row}
→ {v : row}
→ (args : ((args: unit) → u a) * ((args : exception * unit) → v a) * unit)
→ {auto p : u = v + (exn : effect)}
→ v a
Eh, the basic Koka effects that could be used in RowScript are only exn
... and yes the effects are extensible in Koka, but how could users define their own domain-specific effects?
The basic effects in RowScript should be:
throw
: Like Koka's exn
, it throws exceptionResult<T, E>
async
db
, http
Koka's async
effect is still being worked on and entirely based on the paper Structured Asynchrony with Algebraic Effects, where an async runtime is encoded directly using algebraic effects.
I personally don't like this approach since it's all "encoded", it brings the burden to the user if one really wants to dive in.
I prefer using its type signature and doing a simple CPS transformation for codegen, and then the effect performing (i.e. await
) could be directly translated into JavaScript's native await
. And with CPS resolve
, we could implement the wait
function in the paper for simulating things like Go's time.Sleep
.
Interestingly, the type signature is pretty much like "call/cc".
effect async {
fun await( initiate : (result⟨a⟩ → io ()) → io ()) : result⟨a⟩
}
The most general Haskell version callCC
:
newtype Cont r a = Cont {runCont :: (a -> r) -> r}
callCC :: ((forall b. a -> Cont r b) -> Cont r a) -> Cont r a
There should be 3 basic constructs for users:
__await__
: The very original method in interface Await
, it's impossible to directly invoke, it should be internall combined with the special effectful construct, otherwise it's just a plain interface method call and not effectful at allawait
: JavaScript-compatible await
, nothing fancycallcc
(name and presence still not sure): Construct for doing some simple continuation controlThe type signature of Koka's await
looks like call/cc
, but the semantics should not be related to it since it confuses our users. And we need to introduce a new syntax for the continuation-passing version of RowScript's await
.
For now, we would have these asynchronous constructs in RowScript, which are super versatile:
__await__
: The method of interface Await
, for internal use onlyawait expr
: The JavaScript compatible version of await
, but it's a sugar using __await__
await { ... }
(with resolve
in scope): The continuation-passing version of await
, again a sugar using __await__
Await.{all, any}
(wrapper for await#{all, any}
): The renamed version of Promise.{all, any}
since we don't have the concepts of promisesawait { const a = f0(), let b = f1() }; ...
const (a, b) = await(f0(), f1())
: The static declaration bindings of Await.all
since the above APIs are only available for arrays, this one could handle heterogeneous awaits. We don't have the same constructs for Await.any
Await.race
And the sugars:
await
: asyncEmit __await__
await { ... }; ...
: asyncEmit __await_many__
await(expr0, expr1, ...)
: asyncEmit __await_let__
Await.all
: asyncEmit __await_all__
Await.any
: asyncEmit __await_any__
Await.race
: asyncEmit __await_race__
Next we need to consider the CPS for await
, await { ... }
and Await.{all, any, race}
.
Notice that the await(expr0, expr1, ...)
syntax, it's a direct call to interface method __await_let__
:
interface AsyncLet {
__await_let__<Args>(...: Args): Args;
}
So the arguments would actually be the resolved results, not the executor list. The ability of fine-grained executor control is not available in this syntax.
If we really need to have this support, e.g.:
await(
{ resolve(42) },
{ resolve(69) }
);
We actually need much dependent types magic like List<type>
as input and the function signature as the output (i.e. larget elimination), and then generate the function in compile time, which is quite annoying.
Users suggest that we need to treat the effects in a disciplined way, especially these:
ExceptionsConsider using Koka's row polymorphic effect types.