Currently this is not possible:
alias eff<x::X,e::E> = <console,x|e>
test.kk(1,33): parse error: invalid syntax
encountered effect variable x when an effect label was expected
hint: effect variables must be after `|` (e.g `<labels|e>`), or by themselves (e.g. `e`)
@daanx In your paper describing Koka's effect system, you mention that "An extra restriction [of the type system] is that effect constants cannot be type variables, i.e. αk is illegal", but I didn't understand why this restriction is necessary from the rest of the paper.
I'm asking because I'm writing a library to write choreographies (1, 2, 3) in Koka, which essentially let you write distributed systems as monolitic programs. For example, the two_buyer example from (1) could be written like this:
fun two-buyer()
val b1/title = b1.locally fn(_) input()
val s/title = b1.comm(s, b1/title)
val s/price = s.locally fn(un) catalog.get-price(s/title.un)
val b1/price = s.comm(b1, s/price)
val b2/price = s.comm(b2, s/price)
val b1/contribution = b2.comm(b1, b2-budget)
val b1/decision = b1.locally fn(un) b1/price <= b1/budget + contribution.un
// enclave restricts the choreography to b1 and s only
val b1s/decision = b1.share(s, b1/decision)
enclave(b1, s) fn(un)
// This shouldn't typecheck, since b2 isn't part of the sub choreography
// val b2/dummy = b2.locally fn(_) 42
if b1s/decision.un then
val s/delivery = s.locally fn(un) catalog.get-delivery(s/title.un)
val b1/delivery = s.comm(b1, s/delivery)
b1.locally fn(un) b1/delivery.un
else
b1.locally fn(_) Nothing
I have a choreo effect that provides these operations. Since we want to enforce the census (the set of participants involved in a choreography), the effect should be parametrized over it. With atomic effect variables, we could represent the census with a row:
// A value of type a at location(s) l
type at<a,l::E>
Local(v: a)
Remote
// l1 and l2 are phantom types representing single participants (locations). k is a location container, for which we can compute actual values. For the two-buyer example, we could have defined:
//
// struct container<l::X>
// name: string
//
// type buyer1 :: X
// val b1: container<buyer1> = Container("buyer1")
// ...
//
// choreo itself is private, the commented choreo<...> annotations are exposed by the public interface
alias choreo = <choreo-div<l,k,e>,div|e>
div effect choreo-div<l :: E, k :: X -> V, e :: E>
// ...
fun locally(l1: k<l1>, sub: (forall<t> at<t,<l1|_>> -> t) -> choreo<<l1>,k,e> a): /* choreo<<l1|_>,k,e> */ at<a,<l1>>
fun enclave(l1: k<l1>, l2: k<l2>, sub: (forall<t> at<t,<l1,l2|_>> -> t) -> choreo<<l1,l2>,k,e> a): /* choreo<<l1,l2|_>,k,e> */ at<a,<l1,l2>>
// ...
In the two-buyer example, enclave would enforce that only b1 and s can be involved in the sub choreography since its census row is closed (doing anything with b2 would be a type error, which is what we want), while the enclave itself is allowed to run within a choreography that involves more participants thanks to its open census row.
My current implementation uses phantom tuples to represent the census instead of phantom rows. I had to overload locally and enclave many times to describe in which contexts they can be used (i.e. a choreo<(l1,l2),...)> enclave is valid inside a choreo<(l2,_,l1),...> context, you can imagine the combinatorial explosion as censuses get bigger), but I just realized that I was just enumerating row extensions / reorderings manually. Maybe there are other solutions I didn't think of, but with atomic effect variables, rows would be a perfect fit.
(I glossed over a lot of details, especially related to choreographic programming, I'm happy to answer questions :))
Currently this is not possible:
@daanx In your paper describing Koka's effect system, you mention that "An extra restriction [of the type system] is that effect constants cannot be type variables, i.e. αk is illegal", but I didn't understand why this restriction is necessary from the rest of the paper.
I'm asking because I'm writing a library to write choreographies (1, 2, 3) in Koka, which essentially let you write distributed systems as monolitic programs. For example, the
two_buyerexample from (1) could be written like this:I have a
choreoeffect that provides these operations. Since we want to enforce the census (the set of participants involved in a choreography), the effect should be parametrized over it. With atomic effect variables, we could represent the census with a row:In the
two-buyerexample,enclavewould enforce that onlyb1andscan be involved in the sub choreography since its census row is closed (doing anything withb2would be a type error, which is what we want), while the enclave itself is allowed to run within a choreography that involves more participants thanks to its open census row.My current implementation uses phantom tuples to represent the census instead of phantom rows. I had to overload
locallyandenclavemany times to describe in which contexts they can be used (i.e. achoreo<(l1,l2),...)>enclave is valid inside achoreo<(l2,_,l1),...>context, you can imagine the combinatorial explosion as censuses get bigger), but I just realized that I was just enumerating row extensions / reorderings manually. Maybe there are other solutions I didn't think of, but with atomic effect variables, rows would be a perfect fit.(I glossed over a lot of details, especially related to choreographic programming, I'm happy to answer questions :))