on master help sign in
Bring effects tutorial up to date

Remove deprecated syntactic sugar, and an up to date version of pureM.

commit2ac00791ff07d4ad94746ae3a83e10bdab9079f8 authorEdwin Brady <ecb10@st-andrews.ac.uk> dateFri 3 Jul 2015, 12:57 (11 years ago) parent6e7f896
6 files changed+225−208
docs/effects/depeff.rst+44−39 docs/effects/hangman.rst+27−30 docs/effects/impleff.rst+62−51 docs/effects/introduction.rst+4−8 docs/effects/simpleeff.rst+33−33 docs/effects/state.rst+55−47
docs/effects/depeff.rst+44−39
@@ -24,7 +24,7 @@ It might look something like the following:
2424
2525.. code-block:: idris
2626
27− readInt : { [STATE (List Int), STDIO] } Eff ()
27+ readInt : Eff () [STATE (List Int), STDIO]
2828 readInt = do let x = trim !getStr
2929 put (cast x :: !get)
3030
@@ -33,7 +33,7 @@ But what if, instead of a list of integers, we would like to store a
3333
3434.. code-block:: idris
3535
36− readInt : { [STATE (Vect n Int), STDIO] } Eff ()
36+ readInt : Eff () [STATE (Vect n Int), STDIO]
3737 readInt = do let x = trim !getStr
3838 put (cast x :: !get)
3939
@@ -43,8 +43,8 @@ express this as follows:
4343
4444.. code-block:: idris
4545
46− readInt : { [STATE (Vect n Int), STDIO] ==>
47− [STATE (Vect (S n) Int), STDIO] } Eff ()
46+ readInt : Eff ()[STATE (Vect n Int), STDIO]
47+ [STATE (Vect (S n) Int), STDIO]
4848 readInt = do let x = trim !getStr
4949 putM (cast x :: !get)
5050
@@ -56,7 +56,7 @@ It has the following type:
5656
5757.. code-block:: idris
5858
59− putM : y -> { [STATE x] ==> [STATE y] } Eff ()
59+ putM : y -> Eff () [STATE x] [STATE y]
6060
6161Result-dependent Effects
6262========================
@@ -69,8 +69,8 @@ digits). As a first attempt, we could try the following, returning a
6969
7070.. code-block:: idris
7171
72− readInt : { [STATE (Vect n Int), STDIO] ==>
73− [STATE (Vect (S n) Int), STDIO] } Eff Bool
72+ readInt : Eff Bool [STATE (Vect n Int), STDIO]
73+ [STATE (Vect (S n) Int), STDIO]
7474 readInt = do let x = trim !getStr
7575 case all isDigit (unpack x) of
7676 False => pure False
@@ -91,31 +91,31 @@ value read from the user was valid. We can express this in the type:
9191
9292.. code-block:: idris
9393
94− readInt : { [STATE (Vect n Int), STDIO] ==>
95− {ok} if ok then [STATE (Vect (S n) Int), STDIO]
96− else [STATE (Vect n Int), STDIO] } Eff Bool
94+ readInt : Eff Bool [STATE (Vect n Int), STDIO]
95+ (\ok => if ok then [STATE (Vect (S n) Int), STDIO]
96+ else [STATE (Vect n Int), STDIO])
9797 readInt = do let x = trim !getStr
9898 case all isDigit (unpack x) of
9999 False => pure False
100100 True => do putM (cast x :: !get)
101− pure True
101+ pureM True
102+
103+Using ``pureM`` rather than ``pure`` allows the output effects to be
104+calculated from the value given. Its type is:
105+
106+.. code-block:: idris
102107
103−The notation ``{ xs ==> res xs’ } Eff a`` in a type means that the
104−effects available are updated from ``xs`` to ``xs’``, *and* the
105−resulting effects ``xs’`` may depend on the result of the operation
106−``res``, of type ``a``. Here, the resulting effects are computed from
107−the result ``ok``—if ``True``, the vector is extended, otherwise it
108−remains the same.
108+ pureM : (val : a) -> EffM m a (xs val) xs
109109
110−When using the function, we will naturally have to check its return
110+When using ``readInt``, we will have to check its return
111111value in order to know what the new set of effects is. For example, to
112112read a set number of values into a vector, we could write the following:
113113
114114.. code-block:: idris
115115
116116 readN : (n : Nat) ->
117− { [STATE (Vect m Int), STDIO] ==>
118− [STATE (Vect (n + m) Int), STDIO] } Eff ()
117+ Eff () [STATE (Vect m Int), STDIO]
118+ [STATE (Vect (n + m) Int), STDIO]
119119 readN Z = pure ()
120120 readN {m} (S k) = case !readInt of
121121 True => rewrite plusSuccRightSucc k m in readN k
@@ -171,14 +171,17 @@ to support random access files and better reporting of error conditions.
171171
172172 data OpenFile : Mode -> Type
173173
174− open : String -> (m : Mode) ->
175− { [FILE_IO ()] ==>
176− {ok} [FILE_IO (if ok then OpenFile m else ())] } Eff Bool
177− close : { [FILE_IO (OpenFile m)] ==> [FILE_IO ()] } Eff ()
174+ open : (fname : String)
175+ -> (m : Mode)
176+ -> Eff Bool [FILE_IO ()]
177+ (\res => [FILE_IO (case res of
178+ True => OpenFile m
179+ False => ())])
180+ close : Eff () [FILE_IO (OpenFile m)] [FILE_IO ()]
178181
179− readLine : { [FILE_IO (OpenFile Read)] } Eff String
180− writeLine : { [FILE_IO (OpenFile Write)] } Eff ()
181− eof : { [FILE_IO (OpenFile Read)] } Eff Bool
182+ readLine : Eff String [FILE_IO (OpenFile Read)]
183+ writeLine : String -> Eff () [FILE_IO (OpenFile Write)]
184+ eof : Eff Bool [FILE_IO (OpenFile Read)]
182185
183186 instance Handler FileIO IO
184187
@@ -186,9 +189,12 @@ In particular, consider the type of ``open``:
186189
187190.. code-block:: idris
188191
189− open : String -> (m : Mode) ->
190− { [FILE_IO ()] ==>
191− {ok} [FILE_IO (if ok then OpenFile m else ())] } Eff Bool
192+ open : (fname : String)
193+ -> (m : Mode)
194+ -> Eff Bool [FILE_IO ()]
195+ (\res => [FILE_IO (case res of
196+ True => OpenFile m
197+ False => ())])
192198
193199This returns a ``Bool`` which indicates whether opening the file was
194200successful. The resulting state depends on whether the operation was
@@ -199,10 +205,9 @@ we continue the protocol accordingly.
199205.. _eff-readfile:
200206.. code-block:: idris
201207
202− readFile : { [FILE_IO (OpenFile Read)] } Eff (List String)
208+ readFile : Eff (List String) [FILE_IO (OpenFile Read)]
203209 readFile = readAcc [] where
204− readAcc : List String -> { [FILE_IO (OpenFile Read)] }
205− Eff (List String)
210+ readAcc : List String -> Eff (List String) [FILE_IO (OpenFile Read)]
206211 readAcc acc = if (not !eof)
207212 then readAcc (!readLine :: acc)
208213 else pure (reverse acc)
@@ -214,7 +219,7 @@ correctly following the protocol:
214219
215220.. code-block:: idris
216221
217− dumpFile : String -> { [FILE_IO (), STDIO] } Eff ()
222+ dumpFile : String -> Eff () [FILE_IO (), STDIO]
218223 dumpFile name = case !(open name Read) of
219224 True => do putStrLn (show !readFile)
220225 close
@@ -255,7 +260,7 @@ Idris supports *pattern-matching* bindings, such as the following:
255260
256261.. code-block:: idris
257262
258− dumpFile : String -> { [FILE_IO (), STDIO] } Eff ()
263+ dumpFile : String -> Eff () [FILE_IO (), STDIO]
259264 dumpFile name = do True <- open name Read
260265 putStrLn (show !readFile)
261266 close
@@ -267,7 +272,7 @@ example, we could write:
267272
268273.. code-block:: idris
269274
270− dumpFile : String -> { [FILE_IO (), STDIO] } Eff ()
275+ dumpFile : String -> Eff () [FILE_IO (), STDIO]
271276 dumpFile name = do True <- open name Read | False => putStrLn "Error"
272277 putStrLn (show !readFile)
273278 close
@@ -297,7 +302,7 @@ reading command line arguments, among other things (see Appendix
297302
298303.. code-block:: idris
299304
300− getArgs : { [SYSTEM] } Eff (List String)
305+ getArgs : Eff (List String) [SYSTEM]
301306
302307A main program can read command line arguments as follows, where in the
303308list which is returned, the first element ``prog`` is the executable
@@ -305,7 +310,7 @@ name and the second is an expected argument:
305310
306311.. code-block:: idris
307312
308− emain : { [SYSTEM, STDIO] } Eff ()
313+ emain : Eff () [SYSTEM, STDIO]
309314 emain = do [prog, arg] <- getArgs
310315 putStrLn $ "Argument is " ++ arg
311316 {- ... rest of function ... -}
@@ -316,7 +321,7 @@ alternatives to give a better (more informative) error:
316321
317322.. code-block:: idris
318323
319− emain : { [SYSTEM, STDIO] } Eff ()
324+ emain : Eff () [SYSTEM, STDIO]
320325 emain = do [prog, arg] <- getArgs | [] => putStrLn "Can't happen!"
321326 | [prog] => putStrLn "No arguments!"
322327 | _ => putStrLn "Too many arguments!"
docs/effects/hangman.rst+27−30
@@ -95,7 +95,7 @@ NewWord
9595 choice of 6 is somewhat arbitrary here) and the number of unique
9696 letters in the word still to be guessed.
9797
98−StrState
98+Get
9999 Get a string representation of the game state. This is for display
100100 purposes; there are no pre- or postconditions.
101101
@@ -106,16 +106,19 @@ effect signature:
106106
107107 data MysteryRules : Effect where
108108 Guess : (x : Char) ->
109− { Mystery (Running (S g) (S w)) ==>
110− {inword} if inword then Mystery (Running (S g) w)
111− else Mystery (Running g (S w)) }
112− MysteryRules Bool
113− Won : { Mystery (Running g 0) ==> Mystery NotRunning } MysteryRules ()
114− Lost : { Mystery (Running 0 g) ==> Mystery NotRunning } MysteryRules ()
115− NewWord : (w : String) ->
116− { Mystery NotRunning ==>
117− Mystery (Running 6 (length (letters w))) } MysteryRules ()
118− StrState : { Mystery h } MysteryRules String
109+ sig MysteryRules Bool
110+ (Mystery (Running (S g) (S w)))
111+ (\inword =>
112+ Mystery (case inword of
113+ True => (Running (S g) w)
114+ False => (Running g (S w))))
115+ Won : sig MysteryRules () (Mystery (Running g 0))
116+ (Mystery NotRunning)
117+ Lost : sig MysteryRules () (Mystery (Running 0 g))
118+ (Mystery NotRunning)
119+ NewWord : (w : String) ->
120+ sig MysteryRules () h (Mystery (Running 6 (length (letters w))))
121+ Get : sig MysteryRules String (Mystery h)
119122
120123This description says nothing about how the rules are implemented. In
121124particular, it does not specify *how* to tell whether a guessed letter
@@ -195,7 +198,7 @@ implementation for ``MysteryRules`` is surprisingly straightforward:
195198 handle (MkG w g got []) Won k = k () (GameWon w)
196199 handle (MkG w Z got m) Lost k = k () (GameLost w)
197200
198− handle st StrState k = k (show st) st
201+ handle st Get k = k (show st) st
199202 handle st (NewWord w) k = k () (initState w)
200203
201204 handle (MkG w (S g) got m) (Guess x) k =
@@ -219,8 +222,8 @@ for the game which uses the ``MYSTERY`` effect:
219222
220223.. code-block:: idris
221224
222− game : { [MYSTERY (Running (S g) w), STDIO] ==>
223− [MYSTERY NotRunning, STDIO] } Eff ()
225+ game : Eff () [MYSTERY (Running (S g) w), STDIO]
226+ [MYSTERY NotRunning, STDIO]
224227
225228The type indicates that the game must start in a running state, with
226229some guesses available, and eventually reach a not-running state (i.e.
@@ -239,12 +242,12 @@ running ``game``:
239242
240243.. code-block:: idris
241244
242− runGame : { [MYSTERY NotRunning, RND, SYSTEM, STDIO] } Eff ()
245+ runGame : Eff () [MYSTERY NotRunning, RND, SYSTEM, STDIO]
243246 runGame = do srand (cast !time)
244247 let w = index !(rndFin WEWEWE) words
245248 NewWord w
246249 game
247− putStrLn !StrState
250+ putStrLn !Get
248251
249252We use the system time (``time`` from the ``SYSTEM`` effect; see
250253Appendix :ref:`sect-appendix`) to initialise the random number
@@ -271,11 +274,11 @@ presented below:
271274
272275.. code-block:: idris
273276
274− game : { [MYSTERY (Running (S g) w), STDIO] ==>
275− [MYSTERY NotRunning, STDIO] } Eff ()
277+ game : Eff () [MYSTERY (Running (S g) w), STDIO]
278+ [MYSTERY NotRunning, STDIO]
276279 game {w=Z} = Won
277280 game {w=S _}
278− = do putStrLn !StrState
281+ = do putStrLn !Get
279282 putStr "Enter guess: "
280283 let guess = trim !getStr
281284 case choose (not (guess == "")) of
@@ -283,9 +286,8 @@ presented below:
283286 (Right p) => do putStrLn "Invalid input!"
284287 game
285288 where
286− processGuess : Char -> { [MYSTERY (Running (S g) (S w)), STDIO] ==>
287− [MYSTERY NotRunning, STDIO] }
288− Eff ()
289+ processGuess : Char -> Eff () [MYSTERY (Running (S g) (S w)), STDIO]
290+ [MYSTERY NotRunning, STDIO]
289291 processGuess {g} {w} c
290292 = case !(Main.Guess c) of
291293 True => do putStrLn "Good guess!"
@@ -315,16 +317,11 @@ transitions for ``MysteryRules``. Then, when implementing ``game`` at
315317first, any incorrect assumption was caught as a type error. The
316318following errors were caught during development:
317319
318−- Not realising that allowing ``NewWord`` to be an arbitrary string
319− would mean that ``game`` would have to deal with a zero-length word
320− as a starting state.
320+- Not realising that allowing ``NewWord`` to be an arbitrary string would mean that ``game`` would have to deal with a zero-length word as a starting state.
321321
322−- Forgetting to check whether a game was won before recursively
323− calling ``processGuess``, thus accidentally continuing a finished
324− game.
322+- Forgetting to check whether a game was won before recursively calling ``processGuess``, thus accidentally continuing a finished game.
325323
326−- Accidentally checking the number of missing letters, rather than the
327− number of remaining guesses, when checking if a game was lost.
324+- Accidentally checking the number of missing letters, rather than the number of remaining guesses, when checking if a game was lost.
328325
329326These are, of course, simple errors, but were caught by the type
330327checker before any testing of the game.
docs/effects/impleff.rst+62−51
@@ -23,24 +23,11 @@ available. Stateful operations are described as follows:
2323.. code-block:: idris
2424
2525 data State : Effect where
26− Get : { a } State a
27− Put : b -> { a ==> b } State ()
26+ Get : State a a (\x => a)
27+ Put : b -> State () a (\x => b)
2828
29−Each effect is associated with a *resource*, the type of which is
30−given with the notation ``{ x ==> x’ }``. This notation gives the
31−resource type expected by each operation, and how it updates when the
32−operation is run. Here, it means:
33−
34−- ``Get`` takes no arguments. It has a resource of type ``a``, which
35− is not updated, and running the ``Get`` operation returns something
36− of type ``a``.
37−
38−- ``Put`` takes a ``b`` as an argument. It has a resource of type
39− ``a`` on input, which is updated to a resource of type
40− ``b``. Running the ``Put`` operation returns the element of the
41− unit type.
42−
43−``Effect`` itself is a type synonym, declared as follows:
29+``Effect`` itself is a type synonym, giving the required type for an
30+effect signature:
4431
4532.. code-block:: idris
4633
@@ -49,18 +36,41 @@ operation is run. Here, it means:
4936 (input_resource : Type) ->
5037 (output_resource : result -> Type) -> Type
5138
52−That is, an effectful operation returns something of type ``result``,
53−has an input resource of type ``input_resource``, and a function
54−``output_resource`` which computes the output resource type from the
55−result. We use the same syntactic sugar as with ``Eff`` to make effect
56−declarations more readable. It is defined as follows in the library:
39+Each effect is associated with a *resource*. The second argument to
40+an effect signature is the resource type on *input* to an operation,
41+and the third is a function which computes the resource type on
42+*output*. Here, it means:
43+
44+- ``Get`` takes no arguments. It has a resource of type ``a``, which is not updated, and running the ``Get`` operation returns something of type ``a``.
45+
46+- ``Put`` takes a ``b`` as an argument. It has a resource of type ``a`` on input, which is updated to a resource of type ``b``. Running the ``Put`` operation returns the element of the unit type.
47+
48+The effects library provides an overloaded function ``sig``
49+which can make effect signatures more concise, particularly when the
50+result has no effect on the resource type. For ``State``, we can
51+write:
5752
5853.. code-block:: idris
5954
60− syntax "{" [inst] "}" [eff] = eff inst (\result => inst)
61− syntax "{" [inst] "==>" "{" {b} "}" [outst] "}" [eff]
62− = eff inst (\b => outst)
63− syntax "{" [inst] "==>" [outst] "}" [eff] = eff inst (\result => outst)
55+ data State : Effect where
56+ Get : sig State a a
57+ Put : b -> sig State () a b
58+
59+There are four versions of ``sig``, depending on whether we
60+are interested in the resource type, and whether we are updating the
61+resource. Idris will infer the appropriate version from usage.
62+
63+.. code-block:: idris
64+
65+ NoResourceEffect.sig : Effect -> Type -> Type
66+ NoUpdateEffect.sig : Effect -> (ret : Type) ->
67+ (resource : Type) -> Type
68+ UpdateEffect.sig : Effect -> (ret : Type) ->
69+ (resource_in : Type) ->
70+ (resource_out : Type) -> Type
71+ DepUpdateEffect.sig : Effect -> (ret : Type) ->
72+ (resource_in : Type) ->
73+ (resource_out : ret -> Type) -> Type
6474
6575In order to convert ``State`` (of type ``Effect``) into something
6676usable in an effects list, of type ``EFFECT``, we write the following:
@@ -96,13 +106,11 @@ summaries in Section :ref:`sect-simpleff`. An instance of ``Handler e
96106m`` means that the effect declared with signature ``e`` can be run in
97107computation context ``m``. The ``handle`` function takes:
98108
99−- The ``resource`` on input (so, the current value of the state for
100− ``State``)
109+- The ``resource`` on input (so, the current value of the state for ``State``)
101110
102111- The effectful operation (either ``Get`` or ``Put x`` for ``State``)
103112
104−- A *continuation*, which we conventionally call ``k``, and should be
105− passed the result value of the operation, and an updated resource.
113+- A *continuation*, which we conventionally call ``k``, and should be passed the result value of the operation, and an updated resource.
106114
107115There are two reasons for taking a continuation here: firstly, this is
108116neater because there are multiple return values (a new resource and
@@ -125,13 +133,13 @@ functions in ``Eff``, as follows:
125133
126134.. code-block:: idris
127135
128− get : { [STATE x] } Eff x
136+ get : Eff x [STATE x]
129137 get = call Get
130138
131− put : x -> { [STATE x] } Eff ()
139+ put : x -> Eff () [STATE x]
132140 put val = call (Put val)
133141
134− putM : y -> { [STATE x] ==> [STATE y] } Eff ()
142+ putM : y -> Eff () [STATE x] [STATE y]
135143 putM val = call (Put val)
136144
137145**An implementation detail (aside):** The ``call`` function converts
@@ -162,8 +170,8 @@ The following listing summarises what is required to define the
162170.. code-block:: idris
163171
164172 data State : Effect where
165− Get : { a } State a
166− Put : b -> { a ==> b } State ()
173+ Get : sig State a a
174+ Put : b -> sig State () a b
167175
168176 STATE : Type -> EFFECT
169177 STATE t = MkEff t State
@@ -172,13 +180,13 @@ The following listing summarises what is required to define the
172180 handle st Get k = k st st
173181 handle st (Put n) k = k () n
174182
175− get : { [STATE x] } Eff x
183+ get : Eff x [STATE x]
176184 get = call Get
177185
178− put : x -> { [STATE x] } Eff ()
186+ put : x -> Eff () [STATE x]
179187 put val = call (Put val)
180188
181− putM : y -> { [STATE x] ==> [STATE y] } Eff ()
189+ putM : y -> Eff () [STATE x] [STATE y]
182190 putM val = call (Put val)
183191
184192
@@ -198,10 +206,10 @@ directly.
198206.. code-block:: idris
199207
200208 data StdIO : Effect where
201− PutStr : String -> { () } StdIO ()
202− GetStr : { () } StdIO String
203− PutCh : Char -> { () } StdIO ()
204− GetCh : { () } StdIO Char
209+ PutStr : String -> sig StdIO ()
210+ GetStr : sig StdIO String
211+ PutCh : Char -> sig StdIO ()
212+ GetCh : sig StdIO Char
205213
206214 instance Handler StdIO IO where
207215 handle () (PutStr s) k = do putStr s; k () ()
@@ -231,7 +239,7 @@ error.
231239.. code-block:: idris
232240
233241 data Exception : Type -> Effect where
234− Raise : a -> { () } Exception a b
242+ Raise : a -> sig (Exception a) b
235243
236244 instance Handler (Exception a) Maybe where
237245 handle _ (Raise e) k = Nothing
@@ -255,7 +263,7 @@ value.
255263.. code-block:: idris
256264
257265 data Selection : Effect where
258− Select : List a -> { () } Selection a
266+ Select : List a -> sig Selection a
259267
260268 instance Handler Selection Maybe where
261269 handle _ (Select xs) k = tryAll xs where
@@ -289,13 +297,16 @@ the equivalent ``Eff`` programs.
289297.. code-block:: idris
290298
291299 data FileIO : Effect where
292− Open : String -> (m : Mode) ->
293− {() ==> {res} if res then OpenFile m else ()} FileIO Bool
294− Close : {OpenFile m ==> ()} FileIO ()
295−
296− ReadLine : {OpenFile Read} FileIO String
297− WriteLine : String -> {OpenFile Write} FileIO ()
298− EOF : {OpenFile Read} FileIO Bool
300+ Open : (fname: String)
301+ -> (m : Mode)
302+ -> sig FileIO Bool () (\res => case res of
303+ True => OpenFile m
304+ False => ())
305+ Close : sig FileIO () (OpenFile m)
306+
307+ ReadLine : sig FileIO String (OpenFile Read)
308+ WriteLine : String -> sig FileIO () (OpenFile Write)
309+ EOF : sig FileIO Bool (OpenFile Read)
299310
300311 instance Handler FileIO IO where
301312 handle () (Open fname m) k = do h <- openFile fname m
docs/effects/introduction.rst+4−8
@@ -37,11 +37,9 @@ introductory tutorials, is “How do I turn this into a program?” That is,
3737given some lists entered by a user, how do we get into a position to be
3838able to apply the ``vadd`` function? Before doing so, we will have to:
3939
40−- Read user input, either from the keyboard, a file, or some other
41− input device.
40+- Read user input, either from the keyboard, a file, or some other input device.
4241
43−- Check that the user inputs are valid, i.e. contain only ``Int`` s
44− and are the same length, and report an error if not.
42+- Check that the user inputs are valid, i.e. contain only ``Int`` and are the same length, and report an error if not.
4543
4644- Write output
4745
@@ -66,7 +64,7 @@ library:
6664 import Effects
6765 import Effect.StdIO
6866
69− hello : {[STDIO]} Eff ()
67+ hello : Eff () [STDIO]
7068 hello = putStrLn “Hello world!”
7169
7270 main : IO ()
@@ -108,6 +106,4 @@ accurately. References to further reading are given in Section
108106:ref:`sect-further`.
109107
110108.. [1]
111− You do not, however, need to know what a monad is. A correctness
112− property of this tutorial is that the word “monad” should appear
113− exactly twice, both in this footnote.
109+ You do not, however, need to know what a monad is!
docs/effects/simpleeff.rst+33−33
@@ -14,10 +14,10 @@ in a module ``Effect.State``
1414
1515 STATE : Type -> EFFECT
1616
17− get : { [STATE x] } Eff x
18− put : x -> { [STATE x] } Eff ()
19− putM : y -> { [STATE x] ==> [STATE y] } Eff ()
20− update : (x -> x) -> { [STATE x] } Eff ()
17+ get : Eff x [STATE x]
18+ put : x -> Eff () [STATE x]
19+ putM : y -> Eff () [STATE x] [STATE y]
20+ update : (x -> x) -> Eff () [STATE x]
2121
2222 instance Handler State m
2323
@@ -56,12 +56,12 @@ can perform (or at the very least simulate) console I/O:
5656
5757 STDIO : EFFECT
5858
59− putChar : Char -> { [STDIO] } Eff ()
60− putStr : String -> { [STDIO] } Eff ()
61− putStrLn : String -> { [STDIO] } Eff ()
59+ putChar : Char -> Eff () [STDIO]
60+ putStr : String -> Eff () [STDIO]
61+ putStrLn : String -> Eff () [STDIO]
6262
63− getStr : { [STDIO] } Eff String
64− getChar : { [STDIO] } Eff Char
63+ getStr : Eff String [STDIO]
64+ getChar : Eff Char [STDIO]
6565
6666 instance Handler StdIO IO
6767 instance Handler StdIO (IOExcept a)
@@ -74,7 +74,7 @@ as follows:
7474
7575.. code-block:: idris
7676
77− hello : { [STDIO] } Eff ()
77+ hello : Eff () [STDIO]
7878 hello = do putStr "Name? "
7979 x <- getStr
8080 putStrLn ("Hello " ++ trim x ++ "!")
@@ -94,7 +94,7 @@ getStr``, since we only use the string that is read once:
9494
9595.. code-block:: idris
9696
97− hello : { [STDIO] } Eff ()
97+ hello : Eff () [STDIO]
9898 hello = do putStr "Name? "
9999 putStrLn ("Hello " ++ trim !getStr ++ "!")
100100
@@ -104,7 +104,7 @@ said hello to:
104104
105105.. code-block:: idris
106106
107− hello : { [STATE Int, STDIO] } Eff ()
107+ hello : Eff () [STATE Int, STDIO]
108108 hello = do putStr "Name? "
109109 putStrLn ("Hello " ++ trim !getStr ++ "!")
110110 update (+1)
@@ -146,7 +146,7 @@ generally:
146146
147147 EXCEPTION : Type -> EFFECT
148148
149− raise : a -> { [EXCEPTION a ] } Eff b
149+ raise : a -> Eff b [EXCEPTION a]
150150
151151 instance Handler (Exception a) Maybe
152152 instance Handler (Exception a) List
@@ -167,7 +167,7 @@ paramaterised by an error type:
167167
168168 data Err = NotANumber | OutOfRange
169169
170− parseNumber : Int -> String -> { [EXCEPTION Err] } Eff Int
170+ parseNumber : Int -> String -> Eff Int [EXCEPTION Err]
171171 parseNumber num str
172172 = if all isDigit (unpack str)
173173 then let x = cast str in
@@ -222,7 +222,7 @@ the boundedness proof:
222222
223223.. code-block:: idris
224224
225− parseNumber : (x : Int) -> String -> { [EXCEPTION Err] } Eff (Bounded x)
225+ parseNumber : (x : Int) -> String -> Eff (Bounded x) [EXCEPTION Err]
226226 parseNumber x str
227227 = if all isDigit (unpack str)
228228 then let num = cast str in
@@ -243,9 +243,9 @@ Random number generation is also implemented by the library, in module
243243
244244 RND : EFFECT
245245
246− srand : Integer -> { [RND] } Eff ()
247− rndInt : Integer -> Integer -> { [RND] } Eff Integer
248− rndFin : (k : Nat) -> { [RND] } Eff (Fin (S k))
246+ srand : Integer -> Eff () [RND]
247+ rndInt : Integer -> Integer -> Eff Integer [RND]
248+ rndFin : (k : Nat) -> Eff (Fin (S k)) [RND]
249249
250250 instance Handler Random m
251251
@@ -272,14 +272,14 @@ correct:
272272
273273.. code-block:: idris
274274
275− guess : Int -> { [STDIO] } Eff ()
275+ guess : Int -> Eff () [STDIO]
276276
277277For reference, the code for ``guess`` is given below:
278278
279279.. _eff-game:
280280.. code-block:: idris
281281
282− guess : Int -> { [STDIO] } Eff ()
282+ guess : Int -> Eff () [STDIO]
283283 guess target
284284 = do putStr "Guess: "
285285 case run {m=Maybe} (parseNumber 100 (trim !getStr)) of
@@ -303,7 +303,7 @@ having set up the random number generator with a seed, then run
303303
304304.. code-block:: idris
305305
306− game : { [RND, STDIO] } Eff ()
306+ game : Eff () [RND, STDIO]
307307 game = do srand 123456789
308308 guess (fromInteger !(rndInt 0 100))
309309
@@ -331,7 +331,7 @@ succeeds:
331331
332332 SELECT : EFFECT
333333
334− select : List a -> { [SELECT] } Eff a
334+ select : List a -> Eff a [SELECT]
335335
336336 instance Handler Selection Maybe
337337 instance Handler Selection List
@@ -346,7 +346,7 @@ of values which does not satisfy the constraint:
346346
347347.. code-block:: idris
348348
349− triple : Int -> { [SELECT, EXCEPTION String] } Eff (Int, Int, Int)
349+ triple : Int -> Eff (Int, Int, Int) [SELECT, EXCEPTION String]
350350 triple max = do z <- select [1..max]
351351 y <- select [1..z]
352352 x <- select [1..y]
@@ -396,7 +396,7 @@ they are equal:
396396.. code-block:: idris
397397
398398 vadd_check : Vect n Int -> Vect m Int ->
399− { [EXCEPTION String] } Eff (Vect m Int)
399+ Eff (Vect m Int) [EXCEPTION String]
400400 vadd_check {n} {m} xs ys with (decEq n m)
401401 vadd_check {n} {m=n} xs ys | (Yes Refl) = pure (vadd xs ys)
402402 vadd_check {n} {m} xs ys | (No contra) = raise "Length mismatch"
@@ -406,7 +406,7 @@ following type:
406406
407407.. code-block:: idris
408408
409− read_vec : { [STDIO] } Eff (p ** Vect p Int)
409+ read_vec : Eff (p ** Vect p Int) [STDIO]
410410
411411This returns a dependent pair of a length, and a vector of that
412412length, because we cannot know in advance how many integers the user
@@ -421,7 +421,7 @@ inputs are of differing lengths:
421421
422422.. code-block:: idris
423423
424− do_vadd : { [STDIO, EXCEPTION String] } Eff ()
424+ do_vadd : Eff () [STDIO, EXCEPTION String]
425425 do_vadd = do putStrLn "Vector 1"
426426 (_ ** xs) <- read_vec
427427 putStrLn "Vector 2"
@@ -436,7 +436,7 @@ with gracefully.
436436
437437.. code-block:: idris
438438
439− read_vec : { [STDIO] } Eff (p ** Vect p Int)
439+ read_vec : Eff (p ** Vect p Int) [STDIO]
440440 read_vec = do putStr "Number (-1 when done): "
441441 case run (parseNumber (trim !getStr)) of
442442 Nothing => do putStrLn "Input error"
@@ -446,7 +446,7 @@ with gracefully.
446446 pure (_ ** v :: xs)
447447 else pure (_ ** [])
448448 where
449− parseNumber : String -> { [EXCEPTION String] } Eff Int
449+ parseNumber : String -> Eff Int [EXCEPTION String]
450450 parseNumber str
451451 = if all (\x => isDigit x || x == '-') (unpack str)
452452 then pure (cast str)
@@ -493,7 +493,7 @@ values:
493493 Env : Type
494494 Env = List (String, Integer)
495495
496− eval : Expr -> { [EXCEPTION String, STATE Env] } Eff Integer
496+ eval : Expr -> Eff Integer [EXCEPTION String, STATE Env]
497497 eval (Val x) = return x
498498 eval (Add l r) = return $ !(eval l) + !(eval r)
499499
@@ -520,7 +520,7 @@ sets the state then invokes ``eval``:
520520
521521 runEval : List (String, Integer) -> Expr -> Maybe Integer
522522 runEval args expr = run (eval' expr)
523− where eval' : Expr -> { [EXCEPTION String, STATE Env] } Eff Integer
523+ where eval' : Expr -> Eff Integer [EXCEPTION String, STATE Env]
524524 eval' e = do put args
525525 eval e
526526
@@ -547,7 +547,7 @@ appears in the list, as long as it is present:
547547
548548.. code-block:: idris
549549
550− eval : Expr -> { [EXCEPTION String, RND, STATE Env] } Eff Integer
550+ eval : Expr -> Eff Integer [EXCEPTION String, RND, STATE Env]
551551
552552 eval (Random upper) = rndInt 0 upper
553553
@@ -575,7 +575,7 @@ this simply by updating the type of ``eval`` to include ``STDIO``.
575575
576576.. code-block:: idris
577577
578− eval : Expr -> { [STDIO, EXCEPTION String, RND, STATE Env] } Eff Integer
578+ eval : Expr -> Eff Integer [STDIO, EXCEPTION String, RND, STATE Env]
579579
580580Note that using ``STDIO`` will restrict the number of contexts in
581581which ``eval`` can be ``run`` to those which support ``STDIO``, such
@@ -587,6 +587,6 @@ types are first class in Idris:
587587.. code-block:: idris
588588
589589 EvalEff : Type -> Type
590− EvalEff t = { [STDIO, EXCEPTION String, RND, STATE Env] } Eff t
590+ EvalEff t = Eff t [STDIO, EXCEPTION String, RND, STATE Env]
591591
592592 eval : Expr -> EvalEff Integer
docs/effects/state.rst+55−47
@@ -83,7 +83,7 @@ effectful program ``f`` has a type of the following form:
8383
8484.. code-block:: idris
8585
86− f : (x1 : a1) -> (x2 : a2) -> ... -> { effs } Eff t
86+ f : (x1 : a1) -> (x2 : a2) -> ... -> Eff t effs
8787
8888That is, the return type gives the effects that ``f`` supports
8989(``effs``, of type ``List EFFECT``) and the type the computation
@@ -92,7 +92,7 @@ following type:
9292
9393.. code-block:: idris
9494
95− treeTagAux : BTree a -> { [STATE Int] } Eff (BTree (Int, a))
95+ treeTagAux : BTree a -> Eff (BTree (Int, a)) [STATE Int]
9696
9797That is, ``treeTagAux`` has access to an integer state, because the
9898list of available effects includes ``STATE Int``. ``STATE`` is
@@ -141,8 +141,8 @@ following types, polymorphic in the state ``t`` they manage:
141141
142142.. code-block:: idris
143143
144− get : { [STATE t] } Eff t
145− put : t -> { [STATE t] } Eff ()
144+ get : Eff t [STATE t]
145+ put : t -> Eff () [STATE t]
146146
147147A program in ``Eff`` can call any other function in ``Eff`` provided
148148that the calling function supports at least the effects required by
@@ -168,7 +168,7 @@ instead of ``runPure``:
168168
169169.. code-block:: idris
170170
171− treeTagAux : BTree a -> { [STATE Int] } Eff (BTree (Int, a))
171+ treeTagAux : BTree a -> Eff (BTree (Int, a)) [STATE Int]
172172 ...
173173
174174 treeTag : (i : Int) -> BTree a -> IO (BTree (Int, a))
@@ -202,7 +202,7 @@ shown in Listing [introprog].
202202 "Sheila"
203203 (Node Leaf "Bob" Leaf))
204204
205− treeTagAux : BTree a -> { [STATE Int] } Eff (BTree (Int, a))
205+ treeTagAux : BTree a -> Eff (BTree (Int, a)) [STATE Int]
206206 treeTagAux Leaf = pure Leaf
207207 treeTagAux (Node l x r) = do l' <- treeTagAux l
208208 i <- get
@@ -227,8 +227,8 @@ here for illustrative purposes):
227227
228228.. code-block:: idris
229229
230− runPure : {env : Env id xs} -> { xs } Eff a -> a
231− run : Applicative m => {env : Env m xs} -> { xs } Eff a -> m a
230+ runPure : {env : Env id xs} -> Eff a xs -> a
231+ run : Applicative m => {env : Env m xs} -> Eff a xs -> m a
232232
233233The ``env`` argument is implicit, and initialised automatically where
234234possible using default values given by instances of the following type
@@ -248,8 +248,8 @@ using one of the following functions:
248248
249249.. code-block:: idris
250250
251− runPureInit : Env id xs -> { xs } Eff a -> a
252− runInit : Applicative m => Env m xs -> { xs } Eff a -> m a
251+ runPureInit : Env id xs -> Eff a xs -> a
252+ runInit : Applicative m => Env m xs -> Eff a xs -> m a
253253
254254To be well-typed, the environment must contain resources corresponding
255255exactly to the effects in ``xs``. For example, we could also have
@@ -272,8 +272,7 @@ captured both of these values, e.g.:
272272
273273.. code-block:: idris
274274
275− treeTagAux : BTree a
276− -> { [STATE (Int, Int)] } Eff (BTree (Int, a))
275+ treeTagAux : BTree a -> Eff (BTree (Int, a)) [STATE (Int, Int)]
277276
278277Doing this, however, ties the two states together throughout (as well
279278as not indicating which integer is which). It would be nice to be able
@@ -288,9 +287,9 @@ leaves and update the tag separately, by labelling them as follows:
288287
289288.. code-block:: idris
290289
291− treeTagAux : BTree a
292− -> {['Tag ::: STATE Int,
293− 'Leaves ::: STATE Int]} Eff (BTree (Int, a))
290+ treeTagAux : BTree a -> Eff (BTree (Int, a))
291+ ['Tag ::: STATE Int,
292+ 'Leaves ::: STATE Int]
294293
295294The ``:::`` operator allows an arbitrary label to be given to an
296295effect. This label can be any type—it is simply used to identify an
@@ -320,7 +319,7 @@ applying a function to the current state.
320319
321320.. code-block:: idris
322321
323− update : (x -> x) -> { [STATE x] } Eff ()
322+ update : (x -> x) -> Eff () [STATE x]
324323
325324Finally, our top level ``treeTag`` function now returns a pair of the
326325number of leaves, and the new tree. Resources for labelled effects are
@@ -339,8 +338,7 @@ To summarise, we have:
339338
340339- ``:::`` to convert an effect to a labelled effect.
341340
342−- ``:-`` to convert an effectful operation to a labelled effectful
343− operation.
341+- ``:-`` to convert an effectful operation to a labelled effectful operation.
344342
345343- ``:=`` to initialise a resource for a labelled effect.
346344
@@ -350,7 +348,7 @@ only for the situation where available effects are not updated):
350348.. code-block:: idris
351349
352350 (:::) : lbl -> EFFECT -> EFFECT
353− (:-) : (l : lbl) -> { [x] } Eff a -> { [l ::: x] } Eff a
351+ (:-) : (l : lbl) -> Eff a [x] -> Eff a [l ::: x]
354352 (:=) : (l : lbl) -> res -> LRes l res
355353
356354Here, ``LRes`` is simply the resource type associated with a labelled
@@ -367,7 +365,7 @@ immediately. The following program returns the length of the
367365
368366.. code-block:: idris
369367
370− stateLength : { [STATE String] } Eff Nat
368+ stateLength : Eff Nat [STATE String]
371369 stateLength = do x <- get
372370 pure (length x)
373371
@@ -377,7 +375,7 @@ this. The above program can be written instead as:
377375
378376.. code-block:: idris
379377
380− stateLength : { [STATE String] } Eff Nat
378+ stateLength : Eff Nat [STATE String]
381379 stateLength = pure (length !get)
382380
383381The notation ``!expr`` means that the expression ``expr`` should be
@@ -386,7 +384,7 @@ evaluated and then implicitly bound. Conceptually, we can think of
386384
387385.. code-block:: idris
388386
389− (!) : { xs } Eff a -> a
387+ (!) : Eff a xs -> a
390388
391389Note, however, that it is not really a function, merely syntax! In
392390practice, a subexpression ``!expr`` will lift ``expr`` as high as
@@ -411,22 +409,22 @@ is lifted to:
411409 g' <- g y' x'
412410 f g'
413411
414−Syntactic Sugar and ``Eff``
415−===========================
412+The Type ``Eff``
413+================
416414
417−By now, you may be wondering about the syntax we are using for
418−``Eff``, because it doesn’t look like a normal type! (If not, you may
419−safely skip this section and return to it later.) In fact, the type of
420−``Eff`` is the following:
415+Underneath, ``Eff`` is an overloaded function which translates to an
416+underlying type ``EffM``:
421417
422418.. code-block:: idris
423419
424− Eff : (x : Type) ->
425− List EFFECT -> (x -> List EFFECT) -> Type
420+ EffM : (m : Type -> Type) -> (t : Type)
421+ -> (List EFFECT)
422+ -> (t -> List EFFECT) -> Type
426423
427424This is more general than the types we have been writing so far. It is
428−parameterised over a result type ``x``, as we have already seen, but
429−also a ``List EFFECT`` and a function type ``x -> List EFFECT``.
425+parameterised over an underying computation context ``m``, a
426+result type ``t`` as we have already seen, as well as a ``List EFFECT`` and a
427+function type ``t -> List EFFECT``.
430428
431429These additional parameters are the list of *input* effects, and a
432430list of *output* effects, computed from the result of an effectful
@@ -435,8 +433,7 @@ of effects available! This is a particularly powerful idea, and we
435433will see its consequences in more detail later. Some examples of
436434operations which can change the set of available effects are:
437435
438−- Updating a state containing a dependent type (for example adding an
439− element to a vector).
436+- Updating a state containing a dependent type (for example adding an element to a vector).
440437
441438- Opening a file for reading is an effect, but whether the file really
442439 *is* open afterwards depends on whether the file was successfully
@@ -445,19 +442,25 @@ operations which can change the set of available effects are:
445442- Closing a file means that reading from the file should no longer be
446443 possible.
447444
448−While powerful, this can make uses of the ``Eff`` type hard to read.
449−Therefore, the library provides syntactic sugar which is translated
450−such that:
445+While powerful, this can make uses of the ``EffM`` type hard to read.
446+Therefore the library provides an overloaded function ``Eff``
447+There are the following three versions:
451448
452449.. code-block:: idris
453450
454− { xs } Eff a
451+ SimpleEff.Eff : (t : Type) -> (input_effs : List EFFECT) -> Type
452+ TransEff.Eff : (t : Type) -> (input_effs : List EFFECT) ->
453+ (output_effs : List EFFECT) -> Type
454+ DepEff.Eff : (t : Type) -> (input_effs : List EFFECT) ->
455+ (output_effs_fn : x -> List EFFECT) -> Type
455456
456−is expanded to
457+So far, we have used only the first version, ``SimpleEff.Eff``, which
458+is defined as follows:
457459
458460.. code-block:: idris
459461
460− Eff a xs (\_ => xs)
462+ Eff : (x : Type) -> (es : List EFFECT) -> Type
463+ Eff x es = {m : Type -> Type} -> EffM m x es (\v => es)
461464
462465i.e. the set of effects remains the same on output. This suffices for
463466the ``STATE`` example we have seen so far, and for many useful
@@ -467,19 +470,19 @@ with the expanded type:
467470.. code-block:: idris
468471
469472 treeTagAux : BTree a ->
470− Eff (BTree (Int, a)) [STATE Int] (\x => [STATE Int])
473+ EffM m (BTree (Int, a)) [STATE Int] (\x => [STATE Int])
471474
472475Later, we will see programs which update effects:
473476
474477.. code-block:: idris
475478
476− { xs ==> xs' } Eff a
479+ Eff a xs xs'
477480
478481which is expanded to
479482
480483.. code-block:: idris
481484
482− Eff a xs (\_ => xs')
485+ EffM m a xs (\_ => xs')
483486
484487i.e. the set of effects is updated to ``xs’`` (think of a transition
485488in a state machine). There is, for example, a version of ``put`` which
@@ -487,22 +490,27 @@ updates the type of the state:
487490
488491.. code-block:: idris
489492
490− putM : y -> { [STATE x] ==> [STATE y] } Eff ()
493+ putM : y -> Eff () [STATE x] [STATE y]
491494
492495Also, we have:
493496
494497.. code-block:: idris
495498
496− { xs ==> {res} xs' } Eff a
499+ Eff t xs (\res => xs')
497500
498501which is expanded to
499502
500503.. code-block:: idris
501504
502− Eff a xs (\res => xs')
505+ EffM m t xs (\res => xs')
503506
504507i.e. the set of effects is updated according to the result of the
505−operation ``res``.
508+operation ``res``, of type ``t``.
509+
510+Parameterising ``EffM`` over an underlying computation context allows us
511+to write effectful programs which are specific to one context, and in some
512+cases to write programs which *extend* the list of effects available using
513+the ``new`` function, though this is beyond the scope of this tutorial.
506514
507515.. [1] The earlier paper [3]_ describes the essential implementation
508516 details, although the library presented there is an earlier version