├── .gitignore ├── eff-examples ├── exception.eff ├── state.eff ├── fun_tree.eff ├── queens.eff └── thread.eff ├── LICENSE ├── README.md ├── lecture-4.md ├── lectures-1-2-3.md ├── lecture-3.md └── notes.tex /.gitignore: -------------------------------------------------------------------------------- 1 | /notes.pdf 2 | -------------------------------------------------------------------------------- /eff-examples/exception.eff: -------------------------------------------------------------------------------- 1 | effect Abort : unit -> empty 2 | 3 | let example b = 4 | handle 5 | let x = 7 in 6 | let y = 8 in 7 | if b then 8 | (match perform (Abort ()) with) 9 | else 10 | x + y 11 | with 12 | | v -> v + 20 13 | | effect (Abort ()) _ -> 42 14 | -------------------------------------------------------------------------------- /eff-examples/state.eff: -------------------------------------------------------------------------------- 1 | (** State *) 2 | 3 | effect Get : unit -> int 4 | effect Set : int -> unit 5 | 6 | (* The standard state handler. *) 7 | let state' = handler 8 | | v -> (fun _ -> v) 9 | | effect (Get ()) k -> (fun s -> (k s) s) 10 | | effect (Set s') k -> (fun _ -> (k ()) s') 11 | ;; 12 | 13 | let example1 () = 14 | let f = 15 | (with state' handle 16 | let x = perform (Get ()) in 17 | perform (Set (2 * x)) ; 18 | perform (Get ()) + 10) 19 | in 20 | f 30 21 | ;; 22 | 23 | (* Better state handler, using finally clause *) 24 | let state initial = handler 25 | | y -> (fun _ -> y) 26 | | effect Get k -> (fun s -> k s s) 27 | | effect (Set s') k -> (fun _ -> k () s') 28 | | finally f -> f initial 29 | ;; 30 | 31 | let example2 () = 32 | with state 30 handle 33 | let x = perform (Get ()) in 34 | perform (Set (2 * x)) ; 35 | perform Get + 10 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Algebraic effects and handlers 2 | 3 | These are the notes and materials for the lectures on algebraic effects and 4 | handlers at the [Oregon programming languages summer school 2018 5 | (OPLSS)](https://www.cs.uoregon.edu/research/summerschool/summer18/index.php). 6 | 7 | Each lecture contains a list of problems, which are ordered roughly in the order 8 | of difficulty, either in terms of trickiness, the amount of work, or 9 | prerequisites. I recommend that you discuss the problems in groups, and pick 10 | whichever problems you find interesting. 11 | 12 | # Lectures 13 | 14 | * [What is algebraic about algebraic effects and handlers?](lectures-1-2-3.md) 15 | (lectures 1, 2, and 3) 16 | 17 | * [Designing a programming language](lecture-3.md) 18 | (lecture 3) 19 | 20 | * [Programming with algebraic effects and handlers](lecture-4.md) 21 | (lecture 4) 22 | 23 | # General resources & reading material 24 | 25 | * [Effects bibliography](https://github.com/yallop/effects-bibliography) 26 | * [Effects Rosetta Stone](https://github.com/effect-handlers/effects-rosetta-stone) 27 | * [Programming language Eff](http://www.eff-lang.org) 28 | * [Formalized proofs about Eff](https://github.com/matijapretnar/proofs/) 29 | -------------------------------------------------------------------------------- /eff-examples/fun_tree.eff: -------------------------------------------------------------------------------- 1 | (** This code is compatible with Eff 5.0, see http://www.eff-lang.org *) 2 | 3 | (** We show that with algebraic effects and handlers a total functional 4 | [(int -> bool) -> bool] has a tree representation. *) 5 | 6 | (* A tree representation of a functional. *) 7 | type tree = 8 | | Answer of bool 9 | | Question of int * tree * tree 10 | 11 | (** Convert a tree to a functional. *) 12 | let rec tree2fun t a = 13 | match t with 14 | | Answer y -> y 15 | | Question (x, t1, t2) -> tree2fun (if a x then t1 else t2) a 16 | 17 | (** An effect that we will use to report how the functional is using its argument. *) 18 | effect Report : int -> bool 19 | 20 | (** Convert a functional to a tree. *) 21 | let rec fun2tree h = 22 | handle 23 | Answer (h (fun x -> perform (Report x))) 24 | with 25 | | effect (Report x) k -> Question (x, k false, k true) 26 | 27 | let example1 = fun2tree (fun f -> true) 28 | 29 | let example2 = fun2tree (fun f -> f 10; true) 30 | 31 | let example3 = fun2tree (fun f -> if f 10 then (f 30 || f 15) else (f 20 && not (f 8))) 32 | 33 | (* This one is pretty large, so take care *) 34 | let example4 = 35 | (* convert a string of booleans to an int *) 36 | let rec to_int = function 37 | | [] -> 0 38 | | b :: bs -> (if b then 1 else 0) + 2 * to_int bs 39 | in 40 | fun2tree (fun a -> a (to_int [a 0; a 1; a 2; a 3; a 4; a 5; a 6; a 7; a 8])) 41 | -------------------------------------------------------------------------------- /eff-examples/queens.eff: -------------------------------------------------------------------------------- 1 | (* The queens problem using ambivalent choice. *) 2 | 3 | type queen = int * int 4 | 5 | effect Select : int list -> int 6 | effect Fail : unit -> empty 7 | 8 | (* Do the given queens attack each other? *) 9 | let no_attack (x,y) (x',y') = 10 | x <> x' && y <> y' && abs (x - x') <> abs (y - y') 11 | ;; 12 | 13 | (* Given that queens qs are already placed, return the list of 14 | rows in column x which are not attacked yet. *) 15 | let available x qs = 16 | filter (fun y -> forall (no_attack (x,y)) qs) [1;2;3;4;5;6;7;8] 17 | ;; 18 | 19 | (* Solve the queens problem by guessing what to do *) 20 | let queens () = 21 | let rec place x qs = 22 | if x = 9 then 23 | qs 24 | else 25 | let y = perform (Select (available x qs)) in 26 | place (x+1) ((x,y) :: qs) 27 | in 28 | place 1 [] 29 | 30 | (* A handler for ambivalent choice which uses depth-first search *) 31 | let dfs = handler 32 | | v -> v 33 | | effect (Select lst) k -> 34 | let rec tryem = function 35 | | [] -> (match perform (Fail ()) with) 36 | | x::xs -> (handle k x with effect (Fail ()) _ -> tryem xs) 37 | in 38 | tryem lst 39 | ;; 40 | 41 | (* A handler for ambivalent choice which uses depth-first search *) 42 | let dfs_all = handler 43 | | v -> [v] 44 | | effect (Select lst) k -> 45 | let rec tryem = function 46 | | [] -> [] 47 | | x::xs -> (handle k x with 48 | | lst -> lst @ (tryem xs) 49 | | effect (Fail ()) _ -> tryem xs) 50 | in 51 | tryem lst 52 | ;; 53 | 54 | (* And we can solve the problem: *) 55 | let solution = 56 | with dfs handle queens () 57 | ;; 58 | 59 | let all_solutions = 60 | with dfs_all handle queens () 61 | -------------------------------------------------------------------------------- /eff-examples/thread.eff: -------------------------------------------------------------------------------- 1 | (* This example is described in Section 6.10 of "Programming with Algebraic Effects and 2 | Handlers" by A. Bauer and M. Pretnar. *) 3 | 4 | type thread = unit -> unit 5 | 6 | effect Yield : unit -> unit 7 | effect Spawn : thread -> unit 8 | 9 | (* We will need a queue to keep track of inactive threads. 10 | We implement the queue as state. *) 11 | 12 | effect Dequeue : unit -> thread option 13 | effect Enqueue : thread -> unit 14 | 15 | (* The queue handler *) 16 | let queue initial = handler 17 | | effect (Dequeue ()) k -> 18 | (fun queue -> match queue with 19 | | [] -> k None [] 20 | | hd::tl -> k (Some hd) tl) 21 | | effect (Enqueue y) k -> (fun queue -> k () (queue @ [y])) 22 | | x -> (fun _ -> x) 23 | | finally x -> x initial 24 | ;; 25 | 26 | (* Round-robin thread scheduler. It is an example of a recursively defined handler. *) 27 | let round_robin = 28 | let dequeue_thread () = 29 | match perform (Dequeue ()) with 30 | | None -> () 31 | | Some t -> t () 32 | in 33 | let rec h () = handler 34 | | effect Yield k -> perform (Enqueue k) ; dequeue_thread () 35 | | effect (Spawn t) k -> perform (Enqueue k) ; with h () handle t () 36 | | () -> dequeue_thread () 37 | in 38 | h () 39 | ;; 40 | 41 | (* An example of nested multithreading. We have a thread which prints 42 | the letter a and another one which has two sub-threads printing x and y. *) 43 | 44 | let print_list lst = 45 | iter (fun x -> perform (Print x) ; perform Yield) lst 46 | ;; 47 | 48 | with queue [] handle 49 | with round_robin handle 50 | perform (Spawn (fun _ -> print_list ["a"; "b"; "c"; "d"; "e"])) ; 51 | perform (Spawn (fun _ -> print_list ["A"; "B"; "C"; "D"; "E"])) 52 | ;; 53 | 54 | 55 | (* We can run an unbounded amount of threads. The following example enumerates all 56 | reduced positive fractions less than 1 by spawning a thread for each denominator 57 | between d and e. *) 58 | 59 | let rec fractions d e = 60 | let rec find_fractions n = 61 | (* If the fraction is reduced, print it and yield *) 62 | if gcd n d = 1 then 63 | perform (Print (to_string n ^ "/" ^ to_string d ^ ", ")); perform Yield 64 | else (); 65 | if d > n then 66 | find_fractions (n+1) 67 | else () 68 | in 69 | (* Spawn a thread for the next denominator *) 70 | (if d < e then 71 | perform (Spawn (fun _ -> perform Yield; fractions (d + 1) e)) else ()) ; 72 | (* List all the fractions with the current denominator *) 73 | find_fractions 1 74 | ;; 75 | 76 | with queue [] handle 77 | with round_robin handle 78 | fractions 1 100 79 | ;; 80 | -------------------------------------------------------------------------------- /lecture-4.md: -------------------------------------------------------------------------------- 1 | # Programming with algebraic effects and handlers 2 | 3 | In the last lecture we shall explore how algebraic operations and handlers can 4 | be used in programming. 5 | 6 | ## Eff 7 | 8 | There are several languages that support algebraic effects and handlers. The 9 | ones most faithful to the theory of algebraic effects are [Eff](http://www.eff-lang.org) and the [multicore 10 | OCaml](https://github.com/ocamllabs/ocaml-multicore). They have very similar syntax, and we could use either, but let us use 11 | Eff, just because it was the first language with algebraic effects and handlers. 12 | 13 | You can [run Eff in your browser](http://www.eff-lang.org/try/) or [install it](https://github.com/matijapretnar/eff/#installation--usage) locally. The page also has a quick 14 | overview of the syntax of Eff, which mimics the syntax of OCaml. 15 | 16 | 17 | ## Reading material 18 | 19 | We shall draw on examples from [An introduction to algebraic effects and handlers](http://www.eff-lang.org/handlers-tutorial.pdf) 20 | and [Programming with algebraic effects and handlers](https://arxiv.org/abs/1203.1539). Some examples can be seen 21 | also at the [Effects Roset Stone](https://github.com/effect-handlers/effects-rosetta-stone). 22 | 23 | 24 | ## Basic examples 25 | 26 | * [Exceptions](./eff-examples/exception.eff) 27 | * [State](./eff-examples/state.eff) 28 | 29 | Other examples, such as I/O and redirection can be seen at the [try Eff](http://www.eff-lang.org/try/) page. 30 | 31 | ## Multi-shot handlers 32 | 33 | A handler has access to the continuation, and it may do with it whatever it 34 | likes. We may distinguish handlers according to how many times the continuation 35 | is invoked: 36 | 37 | * an **exception-like** handler does not invoke the continuation 38 | * a **single-shot** handler invokes the continuation exactly once 39 | * a **multi-shot** handler invokes the continuation more than once 40 | 41 | Of course, combinations of these are possible, and there are handlers where it's 42 | difficult to "count" the number of invocations of the continuation, such as 43 | multi-threading below. 44 | 45 | An exception-like handler is, well, like an exception handler. 46 | 47 | A single-shot handler appears to the programmer as a form of dynamic-dispatch 48 | callbacks: performing the operation is like calling the callback, where the 49 | callback is determined dynamically by the enclosing handlers. 50 | 51 | The most interesting (and confusing!) are multi-shot handlers. Let us have a 52 | look at one such handler. 53 | 54 | ### Ambivalent choice 55 | 56 | Ambivalent choice is a computational effect which works as follows. There is an 57 | exception `Fail : unit → empty` which signifies failure to compute successfully, 58 | and an operation `Select : α list → α`, which returns one of the elements of the 59 | list. It has to do return an element such that the subsequent computation does 60 | *not* fail (if possible). 61 | 62 | With ambivalent choice, we may solve the `n`-queens problem (of placing `n` 63 | queens on an `n × n` chess board so they do not attack each other), see [queens.eff](eff-examples/queens.eff). 64 | 65 | ## Cooperative multi-threading 66 | 67 | Operations and handlers have explicit access to continuations. A handler need 68 | not invoke a continue, it may instead store it somewhere and run *another* 69 | (previously stored) continuation. This way we get *threads*. This was worked out 70 | in [thread.eff](eff-examples/thread.eff). 71 | 72 | ## Tree representation of a functional 73 | 74 | Suppose we have a **functional** 75 | 76 | h : (int → bool) → bool 77 | 78 | When we apply it to a function `f : int → bool`, we feel that 79 | 80 | h f 81 | 82 | will proceed as follows: `h` will *ask* `f` about the value `f x₀` for some 83 | integer `x₀`. Depending on the result it gets, it will then ask some furter 84 | question `f x₁`, and so on, until it provides an *answer* `a`. 85 | 86 | We may therefore represent such a functional `h` as a **tree**: 87 | 88 | * the leaves are the answers 89 | * a node is labeled by a question, which has two subtrees representing 90 | the two possible continuations (depending on the answer) 91 | 92 | We may encode this as the datatype: 93 | 94 | type tree = 95 | | Answer of bool 96 | | Question of int * tree * tree 97 | 98 | Given such a tree, we can recreate the functional `h`: 99 | 100 | let rec tree2fun t f = 101 | match t with 102 | | Answer y -> y 103 | | Question (x, t1, t2) -> tree2fun (if f x then t1 else t2) f 104 | 105 | Can we go backwards? Given `h`, how do we get the tree? It turns out this is not 106 | possible in a purely functional setting in general (but is possible for out 107 | specific case, Google "impossible functionals"), but it is with computational 108 | effects. You can see how to do it with handlers in [fun_tree.eff](./eff-examples/fun_tree.eff). 109 | 110 | # Problems 111 | 112 | ## Problem: breadth-first search 113 | 114 | Implement the *breadth-first search* strategy for ambivalent choice. 115 | 116 | ## Problem: Monte Carlo sampling 117 | 118 | The [online Eff](http://www.eff-lang.org/try/) page has an example showing a handler which modifies a 119 | probabilistic computation (one that uses randomness) to one that computes the 120 | *distribution* of results. The handler computes the distribution in an exhaustive 121 | way that quickly leads to inefficiency. 122 | 123 | Improve it by implement a [Monte Carlo](https://en.wikipedia.org/wiki/Monte_Carlo_method) handler for estimating distributions of 124 | probabilistic computations. 125 | 126 | ## Problem: recursive cows 127 | 128 | Contemplate the [recursive cows](https://github.com/effect-handlers/effects-rosetta-stone/tree/master/examples/recursive-cow). 129 | 130 | -------------------------------------------------------------------------------- /lectures-1-2-3.md: -------------------------------------------------------------------------------- 1 | # What is algebraic about algebraic effects and handlers? 2 | 3 | The purpose of the first lecture is to review algebraic theories and related 4 | concepts, and connect them with computational effects. We shall start on the 5 | mathematical side of things and gradually derive from it a programming language. 6 | 7 | The contents of this lecture is a bit ambitious. It is likely that the part 8 | about comodels will spill over to the second lecture. 9 | 10 | ## Outline 11 | 12 | * signatures, terms, and algebraic theories 13 | * models of an algebraic theory 14 | * free models of an algebraic theory 15 | * generalization to parameterized operations with arbitrary arities 16 | * sequencing and generic operations 17 | * handlers 18 | * comodels and tensoring of comodels and models 19 | 20 | ## Reading material 21 | 22 | Pretty much everything that will be said in the first lecture is written up in 23 | [What is algebraic about algebraic effects and 24 | handlers?](https://arxiv.org/abs/1807.05923), which still a bit rough around the 25 | edges, so if you see a typo please [let me know](https://github.com/andrejbauer/what-is-algebraic-about-algebraic-effects). 26 | 27 | 28 | ## Problems 29 | 30 | ### Problem: the theory of an associative unital operation 31 | 32 | Consider the theory `T` of an associative operation with a unit. It has a constant 33 | `ε` and a binary operation `·` satisfying equations 34 | 35 | (x · y) · z = x · (y · z) 36 | ε · x = x 37 | x · ε = x 38 | 39 | Give a useful description of the free model of `T` generated by a set `X`. You 40 | can either guess an explicit construction of free models and show that it has 41 | the required universal property, or you can analyze the free model construction 42 | (equivalence classes of well-founded trees) and provide a simple description of 43 | it. 44 | 45 | ### Problem: the theory of apocalypse 46 | 47 | We formulate an algebraic theory `Time` in it is possible to explicitly record 48 | passage of time. The theory has a single unary operation `tick` and no equations. 49 | Each application of `tick` records the passage of one time step. 50 | 51 | **Problem:** Give a useful description of the free model of the theory, 52 | generated by a set `X`. 53 | 54 | **Problem:** Let a given fixed natural number `n` be given. Describe a theory 55 | `Apocalypse` which extends the theory `Time` so that a computation crashes 56 | (aborts, necessarily terminates) if it performs more than `n` of ticks. Give a 57 | useful description of its free models. 58 | 59 | Advice: do *not* concern yourself with any sort of operational semantics which 60 | somehow "aborts" after `n` ticks. Instead, use equations and postulate that 61 | certain computations are equal to an aborted one. 62 | 63 | 64 | ### Problem: the theory of partial maps 65 | 66 | The models of the empty theory are precisely sets and functions. Is there a 67 | theory whose models form (a category equivalent to) the category of sets and 68 | *partial* functions? 69 | 70 | Recall that a partial function `f : A ⇀ B` is an ordinary function `f : S → B` 71 | defined on a subset `S ⊆ A`. (How do we define composition of partial 72 | functions?) 73 | 74 | 75 | ### Problem: models in the category of models 76 | 77 | In [Example 1.27 of the reading material](https://arxiv.org/abs/1807.05923) it 78 | is calculated that a model of the theory `Group` in the category `Mod(Group)` is 79 | an abelian group. We may generalize this idea and ask about models of theory 80 | `T₁` in the category of models `Mod(T₂)` of theory `T₂`. 81 | 82 | The **tensor product `T₁ ⊗ T₂`** of algebraic theories `T₁` and `T₂` is a theory 83 | such that the category of models of `T₁` in the category `Mod(T₂)` is equivalent 84 | to the category of models of `T₁ ⊗ T₂`. 85 | 86 | Hint: start by outlining what data is needed to have a `T₁`-model in `Mod(T₂)` 87 | is, and pay attention to the fact that the operations of `T₁` must be 88 | interpreted as `T₂`-homomorphisms. That will tell you what the ingredients of 89 | `T₁ ⊗ T₂` should be. 90 | 91 | 92 | ### Problem: Morita equivalence 93 | 94 | It may happen that two theories `T₁` and `T₂` have equivalent categories of models, i.e., 95 | 96 | Mod(T₁) ≃ Mod(T₂) 97 | 98 | In such a case we say that `T₁` and `T₂` are **Morita equivalent**. 99 | 100 | Let `T` be an algebraic theory and `t` a term in context `x₁, …, xᵢ`. Define a 101 | **definitional extension `T+[op:=t]`** to be the theory `T` extended with 102 | an additional operation `op` and equation 103 | 104 | x₁, …, xᵢ | op(x₁, …, xᵢ) = t 105 | 106 | We say that `op` is a **defined operation**. 107 | 108 | **Problem:** Confirm the intuitive feeling that `T+[op:=t]` by proving that `T` 109 | and `T+[op:=t]` are Morita equivalent. 110 | 111 | **Problem:** Formulate the idea of a definitional extension so that we allow an 112 | arbitrary set of defined operations, and show that we still have Morita 113 | equivalence. 114 | 115 | 116 | ### Problem: the theory of a given set 117 | 118 | Given any set `A`, define the **theory `T(A)` of the set `A`** as follows: 119 | 120 | * for every `n` and every map `f : Aⁿ → A`, `op(f)` is an `n`-ary operation 121 | * for all `f : Aⁱ → A`, `g : Aʲ → A` and `h₁, …, hᵢ : Aʲ → A`, if 122 | 123 | f ∘ (h₁, …, hᵢ) = g 124 | 125 | then we have an equation 126 | 127 | x₁, …, xⱼ | op(f)(op(h₁)(x₁,…,xⱼ), …, hᵢ(x₁,…,xⱼ)) = g(x₁, …, xⱼ) 128 | 129 | **Problem:** Is `T({0,1})` Morita equivalent to another, well-known algebraic theory? 130 | 131 | 132 | ### Problem: a comodel for non-determinism 133 | 134 | In [Example 4.6 of the reading material](https://arxiv.org/abs/1807.05923) it is 135 | shown that there is no comodel of non-determinism in the category of sets. Can 136 | you suggest a category in which we get a reasonable comodel of non-determinism? 137 | 138 | 139 | ### Problem: formalization of algebraic theories 140 | 141 | If you prefer avoiding doing Real Math, you can formalize algebraic theories and 142 | their comodels in your favorite proof assistant. A possible starting point is 143 | [this 144 | gist](https://gist.github.com/andrejbauer/3cc438ab38646516e5e9278fdb22022c), and 145 | a good goal is the construction of the free model of a theory generated by a set 146 | (or a type). 147 | 148 | Because the free model requires quotienting, you should think ahead on how you 149 | are going to do that. Some possibilities are: 150 | 151 | * use homotopy type theory and make sure that the types involved are h-Sets 152 | * use setoids 153 | * suggest your own solution 154 | 155 | It may be wiser to first show as a warm-up exercise that theories without 156 | equations have initial models, as that only requires the construction of 157 | well-founded trees (which are inductive types). 158 | -------------------------------------------------------------------------------- /lecture-3.md: -------------------------------------------------------------------------------- 1 | # Designing a programming language 2 | 3 | Having worked out algebraic theories in [previous lectures](lectures-1-2-3.md), 4 | let us turn the equational theories into a small programming language. 5 | 6 | What we have to do: 7 | 8 | 1. Change mathematical terminology to one that is familiar to programmers. 9 | 2. Reuse existing concepts (generators, operations, trees) to set up the overall 10 | structure of the language. 11 | 3. Add missing features, such as primitive types and recursion, and generally 12 | rearrange things a bit to make everything look nicer. 13 | 4. Provide operational semantics. 14 | 5. Provide typing rules. 15 | 16 | 17 | ## Reading material 18 | 19 | There are many possible ways and choices of designing a programming language 20 | around algebraic operations and handlers, but we shall mostly rely on Matija 21 | Pretnar's tutorial [An Introduction to Algebraic Effects and Handlers. Invited 22 | tutorial paper](http://www.eff-lang.org/handlers-tutorial.pdf). A more advanced 23 | treatment is available in [An effect system for algebraic effects and 24 | handlers](https://arxiv.org/abs/1306.6316). 25 | 26 | 27 | ## Change of terminology 28 | 29 | * The elements of `Free Σ V` are are **computations** (instead of trees) 30 | * The elements of `V` are **values** (instead of generators) 31 | * We speak of **value types** (instead of sets of generators) 32 | * We speak of **computation type** (instead of free models) 33 | 34 | Henceforth we ignore equations. 35 | 36 | 37 | ## Abstract syntax 38 | 39 | We add only one primitive type, namely `bool`. Other constructs (integers, 40 | products, sums) are left as exercises. 41 | 42 | Value: 43 | 44 | v ::= x (variable) 45 | | false (boolean constants) 46 | | true 47 | | h (handler) 48 | | λ x . c (function) 49 | 50 | Handler: 51 | 52 | h ::= handler { return x ↦ c_ret, ... opᵢ(x, κ) ↦ c_i, ... } 53 | 54 | Computation: 55 | 56 | c ::= return v (pure computation) 57 | | if v then c₁ else c₂ (conditional) 58 | | v₁ v₂ (application) 59 | | with v handle c (handling) 60 | | do x ← c₁ in c₂ (sequencing) 61 | | op (v, λ x . c) (operation call) 62 | | fix x . c (fixed point) 63 | 64 | We introduce **generic operations** as syntactic abbreviation and let 65 | `op v` stand for `op(v, λ x . return x)`. 66 | 67 | ## Operational semantics 68 | 69 | We provide small-step semantics, but big step semantics can also be given (see 70 | reading material). In the rules below `h` stands for 71 | 72 | handler { return x ↦ c_ret, ... opᵢ(x,y) ↦ cᵢ, ... } 73 | 74 | We write `e₁[e₂/x]` for `e₁` with `e₂` substituted for `x`. The operational rules are: 75 | 76 | ________________________________ 77 | (if true then c₁ else c₂) ↦ c₁ 78 | 79 | 80 | _________________________________ 81 | (if false then c₁ else c₂) ↦ c₂ 82 | 83 | 84 | ______________________ 85 | (λ x . c) v ↦ c[v/x] 86 | 87 | 88 | _____________________________________ 89 | with h handle return v ↦ c_ret[v/x] 90 | 91 | 92 | _____________________________________________________________ 93 | with h handle opᵢ(v,κ) ↦ cᵢ[v/x, (λ x . with h handle κ x)/y] 94 | 95 | 96 | _________________________________ 97 | do x ← return v in c₂ ↦ c₂[v/x] 98 | 99 | 100 | _______________________________________________________ 101 | do x ← op(v, κ) in c₂ ↦ op(v, λ y . do x ← κ y in c₂) 102 | 103 | 104 | ______________________________ 105 | fix x . c ↦ c[(fix x . c)/x] 106 | 107 | ## Effect system 108 | 109 | ### Value and computation types 110 | 111 | Value type: 112 | 113 | A, B := bool | A → C | C ⇒ D 114 | 115 | Computation type: 116 | 117 | C, D := A!Δ 118 | 119 | Dirt: 120 | 121 | Δ ::= {op₁, …, opⱼ} 122 | 123 | The idea is that a computation which returns values of type `A` and *may* 124 | perform operations `op₁, …, opⱼ` has the computation type `A!{op₁, …, opⱼ}`. 125 | 126 | ### Signature 127 | 128 | We presume that some way of declaring operations is given, i.e., that we have 129 | a signature `Σ` which lists operations with their parameters and arities: 130 | 131 | Σ = { …, opᵢ : Aᵢ ↝ Bᵢ, … } 132 | 133 | Note that the the parameter and the arity types `Aᵢ` and `Bᵢ` are both value types. 134 | 135 | ### Typing rules 136 | 137 | A typing context assigns value types to free variables: 138 | 139 | Γ ::= x₁:A₁, …, xᵢ:Aᵢ 140 | 141 | We think of `Γ` as a map which takes variables to their types. 142 | 143 | There are two forms of typing judgement: 144 | 145 | * `Γ ⊢ v : A` – value `v` has value type `A` in context `Γ` 146 | * `Γ ⊢ c : C` – computation `c` has computation type `C` in context `Γ` 147 | 148 | Rules for value typing: 149 | 150 | Γ(x) = A 151 | _________ 152 | Γ ⊢ x : A 153 | 154 | 155 | ________________ 156 | Γ ⊢ false : bool 157 | 158 | 159 | ________________ 160 | Γ ⊢ true : bool 161 | 162 | 163 | Γ, x : A ⊢ c_ret : B!Θ 164 | Γ, x : Pᵢ, κ : Aᵢ → B!Θ ⊢ c_i : B!Θ (for each opᵢ : Pᵢ ↝ Aᵢ in Δ) 165 | _______________________________________________________________________ 166 | Γ ⊢ (handler { return x ↦ c_ret, ... opᵢ(x) κ ↦ c_i, ... }) : A!Δ ⇒ B!Θ 167 | 168 | 169 | Γ, x:A ⊢ c : C 170 | _____________________ 171 | Γ ⊢ (λ x . c) : A → C 172 | 173 | Rules for computation typing: 174 | 175 | Γ ⊢ v : A 176 | __________________ 177 | Γ ⊢ return v : A!Δ 178 | 179 | 180 | Γ ⊢ v : bool Γ ⊢ c₁ : C Γ ⊢ c₂ : C 181 | ________________________________________ 182 | Γ ⊢ (if v then c₁ else c₂) : C 183 | 184 | 185 | Γ ⊢ v₁ : A → C Γ ⊢ v₂ : A 186 | ____________________________ 187 | Γ ⊢ v₁ v₂ : C 188 | 189 | 190 | Γ ⊢ v : C ⇒ D Γ ⊢ c : C 191 | ___________________________ 192 | Γ ⊢ (with v handle c) : D 193 | 194 | 195 | Γ ⊢ c₁ : A!Δ Γ, x:A ⊢ c₂ : B!Δ 196 | _________________________________ 197 | Γ ⊢ (do x ← c₁ in c₂) : B!Δ 198 | 199 | 200 | Γ ⊢ v : Aᵢ opᵢ ∈ Δ opᵢ : Aᵢ ↝ Bᵢ 201 | ___________________________________ 202 | Γ ⊢ op v : Bᵢ!Δ 203 | 204 | 205 | Γ, x:A ⊢ c : A!Δ 206 | _____________________ 207 | Γ ⊢ (fix x . c) : A!Δ 208 | 209 | 210 | ## Safety theorem 211 | 212 | If `⊢ c : A!Δ` then: 213 | 214 | 1. `c = return v` for some `⊢ v : A` **or** 215 | 2. `c = op(v, κ)` for some `op ∈ Δ` and some value `v` and continuation `κ`, **or** 216 | 3. `c ↦ c'` for some `⊢ c' : A!Δ`. 217 | 218 | **Proof.** See [An effect system for algebraic effects and handlers](https://arxiv.org/abs/1306.6316) 219 | for a mechanised proof. 220 | 221 | 222 | ## Other considerations 223 | 224 | 1. The effect system suffers from the so-called *poisoning*, which can be resolved if we introduce 225 | **effect subtyping**. 226 | 227 | 2. Recursion requires that we use domain-theoretic denotational semantics. Such 228 | a semantics turns out to be adequate (but not fully abstract for the same reasons 229 | that domain theory is not fully abstract for PCF). 230 | 231 | See [An effect system for algebraic effects and handlers](https://arxiv.org/abs/1306.6316) where the above points are treated 232 | carefully. 233 | 234 | 235 | ## Problems 236 | 237 | ### Problem: products 238 | 239 | Add simple products `A × B` to the core language: 240 | 241 | 1. Extend the syntax of values with pairs. 242 | 2. Extend the syntax of computations with an elimination of pairs, e.g., `do (x,y) ← c₁ in c₂`. 243 | 3. Extend the operational semantics. 244 | 4. Extend the typing rules. 245 | 246 | 247 | ### Problem: sums 248 | 249 | Add simple sums `A + B` to the core language: 250 | 251 | 1. Extend the syntax of values with injections. 252 | 2. Extend the syntax of computations with an elimination of sums (a suitable `match` statement). 253 | 3. Extend the operational semantics. 254 | 4. Extend the typing rules. 255 | 256 | 257 | ### Problem: `empty` and `unit` types 258 | 259 | Add the `empty` and `unit` types to the core language. Follow the same steps as 260 | in the previous exercises. 261 | 262 | 263 | ### Problem: non-terminating program 264 | 265 | Define a program which prints infinitely many booleans. You may assume that the 266 | `print : bool → unit` operation is handled appropriately by the runtime 267 | environment. For extra credit, make it "funny". 268 | 269 | 270 | ### Problem: implement a language with operations and handlers 271 | 272 | Implement the core language from Matija Pretnar's 273 | [tutorial](http://www.eff-lang.org/handlers-tutorial.pdf). To make it 274 | interesting, augment it with recursive function definitions, integers, and 275 | product types. Consider implementing the language as part of the [Programming 276 | Languages Zoo](http://plzoo.andrej.com) 277 | -------------------------------------------------------------------------------- /notes.tex: -------------------------------------------------------------------------------- 1 | \documentclass{article} 2 | 3 | \usepackage[T1]{fontenc} 4 | \usepackage[margin=1in]{geometry} 5 | \usepackage{palatino} 6 | \usepackage{amsthm,amsmath,amssymb} 7 | \usepackage{xcolor} 8 | \usepackage[utf8]{inputenc} 9 | \usepackage[colorlinks=true,urlcolor=blue]{hyperref} 10 | \usepackage{listings} 11 | \newcommand{\task}{\par\noindent\emph{Task:}\ } 12 | 13 | \definecolor{darkblue}{rgb}{0,0,0.5} 14 | \definecolor{lightgray}{gray}{0.75} 15 | 16 | \lstset{basicstyle=\ttfamily\small, numbers=left, 17 | xleftmargin=3em,extendedchars=true, 18 | numberstyle=\tiny\color{lightgray}, keywordstyle=\color{darkblue}, morekeywords={and, 19 | as, begin, check, do, done, downto, else, end, effect, external, finally, 20 | for, fun, function, handle, handler, if, in, match, let, new, of, operation, 21 | perform, rec, val, while, to, type, then, with}, 22 | literate=% 23 | {→}{{$\rightarrow$}}1% 24 | {×}{{$\times$}}1% 25 | {←}{{$\leftarrow$}}1% 26 | {↦}{{$\mapsto$}}1% 27 | {↝}{{$\leadsto$}}1% 28 | {…}{{$\ldots$}}1% 29 | {⇒}{{$\Rightarrow$}}1% 30 | {∈}{{$\in$}}1% 31 | {≡}{{$\equiv$}}1% 32 | {λ}{{$\lambda$}}1% 33 | {⊢}{{$\vdash$}}1% 34 | {κ}{{$\kappa$}}1% 35 | {Σ}{{$\Sigma$}}1% 36 | {Δ}{{$\Delta$}}1% 37 | {Γ}{{$\Gamma$}}1% 38 | {Θ}{{$\Theta$}}1% 39 | {₀}{{${}_0$}}1% 40 | {₁}{{${}_1$}}1% 41 | {₂}{{${}_2$}}1% 42 | {ᵢ}{{${}_\mathtt{i}$}}1% 43 | {ⱼ}{{${}_\mathtt{j}$}}1% 44 | } 45 | 46 | {\theoremstyle{definition} 47 | \newtheorem{problem}{Problem}[section]} 48 | 49 | 50 | \begin{document} 51 | \title{Algebraic effects and handlers\\(OPLSS 2018 lecture notes)} 52 | \author{Andrej Bauer\\University of Ljubljana} 53 | \date{July 2018} 54 | 55 | \maketitle 56 | 57 | These are the notes and materials for the lectures on algebraic effects and 58 | handlers at the 59 | \href{https://www.cs.uoregon.edu/research/summerschool/summer18/index.php}{Oregon 60 | programming languages summer school 2018 (OPLSS)}. The notes were originally 61 | written in Markdown and converted to {\LaTeX} semi-automatically, please excuse 62 | strange formatting. You can find all the resources at 63 | \href{https://github.com/OPLSS/introduction-to-algebraic-effects-and-handlers}{the 64 | accompanying GitHub repository}. 65 | 66 | The lectures were recorded on video that are available at the summer school web 67 | site. 68 | 69 | \hypertarget{general-resources-reading-material}{% 70 | \subsubsection*{General resources \& reading 71 | material}\label{general-resources-reading-material}} 72 | 73 | \begin{itemize} 74 | \item 75 | \href{https://github.com/yallop/effects-bibliography}{Effects 76 | bibliography} 77 | \item 78 | \href{https://github.com/effect-handlers/effects-rosetta-stone}{Effects 79 | Rosetta Stone} 80 | \item 81 | \href{http://www.eff-lang.org}{Programming language Eff} 82 | \end{itemize} 83 | 84 | \hypertarget{what-is-algebraic-about-algebraic-effects-and-handlers}{% 85 | \section{What is algebraic about algebraic effects and 86 | handlers?}\label{what-is-algebraic-about-algebraic-effects-and-handlers}} 87 | 88 | The purpose of the first two lectures is to review algebraic theories and 89 | related concepts, and connect them with computational effects. We shall start on 90 | the mathematical side of things and gradually derive from it a programming 91 | language. 92 | 93 | \hypertarget{outline}{% 94 | \subsubsection*{Outline}\label{outline}} 95 | 96 | Pretty much everything that will be said in the first two lectures is written 97 | up in \href{https://arxiv.org/abs/1807.05923}{``What is algebraic about 98 | algebraic effects and handlers?''}, which still a bit rough around the 99 | edges, so if you see a typo please 100 | \href{https://github.com/andrejbauer/what-is-algebraic-about-algebraic-effects}{let 101 | me know}. 102 | 103 | Contents of the first two lectures: 104 | % 105 | \begin{itemize} 106 | \item 107 | signatures, terms, and algebraic theories 108 | \item 109 | models of an algebraic theory 110 | \item 111 | free models of an algebraic theory 112 | \item 113 | generalization to parameterized operations with arbitrary arities 114 | \item 115 | sequencing and generic operations 116 | \item 117 | handlers 118 | \item 119 | comodels and tensoring of comodels and models 120 | \end{itemize} 121 | 122 | \hypertarget{problems}{% 123 | \subsection{Problems}\label{problems}} 124 | 125 | Each section contains a list of problems, which are ordered roughly in the order 126 | of difficulty, either in terms of trickiness, the amount of work, or 127 | prerequisites. I recommend that you discuss the problems in groups, and pick 128 | whichever problems you find interesting. 129 | 130 | \begin{problem}[The theory of an associative unital operation] 131 | Consider the theory $T$ of an associative operation with a unit. 132 | It has a constant $\epsilon$ and a binary operation $\cdot$ 133 | satisfying equations 134 | % 135 | \begin{align*} 136 | (x \cdot y) \cdot z &= x \cdot (y \cdot z) \\ 137 | \epsilon \cdot x &= x \\ 138 | x \cdot \epsilon &= x 139 | \end{align*} 140 | % 141 | Give a useful description of the free model of $T$ generated by a 142 | set $X$. You can either guess an explicit construction of free 143 | models and show that it has the required universal property, or you can 144 | analyze the free model construction (equivalence classes of well-founded 145 | trees) and provide a simple description of it. 146 | \end{problem} 147 | 148 | \begin{problem}[The theory of apocalypse] 149 | We formulate an algebraic theory $\mathsf{Time}$ in it is possible to 150 | explicitly record passage of time. The theory has a single unary 151 | operation $\mathsf{tick}$ and no equations. Each application of 152 | $\mathsf{tick}$ records the passage of one time step. 153 | 154 | \task Give a useful description of the free model of the 155 | theory, generated by a set $X$. 156 | 157 | \task Let a given fixed natural number $n$ be given. 158 | Describe a theory $\mathsf{Apocalypse}$ which extends the theory 159 | $\mathsf{Time}$ so that a computation crashes (aborts, necessarily 160 | terminates) if it performs more than $n$ of ticks. Give a useful 161 | description of its free models. 162 | 163 | Advice: do \emph{not} concern yourself with any sort of operational 164 | semantics which somehow ``aborts'' after $n$ ticks. Instead, use 165 | equations and postulate that certain computations are equal to an 166 | aborted one. 167 | \end{problem} 168 | 169 | \begin{problem}[The theory of partial maps] 170 | 171 | The models of the empty theory are precisely sets and functions. Is 172 | there a theory whose models form (a category equivalent to) the category 173 | of sets and \emph{partial} functions? 174 | 175 | Recall that a partial function $f : A \hookrightarrow B$ is an ordinary 176 | function $f : S \to B$ defined on a subset $S \subseteq A$. 177 | (How do we define composition of partial functions?) 178 | \end{problem} 179 | 180 | \begin{problem}[Models in the category of models] 181 | 182 | In \href{https://arxiv.org/abs/1807.05923}{Example 1.27 of the reading 183 | material} it is calculated that a model of the theory $\mathsf{Group}$ in 184 | the category $\mathsf{Mod}(\textsf{Group})$ is an abelian group. We may generalize 185 | this idea and ask about models of theory $T_1$ in the category of 186 | models $\mathsf{Mod}(T_2)$ of theory $T_2$. 187 | 188 | The \textbf{tensor product $T_1 \otimes T_2$} of algebraic theories 189 | $T_1$ and $T_2$ is a theory such that the category of models 190 | of $T_1$ in the category $\mathsf{Mod}(T_2)$ is equivalent to the 191 | category of models of $T_1 \otimes T_2$. 192 | 193 | Hint: start by outlining what data is needed to have a $T_1$-model 194 | in $\mathsf{Mod}(T_2)$ is, and pay attention to the fact that the 195 | operations of $T_1$ must be interpreted as 196 | $T_2$-homomorphisms. That will tell you what the ingredients of 197 | $T_1 \otimes T_2$ should be. 198 | \end{problem} 199 | 200 | \subsubsection{Problem: Morita equivalence} 201 | 202 | It may happen that two theories $T_1$ and $T_2$ have 203 | equivalent categories of models, i.e., 204 | % 205 | \begin{equation*} 206 | \mathsf{Mod}(T_1) \simeq \mathsf{Mod}(T_2) 207 | \end{equation*} 208 | % 209 | In such a case we say that $T_1$ and $T_2$ are 210 | \textbf{Morita equivalent}. 211 | 212 | Let $T$ be an algebraic theory and $t$ a term in context 213 | $x_1, \ldots, x_i$. Define a \textbf{definitional extension 214 | $T + (\mathsf{op} {{:}{=}} t)$} to be the theory $T$ extended with an 215 | additional operation $\mathsf{op}$ and equation 216 | % 217 | \begin{equation*} 218 | x_1, \ldots, x_i \mid \mathsf{op}(x_1, \ldots, x_i) = t 219 | \end{equation*} 220 | % 221 | We say that $\mathsf{op}$ is a \textbf{defined operation}. 222 | % 223 | \task Confirm the intuitive feeling that 224 | $T + (\mathsf{op} {{:}{=}} t)$ by proving that $T$ and 225 | $T + (\mathsf{op} {{:}{=}} t)$ are Morita equivalent. 226 | 227 | \task Formulate the idea of a definitional extension so that 228 | we allow an arbitrary set of defined operations, and show that we still 229 | have Morita equivalence. 230 | 231 | \begin{problem}[The theory of a given set] 232 | Given any set $A$, define the \textbf{theory $T(A)$ of the set $A$} as follows: 233 | % 234 | \begin{itemize} 235 | \item 236 | for every $n$ and every map $f : A^n \to A$, 237 | $\mathsf{op}(f)$ is an $n$-ary operation 238 | \item 239 | for all $f : A^i \to A$, $g : A^j \to A$ and 240 | $h_1, \ldots, h_i : A^j \to A$, if 241 | % 242 | \begin{equation*} 243 | f \circ (h_1, \ldots, h_i) = g 244 | \end{equation*} 245 | % 246 | then we have an equation 247 | % 248 | \begin{equation*} 249 | x_1, \ldots, x_j \mid \mathsf{op}(f)(\mathsf{op}(h_1)(x_1,\ldots,x_j), \ldots, h_i(x_1,\ldots,x_j)) = g(x_1, \ldots, x_j) 250 | \end{equation*} 251 | \end{itemize} 252 | 253 | \task Is $T(\{0,1\})$ Morita equivalent to another, well-known algebraic theory? 254 | \end{problem} 255 | 256 | \begin{problem}[A comodel for non-determinism] 257 | In \href{https://arxiv.org/abs/1807.05923}{Example 4.6 of the reading 258 | material} it is shown that there is no comodel of non-determinism in the 259 | category of sets. Can you suggest a category in which we get a reasonable 260 | comodel of non-determinism? 261 | \end{problem} 262 | 263 | \begin{problem}[Formalization of algebraic theories] 264 | 265 | If you prefer avoiding doing Real Math, you can formalize algebraic 266 | theories and their comodels in your favorite proof assistant. A possible 267 | starting point is 268 | \href{https://gist.github.com/andrejbauer/3cc438ab38646516e5e9278fdb22022c}{this 269 | gist}, and a good goal is the construction of the free model of a theory 270 | generated by a set (or a type). 271 | 272 | Because the free model requires quotienting, you should think ahead on 273 | how you are going to do that. Some possibilities are: 274 | % 275 | \begin{itemize} 276 | \item 277 | use homotopy type theory and make sure that the types involved are 278 | h-Sets 279 | \item 280 | use setoids 281 | \item 282 | suggest your own solution 283 | \end{itemize} 284 | % 285 | It may be wiser to first show as a warm-up exercise that theories 286 | without equations have initial models, as that only requires the 287 | construction of well-founded trees (which are inductive types). 288 | \end{problem} 289 | 290 | \section{Designing a programming language} 291 | 292 | Having worked out algebraic theories in previous lectures, let us turn the 293 | equational theories into a small programming language. 294 | 295 | What we have to do: 296 | % 297 | \begin{enumerate} 298 | \item Change mathematical terminology to one that is familiar to programmers. 299 | \item Reuse existing concepts (generators, operations, trees) to set up the overall 300 | structure of the language. 301 | \item Add missing features, such as primitive types and recursion, and generally 302 | rearrange things a bit to make everything look nicer. 303 | \item Provide operational semantics. 304 | \item Provide typing rules. 305 | \end{enumerate} 306 | 307 | \subsection{Reading material} 308 | 309 | There are many possible ways and choices of designing a programming language 310 | around algebraic operations and handlers, but we shall mostly rely on Matija 311 | Pretnar's tutorial \href{http://www.eff-lang.org/handlers-tutorial.pdf}{An 312 | Introduction to Algebraic Effects and Handlers. Invited tutorial paper}. A 313 | more advanced treatment is available in 314 | \href{https://arxiv.org/abs/1306.6316}{An effect system for algebraic effects 315 | and handlers}. 316 | 317 | \subsection{Change of terminology} 318 | 319 | \begin{itemize} 320 | \item The elements of $\mathsf{Free}_\Sigma(V)$ are are \textbf{computations} (instead of trees). 321 | \item The elements of $V$ are \textbf{values} (instead of generators). 322 | \item We speak of \textbf{value types} (instead of sets of generators). 323 | \item We speak of \textbf{computation type} (instead of free models). 324 | \end{itemize} 325 | 326 | Henceforth we ignore equations. 327 | 328 | \subsection{Abstract syntax} 329 | 330 | We add only one primitive type, namely $\mathsf{bool}$. Other constructs 331 | (integers, products, sums) are left as exercises. 332 | 333 | \noindent 334 | Value: 335 | % 336 | \begin{lstlisting} 337 | v ::= x (variable) 338 | | false (boolean constants) 339 | | true 340 | | h (handler) 341 | | λ x . c (function) 342 | \end{lstlisting} 343 | % 344 | Handler: 345 | % 346 | \begin{lstlisting} 347 | h ::= handler { return x ↦ c_ret, ... opᵢ(x, κ) ↦ cᵢ, ... } 348 | \end{lstlisting} 349 | % 350 | Computation: 351 | % 352 | \begin{lstlisting} 353 | c ::= return v (pure computation) 354 | | if v then c₁ else c₂ (conditional) 355 | | v₁ v₂ (application) 356 | | with v handle c (handling) 357 | | do x ← c₁ in c₂ (sequencing) 358 | | op (v, λ x . c) (operation call) 359 | | fix x . c (fixed point) 360 | \end{lstlisting} 361 | % 362 | We introduce \textbf{generic operations} as syntactic abbreviation and let 363 | $\mathsf{op}\;v$ stand for $\mathsf{op}(v, \lambda x . \mathsf{return}\; x)$. 364 | 365 | \subsection{Operational semantics} 366 | 367 | We provide small-step semantics, but big step semantics can also be given (see 368 | reading material). In the rules below \lstinline{h} stands for 369 | % 370 | \begin{lstlisting} 371 | handler { return x ↦ c_ret, ... opᵢ(x,y) ↦ cᵢ, ... } 372 | \end{lstlisting} 373 | % 374 | We write \lstinline{e₁[e₂/x]} for \lstinline{e₁} with \lstinline{e₂} substituted 375 | for \lstinline{x}. The operational rules are: 376 | % 377 | \begin{lstlisting} 378 | ________________________________ 379 | (if true then c₁ else c₂) ↦ c₁ 380 | 381 | _________________________________ 382 | (if false then c₁ else c₂) ↦ c₂ 383 | 384 | ______________________ 385 | (λ x . c) v ↦ c[v/x] 386 | 387 | _____________________________________ 388 | with h handle return v ↦ c_ret[v/x] 389 | 390 | _____________________________________________________________ 391 | with h handle opᵢ(v,κ) ↦ cᵢ[v/x, (λ x . with h handle κ x)/y] 392 | 393 | _________________________________ 394 | do x ← return v in c₂ ↦ c₂[v/x] 395 | 396 | _______________________________________________________ 397 | do x ← op(v, κ) in c₂ ↦ op(v, λ y . do x ← κ y in c₂) 398 | 399 | ______________________________ 400 | fix x . c ↦ c[(fix x . c)/x] 401 | \end{lstlisting} 402 | 403 | \subsection{Effect system} 404 | 405 | \subsubsection{Value and computation types} 406 | 407 | Value type: 408 | % 409 | \begin{lstlisting} 410 | A, B := bool | A → C | C ⇒ D 411 | \end{lstlisting} 412 | % 413 | Computation type: 414 | % 415 | \begin{lstlisting} 416 | C, D := A!Δ 417 | \end{lstlisting} 418 | % 419 | Dirt: 420 | % 421 | \begin{lstlisting} 422 | Δ ::= {op₁, …, opⱼ} 423 | \end{lstlisting} 424 | % 425 | The idea is that a computation which returns values of type \lstinline{A} and 426 | \emph{may} perform operations \lstinline{op₁, …, opⱼ} has the computation type 427 | \lstinline|A!{op₁, …, opⱼ}|. 428 | 429 | \subsubsection{Signature} 430 | 431 | We presume that some way of declaring operations is given, i.e., that we have a 432 | signature \lstinline{Σ} which lists operations with their parameters and 433 | arities: 434 | % 435 | \begin{lstlisting} 436 | Σ = { …, opᵢ : Aᵢ ↝ Bᵢ, … } 437 | \end{lstlisting} 438 | % 439 | Note that the the parameter and the arity types \lstinline{Aᵢ} and 440 | \lstinline{Bᵢ} are both value types. 441 | 442 | \subsubsection{Typing rules} 443 | 444 | A typing context assigns value types to free variables: 445 | % 446 | \begin{lstlisting} 447 | Γ ::= x₁:A₁, …, xᵢ:Aᵢ 448 | \end{lstlisting} 449 | % 450 | We think of \lstinline{Γ} as a map which takes variables to their types. 451 | 452 | There are two forms of typing judgment: 453 | % 454 | \begin{enumerate} 455 | \item \lstinline{Γ ⊢ v : A} -- value \lstinline{v} has value type \lstinline{A} in context \lstinline{Γ} 456 | \item \lstinline{Γ ⊢ c : C} -- computation \lstinline{c} has computation type \lstinline{C} in context \lstinline{Γ} 457 | \end{enumerate} 458 | 459 | Rules for value typing: 460 | % 461 | \begin{lstlisting} 462 | Γ(x) = A 463 | _________ 464 | Γ ⊢ x : A 465 | 466 | ________________ 467 | Γ ⊢ false : bool 468 | 469 | ________________ 470 | Γ ⊢ true : bool 471 | 472 | Γ, x : A ⊢ c_ret : B!Θ 473 | Γ, x : Pᵢ, κ : Aᵢ → B!Θ ⊢ cᵢ : B!Θ (for each opᵢ : Pᵢ ↝ Aᵢ in Δ) 474 | _______________________________________________________________________ 475 | Γ ⊢ (handler { return x ↦ c_ret, ... opᵢ(x) κ ↦ cᵢ, ... }) : A!Δ ⇒ B!Θ 476 | 477 | Γ, x:A ⊢ c : C 478 | _____________________ 479 | Γ ⊢ (λ x . c) : A → C 480 | \end{lstlisting} 481 | % 482 | Rules for computation typing: 483 | % 484 | \begin{lstlisting} 485 | Γ ⊢ v : A 486 | __________________ 487 | Γ ⊢ return v : A!Δ 488 | 489 | Γ ⊢ v : bool Γ ⊢ c₁ : C Γ ⊢ c₂ : C 490 | ________________________________________ 491 | Γ ⊢ (if v then c₁ else c₂) : C 492 | 493 | Γ ⊢ v₁ : A → C Γ ⊢ v₂ : A 494 | ____________________________ 495 | Γ ⊢ v₁ v₂ : C 496 | 497 | Γ ⊢ v : C ⇒ D Γ ⊢ c : C 498 | ___________________________ 499 | Γ ⊢ (with v handle c) : D 500 | 501 | Γ ⊢ c₁ : A!Δ Γ, x:A ⊢ c₂ : B!Δ 502 | _________________________________ 503 | Γ ⊢ (do x ← c₁ in c₂) : B!Δ 504 | 505 | Γ ⊢ v : Aᵢ opᵢ ∈ Δ opᵢ : Aᵢ ↝ Bᵢ 506 | ___________________________________ 507 | Γ ⊢ op v : Bᵢ!Δ 508 | 509 | Γ, x:A ⊢ c : A!Δ 510 | _____________________ 511 | Γ ⊢ (fix x . c) : A!Δ 512 | \end{lstlisting} 513 | 514 | \subsection{Safety theorem} 515 | 516 | If \lstinline{⊢ c : A!Δ} then: 517 | % 518 | \begin{enumerate} 519 | \item \lstinline{c = return v} for some \lstinline{⊢ v : A} \emph{or} 520 | \item \lstinline{c = op(v, κ)} for some \lstinline{op ∈ Δ} and some value \lstinline{v} and continuation \lstinline{κ}, \emph{or} 521 | \item \lstinline{c ↦ c'} for some \lstinline{⊢ c' : A!Δ}. 522 | \end{enumerate} 523 | % 524 | For a mechanized proof see \href{https://arxiv.org/abs/1306.6316}{An effect 525 | system for algebraic effects and handlers}. 526 | 527 | 528 | \subsection{Other considerations} 529 | 530 | The effect system suffers from the so-called \emph{poisoning}, which can be 531 | resolved if we introduce \textbf{effect subtyping}. Recursion requires that we 532 | use domain-theoretic denotational semantics. Such a semantics turns out to be 533 | adequate (but not fully abstract for the same reasons that domain theory is not 534 | fully abstract for PCF). See \href{https://arxiv.org/abs/1306.6316}{An effect 535 | system for algebraic effects and handlers} where the above points are treated 536 | carefully. 537 | 538 | \subsubsection{Problems} 539 | 540 | \begin{problem}[Products] 541 | 542 | Add simple products \lstinline{A × B} to the core language: 543 | % 544 | \begin{enumerate} 545 | \item Extend the syntax of values with pairs. 546 | \item Extend the syntax of computations with an elimination of pairs, e.g., \lstinline{do (x,y) ← c₁ in c₂}. 547 | \item Extend the operational semantics. 548 | \item Extend the typing rules. 549 | \end{enumerate} 550 | \end{problem} 551 | 552 | \begin{problem}[Sums] 553 | 554 | Add simple sums \lstinline{A + B} to the core language: 555 | % 556 | \begin{enumerate} 557 | \item Extend the syntax of values with injections. 558 | \item Extend the syntax of computations with an elimination of sums (a suitable \lstinline{match} statement). 559 | \item Extend the operational semantics. 560 | \item Extend the typing rules. 561 | \end{enumerate} 562 | \end{problem} 563 | 564 | \begin{problem}[\lstinline{empty} and \lstinline{unit} types] 565 | Add the \lstinline{empty} and \lstinline{unit} types to the core language. Follow the same steps as 566 | in the previous exercises. 567 | \end{problem} 568 | 569 | \begin{problem}[Non-terminating program] 570 | Define a program which prints infinitely many booleans. You may assume that the 571 | \lstinline{print : bool → unit} operation is handled appropriately by the runtime 572 | environment. For extra credit, make it "funny". 573 | \end{problem} 574 | 575 | \begin{problem}[Implementation] 576 | Implement the core language from Matija Pretnar's 577 | \href{http://www.eff-lang.org/handlers-tutorial.pdf}{tutorial}. To make it 578 | interesting, augment it with recursive function definitions, integers, and 579 | product types. Consider implementing the language as part of the \href{http://plzoo.andrej.com}{Programming 580 | Languages Zoo}. 581 | \end{problem} 582 | 583 | 584 | \hypertarget{programming-with-algebraic-effects-and-handlers}{% 585 | \section{Programming with algebraic effects and 586 | handlers}\label{programming-with-algebraic-effects-and-handlers}} 587 | 588 | In the last lecture we shall explore how algebraic operations and 589 | handlers can be used in programming. 590 | 591 | \hypertarget{eff}{% 592 | \subsection{Eff}\label{eff}} 593 | 594 | There are several languages that support algebraic effects and handlers. 595 | The ones most faithful to the theory of algebraic effects are 596 | \href{http://www.eff-lang.org}{Eff} and the 597 | \href{https://github.com/ocamllabs/ocaml-multicore}{multicore OCaml}. 598 | They have very similar syntax, and we could use either, but let us use 599 | Eff, just because it was the first language with algebraic effects and 600 | handlers. 601 | 602 | You can \href{http://www.eff-lang.org/try/}{run Eff in your browser} or 603 | \href{https://github.com/matijapretnar/eff/\#installation--usage}{install 604 | it} locally. The page also has a quick overview of the syntax of Eff, 605 | which mimics the syntax of OCaml. 606 | 607 | \hypertarget{reading-material-1}{% 608 | \subsection{Reading material}\label{reading-material-1}} 609 | 610 | We shall draw on examples from 611 | \href{http://www.eff-lang.org/handlers-tutorial.pdf}{An introduction to 612 | algebraic effects and handlers} and 613 | \href{https://arxiv.org/abs/1203.1539}{Programming with algebraic 614 | effects and handlers}. Some examples can be seen also at the 615 | \href{https://github.com/effect-handlers/effects-rosetta-stone}{Effects 616 | Rosetta Stone}. 617 | 618 | Other examples, such as I/O and redirection can be seen at the 619 | \href{http://www.eff-lang.org/try/}{try Eff} page. 620 | 621 | \hypertarget{basic-examples}{% 622 | \subsection{Basic examples}\label{basic-examples}} 623 | 624 | \subsubsection*{Exceptions} 625 | \label{sec:exceptions} 626 | 627 | \lstinputlisting{./eff-examples/exception.eff} 628 | 629 | \subsubsection*{State} 630 | \label{sec:state} 631 | 632 | \lstinputlisting{./eff-examples/state.eff} 633 | 634 | \hypertarget{multi-shot-handlers}{% 635 | \subsection{Multi-shot handlers}\label{multi-shot-handlers}} 636 | 637 | A handler has access to the continuation, and it may do with it whatever 638 | it likes. We may distinguish handlers according to how many times the 639 | continuation is invoked: 640 | 641 | \begin{itemize} 642 | \item 643 | an \textbf{exception-like} handler does not invoke the continuation 644 | \item 645 | a \textbf{single-shot} handler invokes the continuation exactly once 646 | \item 647 | a \textbf{multi-shot} handler invokes the continuation more than once 648 | \end{itemize} 649 | 650 | Of course, combinations of these are possible, and there are handlers 651 | where it's difficult to ``count'' the number of invocations of the 652 | continuation, such as multi-threading below. 653 | 654 | An exception-like handler is, well, like an exception handler. 655 | 656 | A single-shot handler appears to the programmer as a form of 657 | dynamic-dispatch callbacks: performing the operation is like calling the 658 | callback, where the callback is determined dynamically by the enclosing 659 | handlers. 660 | 661 | The most interesting (and confusing!) are multi-shot handlers. Let us 662 | have a look at one such handler. 663 | 664 | \hypertarget{ambivalent-choice}{% 665 | \subsubsection{Ambivalent choice}\label{ambivalent-choice}} 666 | 667 | Ambivalent choice is a computational effect which works as follows. There is an 668 | exception $\mathsf{Fail} : \mathsf{unit} \to \mathsf{empty}$ which signifies 669 | failure to compute successfully, and an operation 670 | $\mathsf{Select} : \alpha\; \mathsf{list} \to \alpha$, which returns one of the 671 | elements of the list. It has to do return an element such that the subsequent 672 | computation does \emph{not} fail (if possible). 673 | 674 | With ambivalent choice, we may solve the $n$-queens problem (of 675 | placing $n$ queens on an $n \times n$ chess board so they do 676 | not attack each other): 677 | 678 | \lstinputlisting{./eff-examples/queens.eff} 679 | 680 | \hypertarget{cooperative-multi-threading}{% 681 | \subsection{Cooperative 682 | multi-threading}\label{cooperative-multi-threading}} 683 | 684 | Operations and handlers have explicit access to continuations. A handler 685 | need not invoke a continue, it may instead store it somewhere and run 686 | \emph{another} (previously stored) continuation. This way we get 687 | \emph{threads}. 688 | 689 | \lstinputlisting{./eff-examples/thread.eff} 690 | 691 | \hypertarget{tree-representation-of-a-functional}{% 692 | \subsection{Tree representation of a 693 | functional}\label{tree-representation-of-a-functional}} 694 | 695 | Suppose we have a \textbf{functional} 696 | % 697 | \begin{equation*} 698 | h : (\mathsf{int} \to \mathsf{bool}) \to \mathsf{bool} 699 | \end{equation*} 700 | 701 | When we apply it to a function $f : \mathsf{int} \to \mathsf{bool}$, we feel 702 | that $h \; f$ will proceed as follows: $h$ will \emph{ask} $f$ about the value 703 | $f \; x_0$ for some integer $x_0$. Depending on the result it gets, it will then 704 | ask some further question $f \; x_1$, and so on, until it provides an 705 | \emph{answer}~$a$. 706 | 707 | We may therefore represent such a functional $h$ as a \textbf{tree}: 708 | % 709 | \begin{itemize} 710 | \item 711 | the leaves are the answers 712 | \item 713 | a node is labeled by a question, which has two subtrees representing 714 | the two possible continuations (depending on the answer) 715 | \end{itemize} 716 | % 717 | We may encode this as the datatype: 718 | % 719 | \begin{verbatim} 720 | type tree = 721 | | Answer of bool 722 | | Question of int * tree * tree 723 | \end{verbatim} 724 | % 725 | Given such a tree, we can recreate the functional $h$: 726 | % 727 | \begin{verbatim} 728 | let rec tree2fun t f = 729 | match t with 730 | | Answer y -> y 731 | | Question (x, t1, t2) -> tree2fun (if f x then t1 else t2) f 732 | \end{verbatim} 733 | % 734 | Can we go backwards? Given $h$, how do we get the tree? It turns out this is not 735 | possible in a purely functional setting in general (but is possible for out 736 | specific case because $\mathsf{int} \to \mathsf{bool}$ is \emph{compact}, 737 | Google ``impossible functionals''), but it is with computational effects. 738 | 739 | \lstinputlisting{./eff-examples/fun_tree.eff} 740 | 741 | \hypertarget{problems-1}{% 742 | \subsection{Problems}\label{problems-1}} 743 | 744 | \begin{problem}[Breadth-first search] 745 | Implement the \emph{breadth-first search} strategy for ambivalent choice. 746 | \end{problem} 747 | 748 | \begin{problem}[Monte Carlo sampling] 749 | % 750 | The \href{http://www.eff-lang.org/try/}{online Eff} page has an example 751 | showing a handler which modifies a probabilistic computation (one that 752 | uses randomness) to one that computes the \emph{distribution} of 753 | results. The handler computes the distribution in an exhaustive way that 754 | quickly leads to inefficiency. 755 | 756 | Improve it by implement a 757 | \href{https://en.wikipedia.org/wiki/Monte_Carlo_method}{Monte Carlo} 758 | handler for estimating distributions of probabilistic computations. 759 | \end{problem} 760 | 761 | \begin{problem}[Recursive cows] 762 | Contemplate the 763 | \href{https://github.com/effect-handlers/effects-rosetta-stone/tree/master/examples/recursive-cow}{recursive 764 | cows}. 765 | \end{problem} 766 | 767 | \end{document} 768 | 769 | %%% Local Variables: 770 | %%% coding: utf-8 771 | %%% mode: latex 772 | %%% TeX-master: t 773 | %%% End: 774 | --------------------------------------------------------------------------------