/Eff

Experiments with effect systems

Primary LanguageAgda

Eff

Most of the code is about constructing a fully universe polymorphic effect system in Agda. It's unreadable as always with generic universe polymorphic stuff.

Loop.Core is the most readable version as it enables --type-in-type, so I'll describe its content rather than the content of Resources.Core, which is properly universe polymorphic and less powerful ("for historical reasons").

Effects are represented like in the Idris Effects library, but resources are values rather than types:

Effect : Set -> Set
Effect R = R -> (A : Set) -> (A -> R) -> Set

Instead of defining Eff directly, we define the indexed version of the Oleg Kiselyov's Freer monad, which is an effect transformer:

data IFreer {R : Set} (Ψ : Effect R) : Effect R where
  return : ∀ {B r′} y -> IFreer Ψ (r′ y) B r′
  call   : ∀ {r A r′ B r′′} -> Ψ r A r′ -> (∀ x -> IFreer Ψ (r′ x) B r′′) -> IFreer Ψ r B r′′

As well as Eff in Idris it's a Hoare state monad (HST in Verifying Higher-order Programs with the Dijkstra Monad) as witnessed by

_>>=_ : ∀ {R Ψ r B r′ C r′′}
      -> IFreer {R} Ψ r B r′ -> (∀ y -> IFreer Ψ (r′ y) C r′′) -> IFreer Ψ r C r′′
return y >>= g = g y
call a f >>= g = call a λ x -> f x >>= g

We also have higher effects which operate on lists of simple effects and transform heterogeneous lists of resources:

Resources = HList

Effects : Sets -> Set
Effects = List₁ Effect

HigherEffect : Set
HigherEffect = ∀ {Rs} -> Effects Rs -> Effect (Resources Rs)

The union of a list of effects is a higher effect:

data Unionᵉ : HigherEffect where
  hereᵉ  : ∀ {R Rs r A r′ rs}  {Ψ : Effect R} {Ψs : Effects Rs}
         -> Ψ r A r′ -> Unionᵉ (Ψ , Ψs) (r , rs) A (λ x -> r′ x , rs)
  thereᵉ : ∀ {R Rs r A rs rs′} {Ψ : Effect R} {Ψs : Effects Rs}
         -> Unionᵉ Ψs rs A rs′ -> Unionᵉ (Ψ , Ψs) (r , rs) A (λ x -> r , rs′ x)

Unionʰᵉ unions a list of higher effects:

data Unionʰᵉ : HigherEffects -> HigherEffect where
  hereʰᵉ   : ∀ {Φs Rs rs A rs′} {Φ : HigherEffect} {Ψs : Effects Rs}
           -> Φ {Rs} Ψs rs A rs′ -> Unionʰᵉ (Φ ∷ Φs) Ψs rs A rs′
  thereʰᵉ  : ∀ {Φs Rs rs A rs′} {Φ : HigherEffect} {Ψs : Effects Rs}
           -> Unionʰᵉ Φs Ψs rs A rs′ -> Unionʰᵉ (Φ ∷ Φs) Ψs rs A rs′

Here is the main definition:

EffOver : HigherEffects -> HigherEffect
EffOver Φs Ψs = IFreer (Unionʰᵉ (Unionᵉ ∷ Φs) Ψs)

EffOver describes computations over a list of higher effects Φs and a list of simple effects Ψs.

The usual Eff is recovered by

Eff : HigherEffect
Eff = EffOver []

I.e. no higher effects except for the union of simple effects.

So while a computation can't change the set of effects like in Idris, it can change the resources. A canonical example is the indexed State:

test : Eff (State , tt) (ℕ , tt) ℕ (λ n -> V.Vec Bool n , tt)
test = get >>= λ n -> put (V.replicate true) >> return n

(put is zap ℕ actually, because instance search needs an additional hint in this case)

So we start from State ℕ and get State (Vec Bool n), where n comes from State ℕ.

We can run this computation:

test-test : runEff (execState 3 test) ≡ (3 , true ∷ true ∷ true ∷ [])
test-test = refl

There is also an effectful tic-tac-toe game that compiles.