on master help sign in
Added experimental neweffects library

Simplifying effects API by removing 'm' parameter from Eff and leaving choice of computation context to the Handler.

commitb2653e435ec1c0d96278c3435233eef51e571437 authorEdwin Brady <ecb10@st-andrews.ac.uk> dateSat 14 Jun 2014, 17:47 (12 years ago) parent2c8d61c
16 files changed+943−5
CHANGELOG+1−1 idris.cabal+4−4 libs/Makefile+5 libs/neweffects/Effect/Default.idr+44 libs/neweffects/Effect/Exception.idr+42 libs/neweffects/Effect/File.idr+128 libs/neweffects/Effect/Memory.idr+175 libs/neweffects/Effect/Monad.idr+21 libs/neweffects/Effect/Random.idr+47 libs/neweffects/Effect/Select.idr+23 libs/neweffects/Effect/State.idr+40 libs/neweffects/Effect/StdIO.idr+60 libs/neweffects/Effect/System.idr+38 libs/neweffects/Effects.idr+292 libs/neweffects/Makefile+14 libs/neweffects/effects.ipkg+9
CHANGELOG+1−1
@@ -7,7 +7,7 @@ New in 0.9.14:
77* Clearly distinguish between type providers and postulate providers at the use site
88* Allow dependent function syntax to be overridden in dsl blocks, similarly to
99 functions and let. The keyword for this is "pi".
10−
10+* Experimental 'neweffects' library, with simplified API
1111
1212New in 0.9.13:
1313--------------
idris.cabal+4−4
@@ -100,10 +100,10 @@ Extra-source-files:
100100 libs/effects/Effect/*.idr
101101 libs/effects/*.idr
102102
103− libs/oldeffects/Makefile
104− libs/oldeffects/oldeffects.ipkg
105− libs/oldeffects/Effect/*.idr
106− libs/oldeffects/*.idr
103+ libs/neweffects/Makefile
104+ libs/neweffects/oldeffects.ipkg
105+ libs/neweffects/Effect/*.idr
106+ libs/neweffects/*.idr
107107
108108 llvm/*.c
109109 llvm/Makefile
libs/Makefile+5
@@ -2,27 +2,32 @@ build:
22 $(MAKE) -C prelude build
33 $(MAKE) -C base build
44 $(MAKE) -C effects build
5+ $(MAKE) -C neweffects build
56
67
78install:
89 $(MAKE) -C prelude install
910 $(MAKE) -C base install
1011 $(MAKE) -C effects install
12+ $(MAKE) -C neweffects install
1113
1214clean:
1315 $(MAKE) -C prelude clean
1416 $(MAKE) -C base clean
1517 $(MAKE) -C effects clean
18+ $(MAKE) -C neweffects clean
1619
1720doc:
1821 $(MAKE) -C prelude doc
1922 $(MAKE) -C base doc
2023 $(MAKE) -C effects doc
24+ $(MAKE) -C neweffects doc
2125
2226doc_clean:
2327 $(MAKE) -C prelude doc_clean
2428 $(MAKE) -C base doc_clean
2529 $(MAKE) -C effects doc_clean
30+ $(MAKE) -C neweffects doc_clean
2631
2732
2833.PHONY: build install clean doc doc_clean
libs/neweffects/Effect/Default.idrnew file+44
@@ -0,0 +1,44 @@
1+module Default
2+
3+class Default a where
4+ default : a
5+
6+instance Default Int where
7+ default = 0
8+
9+instance Default Integer where
10+ default = 0
11+
12+instance Default Float where
13+ default = 0
14+
15+instance Default Nat where
16+ default = 0
17+
18+instance Default Char where
19+ default = '\0'
20+
21+instance Default String where
22+ default = ""
23+
24+instance Default Bool where
25+ default = False
26+
27+instance Default () where
28+ default = ()
29+
30+instance (Default a, Default b) => Default (a, b) where
31+ default = (default, default)
32+
33+instance Default (Maybe a) where
34+ default = Nothing
35+
36+instance Default (List a) where
37+ default = []
38+
39+instance Default a => Default (Vect n a) where
40+ default = mkDef _ where
41+ mkDef : (n : Nat) -> Vect n a
42+ mkDef Z = []
43+ mkDef (S k) = default :: mkDef k
44+
libs/neweffects/Effect/Exception.idrnew file+42
@@ -0,0 +1,42 @@
1+module Effect.Exception
2+
3+import Effects
4+import System
5+import Control.IOExcept
6+
7+data Exception : Type -> Effect where
8+ Raise : a -> { () } Exception a b
9+
10+instance Handler (Exception a) Maybe where
11+ handle _ (Raise e) k = Nothing
12+
13+instance Handler (Exception a) List where
14+ handle _ (Raise e) k = []
15+
16+instance Show a => Handler (Exception a) IO where
17+ handle _ (Raise e) k = do print e
18+ believe_me (exit 1)
19+
20+instance Handler (Exception a) (IOExcept a) where
21+ handle _ (Raise e) k = ioM (return (Left e))
22+
23+instance Handler (Exception a) (Either a) where
24+ handle _ (Raise e) k = Left e
25+
26+EXCEPTION : Type -> EFFECT
27+EXCEPTION t = MkEff () (Exception t)
28+
29+raise : a -> { [EXCEPTION a ] } Eff b
30+raise err = call $ Raise err
31+
32+
33+
34+
35+
36+
37+-- TODO: Catching exceptions mid program?
38+-- probably need to invoke a new interpreter
39+
40+-- possibly add a 'handle' to the Eff language so that an alternative
41+-- handler can be introduced mid interpretation?
42+
libs/neweffects/Effect/File.idrnew file+128
@@ -0,0 +1,128 @@
1+-- -------------------------------------------------------- [ Effectful FileIO ]
2+module Effect.File
3+
4+import Effects
5+import Control.IOExcept
6+
7+
8+||| A Dependent type to describe File Handles. File handles are
9+||| parameterised with the current state of the file: Closed; Open for
10+||| reading; and Open for writing.
11+|||
12+||| @ m The file mode.
13+data OpenFile : (m : Mode) -> Type where
14+ FH : File -> OpenFile m
15+
16+openOK : Mode -> Bool -> Type
17+openOK m True = OpenFile m
18+openOK m False = ()
19+
20+-- ------------------------------------------------------------ [ The Protocol ]
21+
22+||| Here the protocol for resource access is defined as an effect.
23+||| The state transitions diagram for the protocol is as follows:
24+|||
25+||| digraph G {
26+|||
27+||| empty; read; write; // States
28+|||
29+||| empty -> read [label="Open R"];
30+||| read -> empty [label="Close"];
31+||| read -> read [label="ReadLine"];
32+||| read -> read [label="EOF"];
33+|||
34+||| empty -> write [label="Open W"];
35+||| write -> empty [label="Close"];
36+||| write -> write [label="WriteLine"];
37+|||
38+||| }
39+data FileIO : Effect where
40+ ||| Open a file with the specified mode.
41+ |||
42+ ||| Opening a file successful moves the state from 'empty' to the
43+ ||| specified mode. If not successful the state is still 'empty'.
44+ |||
45+ ||| @ fname The file name to be opened.
46+ ||| @ m The file mode.
47+ Open : (fname: String)
48+ -> (m : Mode)
49+ -> {() ==> {res} if res
50+ then OpenFile m
51+ else ()} FileIO Bool
52+
53+ ||| Close a file.
54+ |||
55+ ||| Closing a file moves the state from Open to closed.
56+ Close : {OpenFile m ==> ()} FileIO ()
57+
58+ ||| Read a line from the file.
59+ |||
60+ ||| Only files that are open for reading can be read.
61+ ReadLine : {OpenFile Read} FileIO String
62+
63+ ||| Write a line to a file.
64+ |||
65+ ||| Only file that are open for writing can be written to.
66+ WriteLine : String -> {OpenFile Write} FileIO ()
67+
68+ ||| End of file?
69+ |||
70+ ||| Only files open for reading can be tested for EOF
71+ EOF : {OpenFile Read} FileIO Bool
72+
73+-- ------------------------------------------------------------ [ The Handlers ]
74+
75+--- An implementation of the resource access protocol for the IO Context.
76+instance Handler FileIO IO where
77+ handle () (Open fname m) k = do h <- openFile fname m
78+ valid <- validFile h
79+ if valid then k True (FH h)
80+ else k False ()
81+ handle (FH h) Close k = do closeFile h
82+ k () ()
83+ handle (FH h) ReadLine k = do str <- fread h
84+ k str (FH h)
85+ handle (FH h) (WriteLine str) k = do fwrite h str
86+ k () (FH h)
87+ handle (FH h) EOF k = do e <- feof h
88+ k e (FH h)
89+
90+-- -------------------------------------------------------------- [ The Effect ]
91+FILE_IO : Type -> EFFECT
92+FILE_IO t = MkEff t FileIO
93+
94+-- ------------------------------------------------------------ [ The Bindings ]
95+--
96+-- Bind the IO context handlers to functions. These functions will run
97+-- in the IO context.
98+--
99+
100+||| Open a file with the specified mode.
101+|||
102+||| @ fname The file name to be opened.
103+||| @ m The file mode.
104+open : (fname : String)
105+ -> (m : Mode)
106+ -> { [FILE_IO ()] ==> [FILE_IO (if result
107+ then OpenFile m
108+ else ())] } Eff Bool
109+open f m = call $ Open f m
110+
111+
112+||| Close a file.
113+close : { [FILE_IO (OpenFile m)] ==> [FILE_IO ()] } Eff ()
114+close = call $ Close
115+
116+||| Read a line from the file.
117+readLine : { [FILE_IO (OpenFile Read)] } Eff String
118+readLine = call $ ReadLine
119+
120+||| Write a line to a file.
121+writeLine : String -> { [FILE_IO (OpenFile Write)] } Eff ()
122+writeLine str = call $ WriteLine str
123+
124+||| End of file?
125+eof : { [FILE_IO (OpenFile Read)] } Eff Bool
126+eof = call $ EOF
127+
128+-- --------------------------------------------------------------------- [ EOF ]
libs/neweffects/Effect/Memory.idrnew file+175
@@ -0,0 +1,175 @@
1+module Effect.Memory
2+
3+import Effects
4+import Control.IOExcept
5+
6+%access public
7+
8+abstract
9+data MemoryChunk : Nat -> Nat -> Type where
10+ CH : Ptr -> MemoryChunk size initialized
11+
12+abstract
13+data RawMemory : Effect where
14+ Allocate : (n : Nat) ->
15+ RawMemory () () (\v => MemoryChunk n 0)
16+ Free : RawMemory () (MemoryChunk n i) (\v => ())
17+ Initialize : Bits8 ->
18+ (size : Nat) ->
19+ so (i + size <= n) ->
20+ RawMemory () (MemoryChunk n i) (\v => MemoryChunk n (i + size))
21+ Peek : (offset : Nat) ->
22+ (size : Nat) ->
23+ so (offset + size <= i) ->
24+ RawMemory (Vect size Bits8)
25+ (MemoryChunk n i) (\v => MemoryChunk n i)
26+ Poke : (offset : Nat) ->
27+ (Vect size Bits8) ->
28+ so (offset <= i && offset + size <= n) ->
29+ RawMemory () (MemoryChunk n i) (\v => MemoryChunk n (max i (offset + size)))
30+ Move : (src : MemoryChunk src_size src_init) ->
31+ (dst_offset : Nat) ->
32+ (src_offset : Nat) ->
33+ (size : Nat) ->
34+ so (dst_offset <= dst_init && dst_offset + size <= dst_size) ->
35+ so (src_offset + size <= src_init) ->
36+ RawMemory () (MemoryChunk dst_size dst_init)
37+ (\v => MemoryChunk dst_size (max dst_init (dst_offset + size)))
38+ GetRawPtr : RawMemory (MemoryChunk n i) (MemoryChunk n i) (\v => MemoryChunk n i)
39+
40+private
41+do_malloc : Nat -> IOExcept String Ptr
42+do_malloc size with (fromInteger (cast size) == size)
43+ | True = do ptr <- ioe_lift $ mkForeign (FFun "malloc" [FInt] FPtr) (fromInteger $ cast size)
44+ fail <- ioe_lift $ nullPtr ptr
45+ if fail then ioe_fail "Cannot allocate memory"
46+ else return ptr
47+ | False = ioe_fail "The target architecture does not support adressing enough memory"
48+
49+private
50+do_memset : Ptr -> Nat -> Bits8 -> Nat -> IO ()
51+do_memset ptr offset c size
52+ = mkForeign (FFun "idris_memset" [FPtr, FInt, FByte, FInt] FUnit)
53+ ptr (fromInteger $ cast offset) c (fromInteger $ cast size)
54+
55+private
56+do_free : Ptr -> IO ()
57+do_free ptr = mkForeign (FFun "free" [FPtr] FUnit) ptr
58+
59+private
60+do_memmove : Ptr -> Ptr -> Nat -> Nat -> Nat -> IO ()
61+do_memmove dest src dest_offset src_offset size
62+ = mkForeign (FFun "idris_memmove" [FPtr, FPtr, FInt, FInt, FInt] FUnit)
63+ dest src (fromInteger $ cast dest_offset) (fromInteger $ cast src_offset) (fromInteger $ cast size)
64+
65+private
66+do_peek : Ptr -> Nat -> (size : Nat) -> IO (Vect size Bits8)
67+do_peek _ _ Z = return (Prelude.Vect.Nil)
68+do_peek ptr offset (S n)
69+ = do b <- mkForeign (FFun "idris_peek" [FPtr, FInt] FByte) ptr (fromInteger $ cast offset)
70+ bs <- do_peek ptr (S offset) n
71+ Prelude.Monad.return (Prelude.Vect.(::) b bs)
72+
73+private
74+do_poke : Ptr -> Nat -> Vect size Bits8 -> IO ()
75+do_poke _ _ [] = return ()
76+do_poke ptr offset (b::bs)
77+ = do mkForeign (FFun "idris_poke" [FPtr, FInt, FByte] FUnit) ptr (fromInteger $ cast offset) b
78+ do_poke ptr (S offset) bs
79+
80+instance Handler RawMemory (IOExcept String) where
81+ handle () (Allocate n) k
82+ = do ptr <- do_malloc n
83+ k () (CH ptr)
84+ handle {-{res = MemoryChunk _ offset}-} (CH ptr) (Initialize {i} c size _) k
85+ = ioe_lift (do_memset ptr i c size) $> k () (CH ptr)
86+ handle (CH ptr) (Free) k
87+ = ioe_lift (do_free ptr) $> k () ()
88+ handle (CH ptr) (Peek offset size _) k
89+ = do res <- ioe_lift (do_peek ptr offset size)
90+ k res (CH ptr)
91+ handle (CH ptr) (Poke offset content _) k
92+ = do ioe_lift (do_poke ptr offset content)
93+ k () (CH ptr)
94+ handle (CH dest_ptr) (Move (CH src_ptr) dest_offset src_offset size _ _) k
95+ = do ioe_lift (do_memmove dest_ptr src_ptr dest_offset src_offset size)
96+ k () (CH dest_ptr)
97+ handle chunk (GetRawPtr) k
98+ = k chunk chunk
99+
100+RAW_MEMORY : Type -> EFFECT
101+RAW_MEMORY t = MkEff t RawMemory
102+
103+allocate : (n : Nat) ->
104+ Eff () [RAW_MEMORY ()] (\v => [RAW_MEMORY (MemoryChunk n 0)])
105+allocate size = call $ Allocate size
106+
107+initialize : {i : Nat} ->
108+ {n : Nat} ->
109+ Bits8 ->
110+ (size : Nat) ->
111+ so (i + size <= n) ->
112+ Eff () [RAW_MEMORY (MemoryChunk n i)]
113+ (\v => [RAW_MEMORY (MemoryChunk n (i + size))])
114+initialize c size prf = call $ Initialize c size prf
115+
116+free : Eff () [RAW_MEMORY (MemoryChunk n i)] (\v => [RAW_MEMORY ()])
117+free = call Free
118+
119+peek : {i : Nat} ->
120+ (offset : Nat) ->
121+ (size : Nat) ->
122+ so (offset + size <= i) ->
123+ { [RAW_MEMORY (MemoryChunk n i)] } Eff (Vect size Bits8)
124+peek offset size prf = call $ Peek offset size prf
125+
126+poke : {n : Nat} ->
127+ {i : Nat} ->
128+ (offset : Nat) ->
129+ Vect size Bits8 ->
130+ so (offset <= i && offset + size <= n) ->
131+ Eff () [RAW_MEMORY (MemoryChunk n i)]
132+ (\v => [RAW_MEMORY (MemoryChunk n (max i (offset + size)))])
133+poke offset content prf = call $ Poke offset content prf
134+
135+private
136+getRawPtr : { [RAW_MEMORY (MemoryChunk n i)] } Eff (MemoryChunk n i)
137+getRawPtr = call $ GetRawPtr
138+
139+private
140+move' : {dst_size : Nat} ->
141+ {dst_init : Nat} ->
142+ {src_init : Nat} ->
143+ (src_ptr : MemoryChunk src_size src_init) ->
144+ (dst_offset : Nat) ->
145+ (src_offset : Nat) ->
146+ (size : Nat) ->
147+ so (dst_offset <= dst_init && dst_offset + size <= dst_size) ->
148+ so (src_offset + size <= src_init) ->
149+ Eff () [RAW_MEMORY (MemoryChunk dst_size dst_init)]
150+ (\v => [RAW_MEMORY (MemoryChunk dst_size (max dst_init (dst_offset + size)))])
151+move' src_ptr dst_offset src_offset size dst_bounds src_bounds
152+ = call $ Move src_ptr dst_offset src_offset size dst_bounds src_bounds
153+
154+data MoveDescriptor = Dst | Src
155+
156+move : {dst_size : Nat} ->
157+ {dst_init : Nat} ->
158+ {src_size : Nat} ->
159+ {src_init : Nat} ->
160+ (dst_offset : Nat) ->
161+ (src_offset : Nat) ->
162+ (size : Nat) ->
163+ so (dst_offset <= dst_init && dst_offset + size <= dst_size) ->
164+ so (src_offset + size <= src_init) ->
165+ Eff ()
166+ [ Dst ::: RAW_MEMORY (MemoryChunk dst_size dst_init)
167+ , Src ::: RAW_MEMORY (MemoryChunk src_size src_init)]
168+ (\v =>
169+ [ Dst ::: RAW_MEMORY (MemoryChunk dst_size (max dst_init (dst_offset + size)))
170+ , Src ::: RAW_MEMORY (MemoryChunk src_size src_init)])
171+move dst_offset src_offset size dst_bounds src_bounds
172+ = do src_ptr <- Src :- getRawPtr
173+ Dst :- move' src_ptr dst_offset src_offset size dst_bounds src_bounds
174+ return ()
175+
libs/neweffects/Effect/Monad.idrnew file+21
@@ -0,0 +1,21 @@
1+module Effect.Monad
2+
3+import Effects
4+
5+-- Eff is a monad too, so we can happily use it in a monad transformer.
6+
7+using (xs : List EFFECT, m : Type -> Type)
8+ instance Functor (\a => EffM m a xs (\v => xs)) where
9+ map f prog = do t <- prog
10+ value (f t)
11+
12+ instance Applicative (\a => EffM m a xs (\v => xs)) where
13+ pure = value
14+ (<$>) f a = do f' <- f
15+ a' <- a
16+ value (f' a')
17+
18+ instance Monad (\a => Eff m a xs (\v => xs)) where
19+ (>>=) = Effects.(>>=)
20+
21+
libs/neweffects/Effect/Random.idrnew file+47
@@ -0,0 +1,47 @@
1+module Effect.Random
2+
3+import Effects
4+
5+data Random : Effect where
6+ getRandom : { Integer } Random Integer
7+ setSeed : Integer -> { Integer } Random ()
8+
9+using (m : Type -> Type)
10+ instance Handler Random m where
11+ handle seed getRandom k
12+ = let seed' = assert_total ((1664525 * seed + 1013904223) `prim__sremBigInt` (pow 2 32)) in
13+ k seed' seed'
14+ handle seed (setSeed n) k = k () n
15+
16+RND : EFFECT
17+RND = MkEff Integer Random
18+
19+||| Generates a random Integer in a given range
20+rndInt : Integer -> Integer -> { [RND] } Eff Integer
21+rndInt lower upper = do v <- call $ getRandom
22+ return (v `prim__sremBigInt` (upper - lower) + lower)
23+
24+||| Generate a random number in Fin (S `k`)
25+|||
26+||| Note that rndFin k takes values 0, 1, ..., k.
27+rndFin : (k : Nat) -> { [RND] } Eff (Fin (S k))
28+rndFin k = do let v = assert_total $ !(call getRandom) `prim__sremBigInt` (cast (S k))
29+ return (toFin v)
30+ where toFin : Integer -> Fin (S k)
31+ toFin x = case integerToFin x (S k) of
32+ Just v => v
33+ Nothing => toFin (assert_smaller x (x - cast (S k)))
34+
35+||| Select a random element from a vector
36+rndSelect' : Vect (S k) a -> { [RND] } Eff a
37+rndSelect' {k} xs = return (Vect.index !(rndFin k) xs)
38+
39+||| Select a random element from a list, or Nothing if the list is empty
40+rndSelect : List a -> { [RND] } Eff (Maybe a)
41+rndSelect [] = return Nothing
42+rndSelect (x::xs) = return (Just !(rndSelect' (x::(fromList xs))))
43+
44+||| Sets the random seed
45+srand : Integer -> { [RND] } Eff ()
46+srand n = call $ setSeed n
47+
libs/neweffects/Effect/Select.idrnew file+23
@@ -0,0 +1,23 @@
1+module Effect.Select
2+
3+import Effects
4+
5+data Selection : Effect where
6+ Select : List a -> { () } Selection a
7+
8+instance Handler Selection Maybe where
9+ handle _ (Select xs) k = tryAll xs where
10+ tryAll [] = Nothing
11+ tryAll (x :: xs) = case k x () of
12+ Nothing => tryAll xs
13+ Just v => Just v
14+
15+instance Handler Selection List where
16+ handle r (Select xs) k = concatMap (\x => k x r) xs
17+
18+SELECT : EFFECT
19+SELECT = MkEff () Selection
20+
21+select : List a -> { [SELECT] } Eff a
22+select xs = call $ Select xs
23+
libs/neweffects/Effect/State.idrnew file+40
@@ -0,0 +1,40 @@
1+module Effect.State
2+
3+import Effects
4+
5+%access public
6+
7+data State : Effect where
8+ Get : { a } State a
9+ Put : b -> { a ==> b } State ()
10+
11+using (m : Type -> Type)
12+ instance Handler State m where
13+ handle st Get k = k st st
14+ handle st (Put n) k = k () n
15+
16+STATE : Type -> EFFECT
17+STATE t = MkEff t State
18+
19+get : { [STATE x] } Eff x
20+get = call $ Get
21+
22+put : x -> { [STATE x] } Eff ()
23+put val = call $ Put val
24+
25+putM : y -> { [STATE x] ==> [STATE y] } Eff ()
26+putM val = call $ Put val
27+
28+update : (x -> x) -> { [STATE x] } Eff ()
29+update f = put (f !get)
30+
31+updateM : (x -> y) -> { [STATE x] ==> [STATE y] } Eff ()
32+updateM f = putM (f !get)
33+
34+locally : x -> ({ [STATE x] } Eff t) -> { [STATE y] } Eff t
35+locally newst prog = do st <- get
36+ putM newst
37+ val <- prog
38+ putM st
39+ return val
40+
libs/neweffects/Effect/StdIO.idrnew file+60
@@ -0,0 +1,60 @@
1+module Effect.StdIO
2+
3+import Effects
4+import Control.IOExcept
5+
6+-------------------------------------------------------------
7+-- IO effects internals
8+-------------------------------------------------------------
9+
10+||| The internal representation of StdIO effects
11+data StdIO : Effect where
12+ PutStr : String -> { () } StdIO ()
13+ GetStr : { () } StdIO String
14+ PutCh : Char -> { () } StdIO ()
15+ GetCh : { () } StdIO Char
16+
17+
18+-------------------------------------------------------------
19+-- IO effects handlers
20+-------------------------------------------------------------
21+
22+instance Handler StdIO IO where
23+ handle () (PutStr s) k = do putStr s; k () ()
24+ handle () GetStr k = do x <- getLine; k x ()
25+ handle () (PutCh c) k = do putChar c; k () ()
26+ handle () GetCh k = do x <- getChar; k x ()
27+
28+instance Handler StdIO (IOExcept a) where
29+ handle () (PutStr s) k = do ioe_lift $ putStr s; k () ()
30+ handle () GetStr k = do x <- ioe_lift $ getLine; k x ()
31+ handle () (PutCh c) k = do ioe_lift $ putChar c; k () ()
32+ handle () GetCh k = do x <- ioe_lift $ getChar; k x ()
33+
34+-------------------------------------------------------------
35+--- The Effect and associated functions
36+-------------------------------------------------------------
37+
38+STDIO : EFFECT
39+STDIO = MkEff () StdIO
40+
41+||| Write a string to standard output.
42+putStr : String -> { [STDIO] } Eff ()
43+putStr s = call $ PutStr s
44+
45+||| Write a character to standard output.
46+putChar : Char -> { [STDIO] } Eff ()
47+putChar c = call $ PutCh c
48+
49+||| Write a string to standard output, terminating with a newline.
50+putStrLn : String -> { [STDIO] } Eff ()
51+putStrLn s = putStr (s ++ "\n")
52+
53+||| Read a string from standard input.
54+getStr : { [STDIO] } Eff String
55+getStr = call $ GetStr
56+
57+||| Read a character from standard input.
58+getChar : { [STDIO] } Eff Char
59+getChar = call $ GetCh
60+
libs/neweffects/Effect/System.idrnew file+38
@@ -0,0 +1,38 @@
1+module Effect.System
2+
3+-- Various system interaction features (this is not necessarily the right way
4+-- to split them up, I just need some of them now :))
5+
6+import Effects
7+import System
8+import Control.IOExcept
9+
10+data System : Effect where
11+ Args : { () } System (List String)
12+ Time : { () } System Int
13+ GetEnv : String -> { () } System (Maybe String)
14+
15+instance Handler System IO where
16+ handle () Args k = do x <- getArgs; k x ()
17+ handle () Time k = do x <- time; k x ()
18+ handle () (GetEnv s) k = do x <- getEnv s; k x ()
19+
20+instance Handler System (IOExcept a) where
21+ handle () Args k = do x <- ioe_lift getArgs; k x ()
22+ handle () Time k = do x <- ioe_lift time; k x ()
23+ handle () (GetEnv s) k = do x <- ioe_lift $ getEnv s; k x ()
24+
25+--- The Effect and associated functions
26+
27+SYSTEM : EFFECT
28+SYSTEM = MkEff () System
29+
30+getArgs : { [SYSTEM] } Eff (List String)
31+getArgs = call Args
32+
33+time : { [SYSTEM] } Eff Int
34+time = call Time
35+
36+getEnv : String -> { [SYSTEM] } Eff (Maybe String)
37+getEnv s = call $ GetEnv s
38+
libs/neweffects/Effects.idrnew file+292
@@ -0,0 +1,292 @@
1+module Effects
2+
3+import Language.Reflection
4+import Control.Catchable
5+import Effect.Default
6+
7+--- Effectful computations are described as algebraic data types that
8+--- explain how an effect is interpreted in some underlying context.
9+
10+%access public
11+-- ----------------------------------------------------------------- [ Effects ]
12+||| The Effect type describes effectful computations.
13+|||
14+||| This type is parameterised by:
15+||| + The input resource.
16+||| + The return type of the computation.
17+||| + The computation to run on the resource.
18+Effect : Type
19+Effect = (x : Type) -> Type -> (x -> Type) -> Type
20+
21+||| The `EFFECT` Data type describes how to promote the Effect
22+||| description into a concrete effect.
23+%error_reverse
24+data EFFECT : Type where
25+ MkEff : Type -> Effect -> EFFECT
26+
27+||| Handler classes describe how an effect `e` is translated to the
28+||| underlying computation context `m` for execution.
29+class Handler (e : Effect) (m : Type -> Type) where
30+ ||| How to handle the effect.
31+ |||
32+ ||| @ r The resource being handled.
33+ ||| @ eff The effect to be applied.
34+ ||| @ k The continuation to pass the result of the effect
35+ covering handle : (r : res) -> (eff : e t res resk) ->
36+ (k : ((x : t) -> resk x -> m a)) -> m a
37+
38+||| Get the resource type (handy at the REPL to find out about an effect)
39+resourceType : EFFECT -> Type
40+resourceType (MkEff t e) = t
41+
42+-- --------------------------------------------------------- [ Syntactic Sugar ]
43+
44+-- A bit of syntactic sugar ('syntax' is not very flexible so we only go
45+-- up to a small number of parameters...)
46+
47+-- No state transition
48+syntax "{" [inst] "}" [eff] = eff inst (\result => inst)
49+
50+-- The state transition is dependent on a result `b`, a bound variable.
51+syntax "{" [inst] "==>" "{" {b} "}" [outst] "}" [eff]
52+ = eff inst (\b => outst)
53+
54+--- A simple state transition
55+syntax "{" [inst] "==>" [outst] "}" [eff] = eff inst (\result => outst)
56+
57+-- --------------------------------------- [ Properties and Proof Construction ]
58+using (xs : List a, ys : List a)
59+ data SubList : List a -> List a -> Type where
60+ SubNil : SubList {a} [] []
61+ Keep : SubList xs ys -> SubList (x :: xs) (x :: ys)
62+ Drop : SubList xs ys -> SubList xs (x :: ys)
63+
64+ subListId : SubList xs xs
65+ subListId {xs = Nil} = SubNil
66+ subListId {xs = x :: xs} = Keep subListId
67+
68+namespace Env
69+ data Env : (m : Type -> Type) -> List EFFECT -> Type where
70+ Nil : Env m Nil
71+ (::) : Handler eff m => a -> Env m xs -> Env m (MkEff a eff :: xs)
72+
73+data EffElem : Effect -> Type ->
74+ List EFFECT -> Type where
75+ Here : EffElem x a (MkEff a x :: xs)
76+ There : EffElem x a xs -> EffElem x a (y :: xs)
77+
78+||| make an environment corresponding to a sub-list
79+dropEnv : Env m ys -> SubList xs ys -> Env m xs
80+dropEnv [] SubNil = []
81+dropEnv (v :: vs) (Keep rest) = v :: dropEnv vs rest
82+dropEnv (v :: vs) (Drop rest) = dropEnv vs rest
83+
84+updateWith : (ys' : List a) -> (xs : List a) ->
85+ SubList ys xs -> List a
86+updateWith (y :: ys) (x :: xs) (Keep rest) = y :: updateWith ys xs rest
87+updateWith ys (x :: xs) (Drop rest) = x :: updateWith ys xs rest
88+updateWith [] [] SubNil = []
89+updateWith (y :: ys) [] SubNil = y :: ys
90+updateWith [] (x :: xs) (Keep rest) = []
91+
92+||| Put things back, replacing old with new in the sub-environment
93+rebuildEnv : Env m ys' -> (prf : SubList ys xs) ->
94+ Env m xs -> Env m (updateWith ys' xs prf)
95+rebuildEnv [] SubNil env = env
96+rebuildEnv (x :: xs) SubNil env = x :: xs
97+rebuildEnv [] (Keep rest) (y :: env) = []
98+rebuildEnv (x :: xs) (Keep rest) (y :: env) = x :: rebuildEnv xs rest env
99+rebuildEnv xs (Drop rest) (y :: env) = y :: rebuildEnv xs rest env
100+
101+
102+-- -------------------------------------------------- [ The Effect EDSL itself ]
103+
104+updateResTy : (val : t) ->
105+ (xs : List EFFECT) -> EffElem e a xs -> e t a b ->
106+ List EFFECT
107+updateResTy {b} val (MkEff a e :: xs) Here n = (MkEff (b val) e) :: xs
108+updateResTy val (x :: xs) (There p) n = x :: updateResTy val xs p n
109+
110+infix 5 :::, :-, :=
111+
112+data LRes : lbl -> Type -> Type where
113+ (:=) : (x : lbl) -> res -> LRes x res
114+
115+(:::) : lbl -> EFFECT -> EFFECT
116+(:::) {lbl} x (MkEff r e) = MkEff (LRes x r) e
117+
118+using (lbl : Type)
119+ instance Default a => Default (LRes lbl a) where
120+ default = lbl := default
121+
122+private
123+unlabel : {l : ty} -> Env m [l ::: x] -> Env m [x]
124+unlabel {m} {x = MkEff a eff} [l := v] = [v]
125+
126+private
127+relabel : (l : ty) -> Env m xs -> Env m (map (\x => l ::: x) xs)
128+relabel {xs = []} l [] = []
129+relabel {xs = (MkEff a e :: xs)} l (v :: vs) = (l := v) :: relabel l vs
130+
131+-- ------------------------------------------------- [ The Language of Effects ]
132+||| Definition of a language of effectful programs.
133+|||
134+||| @ x The return type of the result.
135+||| @ es The list of allowed side-effects.
136+||| @ ce Function to compute a new list of allowed side-effects.
137+data Eff : (x : Type)
138+ -> (es : List EFFECT)
139+ -> (ce : x -> List EFFECT) -> Type where
140+ value : a -> Eff a xs (\v => xs)
141+ with_val : (val : a) -> Eff () xs (\v => xs' val) ->
142+ Eff a xs xs'
143+ ebind : Eff a xs xs' ->
144+ ((val : a) -> Eff b (xs' val) xs'') -> Eff b xs xs''
145+ callP : (prf : EffElem e a xs) ->
146+ (eff : e t a b) ->
147+ Eff t xs (\v => updateResTy v xs prf eff)
148+
149+ liftP : (prf : SubList ys xs) ->
150+ Eff t ys ys' -> Eff t xs (\v => updateWith (ys' v) xs prf)
151+
152+ (:-) : (l : ty) ->
153+ Eff t [x] xs' -> -- [x] (\v => xs) ->
154+ Eff t [l ::: x] (\v => map (l :::) (xs' v))
155+
156+(>>=) : Eff a xs xs' ->
157+ ((val : a) -> Eff b (xs' val) xs'') -> Eff b xs xs''
158+(>>=) = ebind
159+
160+-- namespace SimpleBind
161+-- (>>=) : Eff m a xs (\v => xs) ->
162+-- ((val : a) -> Eff m b xs xs') -> Eff m b xs xs'
163+-- (>>=) = ebind
164+
165+||| Run a subprogram which results in an effect state the same as the input.
166+staticEff : Eff a xs (\v => xs) -> Eff a xs (\v => xs)
167+staticEff = id
168+
169+||| Explicitly give the expected set of result effects for an effectful
170+||| operation.
171+toEff : .(xs' : List EFFECT) -> Eff a xs (\v => xs') -> Eff a xs (\v => xs')
172+toEff xs' = id
173+
174+return : a -> Eff a xs (\v => xs)
175+return x = value x
176+
177+-- ------------------------------------------------------ [ for idiom brackets ]
178+
179+infixl 2 <$>
180+
181+pure : a -> Eff a xs (\v => xs)
182+pure = value
183+
184+syntax pureM [val] = with_val val (pure ())
185+
186+(<$>) : Eff (a -> b) xs (\v => xs) ->
187+ Eff a xs (\v => xs) -> Eff b xs (\v => xs)
188+(<$>) prog v = do fn <- prog
189+ arg <- v
190+ return (fn arg)
191+
192+-- ---------------------------------------------------------- [ an interpreter ]
193+
194+private
195+execEff : Env m xs -> (p : EffElem e res xs) ->
196+ (eff : e a res resk) ->
197+ ((v : a) -> Env m (updateResTy v xs p eff) -> m t) -> m t
198+execEff (val :: env) Here eff' k
199+ = handle val eff' (\v, res => k v (res :: env))
200+-- FIXME: Teach the elaborator to propagate parameters here
201+execEff {e} {a} {res} {resk} (val :: env) (There p) eff k
202+ = execEff {e} {a} {res} {resk} env p eff (\v, env' => k v (val :: env'))
203+
204+-- Q: Instead of m b, implement as StateT (Env m xs') m b, so that state
205+-- updates can be propagated even through failing computations?
206+
207+eff : Env m xs -> Eff a xs xs' -> ((x : a) -> Env m (xs' x) -> m b) -> m b
208+eff env (value x) k = k x env
209+eff env (with_val x prog) k = eff env prog (\p', env' => k x env')
210+eff env (prog `ebind` c) k
211+ = eff env prog (\p', env' => eff env' (c p') k)
212+eff env (callP prf effP) k = execEff env prf effP k
213+eff env (liftP prf effP) k
214+ = let env' = dropEnv env prf in
215+ eff env' effP (\p', envk => k p' (rebuildEnv envk prf env))
216+-- FIXME:
217+-- xs is needed explicitly because otherwise the pattern binding for
218+-- 'l' appears too late. Solution seems to be to reorder patterns at the
219+-- end so that everything is in scope when it needs to be.
220+eff {xs = [l ::: x]} env (l :- prog) k
221+ = let env' = unlabel env in
222+ eff env' prog (\p', envk => k p' (relabel l envk))
223+
224+-- yuck :) Haven't got type class instances working nicely in tactic
225+-- proofs yet, and 'search' can't be told about any hints yet,
226+-- so just brute force it.
227+syntax MkDefaultEnv = with Env
228+ (| [], [default], [default, default],
229+ [default, default, default],
230+ [default, default, default, default],
231+ [default, default, default, default, default],
232+ [default, default, default, default, default, default],
233+ [default, default, default, default, default, default, default],
234+ [default, default, default, default, default, default, default, default] |)
235+
236+call : {a, b: _} -> {e : Effect} ->
237+ (eff : e t a b) ->
238+ {default tactics { search 100; }
239+ prf : EffElem e a xs} ->
240+ Eff t xs (\v => updateResTy v xs prf eff)
241+call e {prf} = callP prf e
242+
243+implicit
244+lift : Eff t ys ys' ->
245+ {default tactics { search 100; }
246+ prf : SubList ys xs} ->
247+ Eff t xs (\v => updateWith (ys' v) xs prf)
248+lift e {prf} = liftP prf e
249+
250+
251+-- --------------------------------------------------------- [ Running Effects ]
252+
253+||| Run an effectful program
254+run : Applicative m => {default MkDefaultEnv env : Env m xs} -> Eff a xs xs' -> m a
255+run {env} prog = eff env prog (\r, env => pure r)
256+
257+runPure : {default MkDefaultEnv env : Env id xs} -> Eff a xs xs' -> a
258+runPure {env} prog = eff env prog (\r, env => r)
259+
260+runInit : Applicative m => Env m xs -> Eff a xs xs' -> m a
261+runInit env prog = eff env prog (\r, env => pure r)
262+
263+runPureInit : Env id xs -> Eff a xs xs' -> a
264+runPureInit env prog = eff env prog (\r, env => r)
265+
266+runWith : (a -> m a) -> Env m xs -> Eff a xs xs' -> m a
267+runWith inj env prog = eff env prog (\r, env => inj r)
268+
269+runEnv : Applicative m => Env m xs -> Eff a xs xs' ->
270+ m (x : a ** Env m (xs' x))
271+runEnv env prog = eff env prog (\r, env => pure (r ** env))
272+
273+-- ----------------------------------------------- [ some higher order things ]
274+
275+mapE : Applicative m => (a -> {xs} Eff b) -> List a -> {xs} Eff (List b)
276+mapE f [] = pure []
277+mapE f (x :: xs) = [| f x :: mapE f xs |]
278+
279+
280+mapVE : Applicative m =>
281+ (a -> {xs} Eff b) ->
282+ Vect n a ->
283+ {xs} Eff (Vect n b)
284+mapVE f [] = pure []
285+mapVE f (x :: xs) = [| f x :: mapVE f xs |]
286+
287+
288+when : Applicative m => Bool -> Lazy ({xs} Eff ()) -> {xs} Eff ()
289+when True e = Force e
290+when False e = pure ()
291+
292+-- --------------------------------------------------------------------- [ EOF ]
libs/neweffects/Makefilenew file+14
@@ -0,0 +1,14 @@
1+IDRIS := idris
2+
3+build:
4+ $(IDRIS) --build effects.ipkg
5+
6+clean:
7+ $(IDRIS) --clean effects.ipkg
8+
9+install:
10+ $(IDRIS) --install effects.ipkg
11+
12+rebuild: clean build
13+
14+.PHONY: build clean install rebuild
libs/neweffects/effects.ipkgnew file+9
@@ -0,0 +1,9 @@
1+package neweffects
2+
3+opts = "--nobasepkgs -i ../prelude -i ../base"
4+modules = Effects, Effect.Default,
5+
6+ Effect.Exception, Effect.File, Effect.State,
7+ Effect.Random, Effect.StdIO, Effect.Select,
8+ Effect.Memory, Effect.System
9+