idris-0.9.11: libs/neweffects/Effects.idr
module Effects
import Language.Reflection
import Control.Catchable
import Effect.Default
%access public
---- Effects
Effect : Type
Effect = (x : Type) -> Type -> (x -> Type) -> Type
%error_reverse
data EFFECT : Type where
MkEff : Type -> Effect -> EFFECT
class Handler (e : Effect) (m : Type -> Type) where
handle : res -> (eff : e t res resk) ->
((x : t) -> resk x -> m a) -> m a
-- A bit of syntactic sugar ('syntax' is not very flexible so we only go
-- up to a small number of parameters...)
syntax "{" [inst] "==>" "{" {b} "}" [outst] "}" [eff]
= eff inst (\b => outst)
syntax "{" [inst] "==>" [outst] "}" [eff] = eff inst (\result => outst)
syntax "{" [inst] "}" [eff] = eff inst (\result => inst)
---- Properties and proof construction
using (xs : List a, ys : List a)
data SubList : List a -> List a -> Type where
SubNil : SubList {a} [] []
Keep : SubList xs ys -> SubList (x :: xs) (x :: ys)
Drop : SubList xs ys -> SubList xs (x :: ys)
subListId : SubList xs xs
subListId {xs = Nil} = SubNil
subListId {xs = x :: xs} = Keep subListId
namespace Env
data Env : (m : Type -> Type) -> List EFFECT -> Type where
Nil : Env m Nil
(::) : Handler eff m => a -> Env m xs -> Env m (MkEff a eff :: xs)
data EffElem : Effect -> Type ->
List EFFECT -> Type where
Here : EffElem x a (MkEff a x :: xs)
There : EffElem x a xs -> EffElem x a (y :: xs)
-- make an environment corresponding to a sub-list
dropEnv : Env m ys -> SubList xs ys -> Env m xs
dropEnv [] SubNil = []
dropEnv (v :: vs) (Keep rest) = v :: dropEnv vs rest
dropEnv (v :: vs) (Drop rest) = dropEnv vs rest
updateWith : (ys' : List a) -> (xs : List a) ->
SubList ys xs -> List a
updateWith (y :: ys) (x :: xs) (Keep rest) = y :: updateWith ys xs rest
updateWith ys (x :: xs) (Drop rest) = x :: updateWith ys xs rest
updateWith [] [] SubNil = []
updateWith (y :: ys) [] SubNil = y :: ys
updateWith [] (x :: xs) (Keep rest) = []
-- put things back, replacing old with new in the sub-environment
rebuildEnv : Env m ys' -> (prf : SubList ys xs) ->
Env m xs -> Env m (updateWith ys' xs prf)
rebuildEnv [] SubNil env = env
rebuildEnv (x :: xs) (Keep rest) (y :: env) = x :: rebuildEnv xs rest env
rebuildEnv xs (Drop rest) (y :: env) = y :: rebuildEnv xs rest env
rebuildEnv (x :: xs) SubNil [] = x :: xs
---- The Effect EDSL itself ----
-- some proof automation
%reflection
reflectListEffElem : List a -> Tactic
reflectListEffElem [] = Refine "Here" `Seq` Solve
reflectListEffElem (x :: xs)
= Try (Refine "Here" `Seq` Solve)
(Refine "There" `Seq` (Solve `Seq` reflectListEffElem xs))
-- TMP HACK! FIXME!
-- The evaluator needs a 'function case' to know its a reflection function
-- until we propagate that information! Without this, the _ case won't get
-- matched.
reflectListEffElem (x ++ y) = Refine "Here" `Seq` Solve
reflectListEffElem _ = Refine "Here" `Seq` Solve
%reflection
reflectSubList : List a -> Tactic
reflectSubList [] = Refine "subListId" `Seq` Solve
reflectSubList (x :: xs)
= Try (Refine "subListId" `Seq` Solve)
(Try (Refine "Keep" `Seq` (Solve `Seq` reflectSubList xs))
(Refine "Drop" `Seq` (Solve `Seq` reflectSubList xs)))
reflectSubList (x ++ y) = Refine "subListId" `Seq` Solve
reflectSubList _ = Refine "subListId" `Seq` Solve
%reflection
reflectDefaultList : List a -> Tactic
reflectDefaultList [] = Refine "enil" `Seq` Solve
reflectDefaultList (x :: xs)
= Refine "econs" `Seq`
(Solve `Seq`
(Instance `Seq`
(Refine "default" `Seq`
(Solve `Seq`
(Instance `Seq`
(reflectDefaultList xs))))))
reflectDefaultList (x ++ y) = Refine "Nil" `Seq` Solve
reflectDefaultList _ = Refine "Nil" `Seq` Solve
%reflection
reflectEff : (P : Type) -> Tactic
reflectEff (EffElem m a xs)
= reflectListEffElem xs `Seq` Solve
reflectEff (SubList xs ys)
= reflectSubList ys `Seq` Solve
reflectEff (Env m xs)
= reflectDefaultList xs `Seq` Solve
updateResTy : (val : t) ->
(xs : List EFFECT) -> EffElem e a xs -> e t a b ->
List EFFECT
updateResTy {b} val (MkEff a e :: xs) Here n = (MkEff (b val) e) :: xs
updateResTy val (x :: xs) (There p) n = x :: updateResTy val xs p n
-- updateResTyImm : (xs : List EFFECT) -> EffElem e a xs -> Type ->
-- List EFFECT
-- updateResTyImm (MkEff a e :: xs) Here b = (MkEff b e) :: xs
-- updateResTyImm (x :: xs) (There p) b = x :: updateResTyImm xs p b
infix 5 :::, :-, :=
data LRes : lbl -> Type -> Type where
(:=) : (x : lbl) -> res -> LRes x res
(:::) : lbl -> EFFECT -> EFFECT
(:::) {lbl} x (MkEff r e) = MkEff (LRes x r) e
using (lbl : Type)
instance Default a => Default (LRes lbl a) where
default = lbl := default
private
unlabel : {l : ty} -> Env m [l ::: x] -> Env m [x]
unlabel {m} {x = MkEff a eff} [l := v] = [v]
private
relabel : (l : ty) -> Env m xs -> Env m (map (\x => l ::: x) xs)
relabel {xs = []} l [] = []
relabel {xs = (MkEff a e :: xs)} l (v :: vs) = (l := v) :: relabel l vs
-- the language of Effects
data Eff : (m : Type -> Type) ->
(x : Type) ->
List EFFECT -> (x -> List EFFECT) -> Type where
value : a -> Eff m a xs (\v => xs)
ebind : Eff m a xs xs' ->
((val : a) -> Eff m b (xs' val) xs'') -> Eff m b xs xs''
effect : (prf : EffElem e a xs) ->
(eff : e t a b) ->
Eff m t xs (\v => updateResTy v xs prf eff)
lift : (prf : SubList ys xs) ->
Eff m t ys ys' -> Eff m t xs (\v => updateWith (ys' v) xs prf)
new : Handler e m =>
res ->
Eff m a (MkEff res e :: xs) (\v => (MkEff res' e :: xs')) ->
Eff m a xs (\v => xs')
catch : Catchable m err =>
Eff m a xs xs' -> (err -> Eff m a xs xs') ->
Eff m a xs xs'
(:-) : (l : ty) ->
Eff m t [x] xs' -> -- [x] (\v => xs) ->
Eff m t [l ::: x] (\v => map (l :::) (xs' v))
(>>=) : Eff m a xs xs' ->
((val : a) -> Eff m b (xs' val) xs'') -> Eff m b xs xs''
(>>=) = ebind
implicit
lift' : Eff m t ys ys' ->
{default tactics { byReflection reflectEff; }
prf : SubList ys xs} ->
Eff m t xs (\v => updateWith (ys' v) xs prf)
lift' e {prf} = lift prf e
implicit
effect' : {a, b: _} -> {e : Effect} ->
(eff : e t a b) ->
{default tactics { byReflection reflectEff; }
prf : EffElem e a xs} ->
Eff m t xs (\v => updateResTy v xs prf eff)
effect' e {prf} = effect prf e
return : a -> Eff m a xs (\v => xs)
return x = value x
-- for idiom brackets
infixl 2 <$>
pure : a -> Eff m a xs (\v => xs)
pure = value
(<$>) : Eff m (a -> b) xs (\v => xs) ->
Eff m a xs (\v => xs) -> Eff m b xs (\v => xs)
(<$>) prog v = do fn <- prog
arg <- v
return (fn arg)
-- an interpreter
private
execEff : Env m xs -> (p : EffElem e res xs) ->
(eff : e a res resk) ->
((v : a) -> Env m (updateResTy v xs p eff) -> m t) -> m t
execEff (val :: env) Here eff' k
= handle val eff' (\v, res => k v (res :: env))
execEff (val :: env) (There p) eff k
= execEff env p eff (\v, env' => k v (val :: env'))
-- Q: Instead of m b, implement as StateT (Env m xs') m b, so that state
-- updates can be propagated even through failing computations?
eff : Env m xs -> Eff m a xs xs' -> ((x : a) -> Env m (xs' x) -> m b) -> m b
eff env (value x) k = k x env
eff env (prog `ebind` c) k
= eff env prog (\p', env' => eff env' (c p') k)
eff env (effect prf effP) k = execEff env prf effP k
eff env (lift prf effP) k
= let env' = dropEnv env prf in
eff env' effP (\p', envk => k p' (rebuildEnv envk prf env))
eff env (new r prog) k
= let env' = r :: env in
eff env' prog (\p' => \ (v :: envk) => k p' envk)
eff env (catch prog handler) k
= catch (eff env prog k)
(\e => eff env (handler e) k)
-- FIXME:
-- xs is needed explicitly because otherwise the pattern binding for
-- 'l' appears too late. Solution seems to be to reorder patterns at the
-- end so that everything is in scope when it needs to be.
eff {xs = [l ::: x]} env (l :- prog) k
= let env' = unlabel env in
eff env' prog (\p', envk => k p' (relabel l envk))
-- yuck :) Haven't got type class instances working nicely in tactic
-- proofs yet, so just brute force it.
syntax MkDefaultEnv = (| [], [default], [default, default],
[default, default, default],
[default, default, default, default],
[default, default, default, default, default],
[default, default, default, default, default, default],
[default, default, default, default, default, default, default],
[default, default, default, default, default, default, default, default] |)
run : Applicative m => {default MkDefaultEnv env : Env m xs} -> Eff m a xs xs' -> m a
run {env} prog = eff env prog (\r, env => pure r)
runPure : {default MkDefaultEnv env : Env id xs} -> Eff id a xs xs' -> a
runPure {env} prog = eff env prog (\r, env => r)
runInit : Applicative m => Env m xs -> Eff m a xs xs' -> m a
runInit env prog = eff env prog (\r, env => pure r)
runPureInit : Env id xs -> Eff id a xs xs' -> a
runPureInit env prog = eff env prog (\r, env => r)
runWith : (a -> m a) -> Env m xs -> Eff m a xs xs' -> m a
runWith inj env prog = eff env prog (\r, env => inj r)
runEnv : Applicative m => Env m xs -> Eff m a xs xs' ->
m (x : a ** Env m (xs' x))
runEnv env prog = eff env prog (\r, env => pure (r ** env))
-- some higher order things
mapE : Applicative m => (a -> {xs} Eff m b) -> List a -> {xs} Eff m (List b)
mapE f [] = pure []
mapE f (x :: xs) = [| f x :: mapE f xs |]
when : Applicative m => Bool -> ({xs} Eff m ()) -> {xs} Eff m ()
when True e = e
when False e = pure ()