├── .gitignore ├── examples ├── simple-state │ ├── multicore-ocaml │ │ ├── Makefile │ │ └── main.ml │ ├── eff │ │ ├── Makefile │ │ └── state.eff │ ├── koka │ │ ├── Makefile │ │ └── state.kk │ ├── frank │ │ ├── Makefile │ │ └── state.fk │ └── README.md ├── simple-binary-choice │ ├── eff │ │ ├── Makefile │ │ └── choice.eff │ ├── koka │ │ ├── Makefile │ │ └── choice.kk │ └── README.md ├── simple-exception │ ├── eff │ │ ├── Makefile │ │ └── exception.eff │ ├── koka │ │ ├── Makefile │ │ └── exception.kk │ └── README.md ├── generator-from-iterator │ ├── multicore-ocaml │ │ ├── Makefile │ │ └── main.ml │ └── README.md ├── generator-from-list │ ├── multicore-ocaml │ │ ├── Makefile │ │ └── main.ml │ ├── koka │ │ ├── Makefile │ │ ├── gen-strict.kk │ │ └── gen.kk │ └── README.md ├── pipes │ ├── frank │ │ ├── Makefile │ │ └── pipes.fk │ └── README.md └── recursive-cow │ ├── koka │ ├── Makefile │ └── cow.kk │ ├── eff │ ├── cow.eff │ └── Makefile │ ├── frank │ ├── Makefile │ └── cow.fk │ └── README.md ├── Makefile ├── .travis.sh ├── .travis.yml ├── LICENSE ├── .travis-ocaml.sh └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | **/koka/out 3 | -------------------------------------------------------------------------------- /examples/simple-state/multicore-ocaml/Makefile: -------------------------------------------------------------------------------- 1 | MULTICOREOCAML ?= ocaml 2 | 3 | all: 4 | $(MULTICOREOCAML) main.ml 5 | -------------------------------------------------------------------------------- /examples/simple-state/eff/Makefile: -------------------------------------------------------------------------------- 1 | EFF ?= eff 2 | 3 | ifeq ($(EFF),) 4 | RUN= 5 | else 6 | RUN=run 7 | endif 8 | 9 | all: $(RUN) 10 | 11 | .PHONY: run 12 | run: 13 | $(EFF) state.eff 14 | -------------------------------------------------------------------------------- /examples/simple-binary-choice/eff/Makefile: -------------------------------------------------------------------------------- 1 | EFF ?= eff 2 | 3 | ifeq ($(EFF),) 4 | RUN= 5 | else 6 | RUN=run 7 | endif 8 | 9 | all: $(RUN) 10 | 11 | .PHONY: run 12 | run: 13 | $(EFF) choice.eff 14 | -------------------------------------------------------------------------------- /examples/simple-exception/eff/Makefile: -------------------------------------------------------------------------------- 1 | EFF ?= eff 2 | 3 | ifeq ($(EFF),) 4 | RUN= 5 | else 6 | RUN=run 7 | endif 8 | 9 | all: $(RUN) 10 | 11 | .PHONY: run 12 | run: 13 | $(EFF) exception.eff 14 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TOPTARGETS := all clean 2 | 3 | PROGLANG ?= * 4 | 5 | SUBDIRS := $(wildcard examples/*/$(PROGLANG)/.) 6 | $(TOPTARGETS): $(SUBDIRS) 7 | $(SUBDIRS): 8 | $(MAKE) -C $@ $(MAKECMDGOALS) 9 | 10 | .PHONY: $(TOPTARGETS) $(SUBDIRS) 11 | -------------------------------------------------------------------------------- /examples/generator-from-iterator/multicore-ocaml/Makefile: -------------------------------------------------------------------------------- 1 | MULTICOREOCAML ?= ocaml 2 | 3 | ifeq ($(MULTICOREOCAML),) 4 | RUN= 5 | else 6 | RUN=run 7 | endif 8 | 9 | all: $(RUN) 10 | 11 | .PHONY: run 12 | run: 13 | $(MULTICOREOCAML) main.ml 14 | -------------------------------------------------------------------------------- /examples/generator-from-list/multicore-ocaml/Makefile: -------------------------------------------------------------------------------- 1 | MULTICOREOCAML ?= ocaml 2 | 3 | ifeq ($(MULTICOREOCAML),) 4 | RUN= 5 | else 6 | RUN=run 7 | endif 8 | 9 | all: $(RUN) 10 | 11 | .PHONY: run 12 | run: 13 | $(MULTICOREOCAML) main.ml 14 | -------------------------------------------------------------------------------- /examples/pipes/frank/Makefile: -------------------------------------------------------------------------------- 1 | FRANK ?= frank 2 | 3 | ifeq ($(FRANK),) 4 | RUN= 5 | else 6 | RUN=run 7 | endif 8 | 9 | all: $(RUN) 10 | 11 | .PHONY: run 12 | run: 13 | $(FRANK) ./pipes.fk --entry-point=example1 14 | $(FRANK) ./pipes.fk --entry-point=example2 15 | -------------------------------------------------------------------------------- /examples/recursive-cow/koka/Makefile: -------------------------------------------------------------------------------- 1 | # Assuming the Koka folder is a peer of effects-rosetta-stone 2 | KOKA ?= ../../../../koka/out/debug/koka-0.9.0-dev 3 | 4 | # pass --showtypes to see the inferred types 5 | KOKAOPTS ?= -v 6 | 7 | all: 8 | $(KOKA) $(KOKAOPTS) cow.kk 9 | -------------------------------------------------------------------------------- /examples/simple-state/koka/Makefile: -------------------------------------------------------------------------------- 1 | # Assuming the Koka folder is a peer of effects-rosetta-stone 2 | KOKA ?= ../../../../koka/out/debug/koka-0.9.0-dev 3 | 4 | # pass --showtypes to see the inferred types 5 | KOKAOPTS ?= -v 6 | 7 | all: 8 | $(KOKA) $(KOKAOPTS) state.kk 9 | -------------------------------------------------------------------------------- /examples/simple-binary-choice/koka/Makefile: -------------------------------------------------------------------------------- 1 | # Assuming the Koka folder is a peer of effects-rosetta-stone 2 | KOKA ?= ../../../../koka/out/debug/koka-0.9.0-dev 3 | 4 | # pass --showtypes to see the inferred types 5 | KOKAOPTS ?= -v 6 | 7 | all: 8 | $(KOKA) $(KOKAOPTS) choice.kk 9 | -------------------------------------------------------------------------------- /examples/simple-exception/koka/Makefile: -------------------------------------------------------------------------------- 1 | # Assuming the Koka folder is a peer of effects-rosetta-stone 2 | KOKA ?= ../../../../koka/out/debug/koka-0.9.0-dev 3 | 4 | # pass --showtypes to see the inferred types 5 | KOKAOPTS ?= -v 6 | 7 | all: 8 | $(KOKA) $(KOKAOPTS) exception.kk 9 | -------------------------------------------------------------------------------- /examples/simple-state/frank/Makefile: -------------------------------------------------------------------------------- 1 | FRANK ?= frank 2 | 3 | ifeq ($(FRANK),) 4 | RUN= 5 | else 6 | RUN=run 7 | endif 8 | 9 | all: $(RUN) 10 | 11 | .PHONY: run 12 | run: 13 | $(FRANK) ./state.fk --entry-point=example1 14 | $(FRANK) ./state.fk --entry-point=example2 15 | -------------------------------------------------------------------------------- /examples/generator-from-list/koka/Makefile: -------------------------------------------------------------------------------- 1 | # Assuming the Koka folder is a peer of effects-rosetta-stone 2 | KOKA ?= ../../../../koka/out/debug/koka-0.9.0-dev 3 | 4 | # pass --showtypes to see the inferred types 5 | KOKAOPTS ?= -v 6 | 7 | all: 8 | $(KOKA) $(KOKAOPTS) gen.kk 9 | $(KOKA) $(KOKAOPTS) gen-strict.kk 10 | -------------------------------------------------------------------------------- /examples/recursive-cow/eff/cow.eff: -------------------------------------------------------------------------------- 1 | (* A cow is an operation which return a function of type [unit -> unit] *) 2 | effect Cow : (unit -> unit) 3 | 4 | (* How many times does a cow moo? *) 5 | let cow = 6 | handle 7 | (perform Cow) () 8 | with 9 | | effect Cow k -> print_string "moo " ; k (fun () -> (perform Cow) ()) 10 | -------------------------------------------------------------------------------- /examples/recursive-cow/eff/Makefile: -------------------------------------------------------------------------------- 1 | EFF ?= eff 2 | 3 | ifeq ($(EFF),) 4 | RUN= 5 | else 6 | RUN=run 7 | ifeq ($(TRAVIS),) 8 | RUN_COMMAND=$(EFF) cow.eff 9 | else 10 | RUN_COMMAND=($(EFF) cow.eff) & sleep 1; kill $$! 2> /dev/null || : 11 | endif 12 | endif 13 | 14 | all: $(RUN) 15 | 16 | .PHONY: run 17 | run: 18 | $(RUN_COMMAND) 19 | -------------------------------------------------------------------------------- /examples/simple-state/README.md: -------------------------------------------------------------------------------- 1 | # Simple state 2 | 3 | The standard state monad implements a computation of type `T` which uses state 4 | of type `S` as a state-carrying function `S → S × T`. In this example we 5 | implement the monad and two simple examples: 6 | 7 | * compute `(10 + 4) × 3` using state 8 | * add numbers from `1` to `100` by adding them to the state 9 | -------------------------------------------------------------------------------- /examples/recursive-cow/frank/Makefile: -------------------------------------------------------------------------------- 1 | FRANK ?= frank 2 | 3 | ifeq ($(FRANK),) 4 | RUN= 5 | else 6 | RUN=run 7 | ifeq ($(TRAVIS),) 8 | RUN_COMMAND=$(FRANK) cow.fk 9 | else 10 | RUN_COMMAND=($(FRANK) cow.fk) & sleep 1; kill $$! 2> /dev/null || : 11 | endif 12 | endif 13 | 14 | 15 | ifeq ($(FRANK),) 16 | RUN= 17 | else 18 | RUN=run 19 | endif 20 | 21 | all: $(RUN) 22 | 23 | .PHONY: run 24 | run: 25 | $(RUN_COMMAND) 26 | -------------------------------------------------------------------------------- /examples/simple-exception/README.md: -------------------------------------------------------------------------------- 1 | # Simple exception 2 | 3 | An exception is an algebraic operation which of empty arity. This causes it to 4 | be non-resumable, since there is no element of the empty type that we could pass to the continuation. 5 | 6 | In this example we implement a simple exception `Abort` and show how it can be 7 | handled. For an implementation to be truly correct, it must be *impossible* to 8 | resume the continuation. 9 | 10 | -------------------------------------------------------------------------------- /examples/simple-binary-choice/README.md: -------------------------------------------------------------------------------- 1 | # Simple binary choice 2 | 3 | Non-deterministic binary choice is an operation which returns a boolean value. There are many interesting ways to handle such an operation. Here we demonstrate basic ones: 4 | 5 | * always choose `false` 6 | * collect all choices 7 | * choose the bit which maximizes the result 8 | 9 | In the examples, we use these handles to handle the computation which assigns 10 | `x` either 10 or 15, assigns `y` either 5 or 10, and returns the difference of 11 | `x - y`. 12 | -------------------------------------------------------------------------------- /.travis.sh: -------------------------------------------------------------------------------- 1 | set -uex 2 | 3 | if [ "$KIND" = "ocaml" ]; then 4 | #Multicore OCaml 5 | sh .travis-ocaml.sh 6 | export OPAMYES=1 7 | eval $(opam config env) 8 | opam install -y ocamlfind ocamlbuild 9 | 10 | #Eff 11 | opam switch 4.02.3 12 | eval $(opam config env) 13 | opam pin add -k git eff https://github.com/matijapretnar/eff.git 14 | opam install -y eff 15 | 16 | #Make the files 17 | TRAVIS=true MULTICOREOCAML=$HOME/.opam/4.06.1+multicore/bin/ocaml EFF=$HOME/.opam/4.02.3/bin/eff FRANK= make all 18 | elif [ "$KIND" = "haskell" ]; then 19 | echo "nothing to do" 20 | else 21 | echo "Unknown build kind" 22 | exit 1 23 | fi 24 | -------------------------------------------------------------------------------- /examples/simple-binary-choice/eff/choice.eff: -------------------------------------------------------------------------------- 1 | effect Choose : bool 2 | 3 | (* We handle the following computation in several ways. *) 4 | let f () = 5 | let x = if (perform Choose) then 10 else 15 in 6 | let y = if (perform Choose) then 5 else 10 in 7 | x - y 8 | 9 | (* Always pick [false] *) 10 | let example1 = 11 | handle 12 | f () 13 | with 14 | | effect Choose k -> k false 15 | 16 | (* Colllect all choices into a list (@ is list concatenation) *) 17 | let example2 = 18 | handle 19 | f () 20 | with 21 | | x -> [x] 22 | | effect Choose k -> (k false) @ (k true) 23 | 24 | (* Pick the choice which maximizes the result. *) 25 | let example3 = 26 | handle 27 | f () 28 | with 29 | | x -> x 30 | | effect Choose k -> max (k false) (k true) 31 | -------------------------------------------------------------------------------- /examples/recursive-cow/README.md: -------------------------------------------------------------------------------- 1 | # Recursive cow 2 | 3 | Handlers can hide recursion in the effect signature. We can abuse this to write 4 | a program that superficially looks like it is terminating but it actually 5 | cycles. Here is one such program: 6 | 7 | 1. Declare an operation `Cow` that returns a function of type `unit -> unit`. 8 | 9 | 2. Run the computation 10 | 11 | Cow () 12 | 13 | which obtains a function `unit -> unit` from the handler and activates it. 14 | 15 | 3. Handle the above computation with the handler which handles `Cow` as follows: 16 | print `"moo "` and pass as the result the function `λ () . Cow ()`. 17 | 18 | If we track the effect information, we will see that `Cow` should be typed as 19 | 20 | Cow : (unit → unit ! Cow) 21 | 22 | and therein lies recursion. 23 | -------------------------------------------------------------------------------- /examples/simple-exception/eff/exception.eff: -------------------------------------------------------------------------------- 1 | effect Abort : empty 2 | 3 | (* A function which calls itself [n] times and raises [Abort] *) 4 | let rec deep = function 5 | | 0 -> perform Abort 6 | | n -> deep (n - 1) 7 | 8 | (* Raise an exception from within nested recursive calls. *) 9 | let example1 = 10 | handle 11 | (* does it work even if we change 42 to something big? *) 12 | deep 42 ; 13 | (* we should never get here! *) 14 | 13 15 | with 16 | | effect Abort _ -> 42 17 | 18 | (* Raise an exception, catch it, and re-raise it. *) 19 | let example2 = 20 | handle 21 | handle 22 | let x = 2 in 23 | perform Abort ; 24 | let y = 3 in 25 | x + y 26 | with 27 | | effect Abort _ -> perform Abort ; 13 28 | with 29 | | effect Abort _ -> 42 30 | -------------------------------------------------------------------------------- /examples/simple-state/frank/state.fk: -------------------------------------------------------------------------------- 1 | {-- Declaration of the operations for state --} 2 | interface State S = get : S | set : S -> Unit 3 | 4 | {-- The standard state handler --} 5 | state : {S -> X -> X} 6 | state _ x = x 7 | state s k> = state s (k s) 8 | state _ k> = state s (k unit) 9 | 10 | {-- Example 1: compute the answer --} 11 | example1 : {Int} 12 | example1! = state 10 (let x = get! in set (4 + x); get! + get! + get!) 13 | 14 | {-- Example 2: add numbers from 1 to 100 --} 15 | example2 : {Int} 16 | example2! = state 0 (loop 100; get!) 17 | 18 | {-- Frank doesn't yet support local definitions --} 19 | add_to : {Int -> [State Int]Unit} 20 | add_to k = set (k + get!) 21 | 22 | loop : {Int -> [State Int]Unit} 23 | loop 0 = unit 24 | loop k = add_to k; loop (k - 1) 25 | -------------------------------------------------------------------------------- /examples/generator-from-list/multicore-ocaml/main.ml: -------------------------------------------------------------------------------- 1 | let make_generator (type a) (t : a list) : (unit -> a option) = 2 | let module M = struct effect Yield : a -> unit end in 3 | let open M in 4 | let step = ref (fun () -> assert false) in 5 | let first_step () = 6 | try 7 | List.iter (fun x -> perform (Yield x)) t; 8 | None 9 | with effect (Yield v) k -> 10 | step := continue k; 11 | Some v 12 | in 13 | step := first_step; 14 | fun () -> !step () 15 | 16 | let l1 = [1;2;3;4] 17 | let l2 = ["a";"b";"c";"d"] 18 | 19 | let example = 20 | let g1 = make_generator l1 in 21 | let g2 = make_generator l2 in 22 | let rec loop () = 23 | match g1(), g2() with 24 | | Some v1, Some v2 -> (v1, v2) :: loop () 25 | | _ -> [] 26 | in 27 | loop () 28 | ;; 29 | 30 | assert ([(1,"a"); (2,"b"); (3,"c"); (4,"d")] = example) 31 | -------------------------------------------------------------------------------- /examples/simple-binary-choice/koka/choice.kk: -------------------------------------------------------------------------------- 1 | // Declare as `control` (versus `fun` or `val`) is because we need 2 | // an explicit `resume` in the operation definition (i.e. we modify control) 3 | ambient control choose() : bool 4 | 5 | // example computation 6 | fun f() { 7 | val x = if (choose()) then 10 else 15 8 | val y = if (choose()) then 5 else 10 9 | x - y 10 | } 11 | 12 | // always pick false 13 | fun example1() { 14 | with fun choose() { False } // just an ambient function; no control modification 15 | f() 16 | } 17 | 18 | // collect all choices into a list 19 | fun example2() { 20 | with control choose() { (resume(False) + resume(True) : list) } 21 | [f()] 22 | } 23 | 24 | // pick the choice that maximizes the result 25 | fun example3() { 26 | with control choose() { (max(resume(False),resume(True)) : int) } 27 | f() 28 | } 29 | 30 | fun main() { 31 | example1().println 32 | example2().show-list(show).println 33 | example3().println 34 | } 35 | -------------------------------------------------------------------------------- /examples/generator-from-list/README.md: -------------------------------------------------------------------------------- 1 | # Generator from list 2 | 3 | The goal of this example is to make a generator from a list. Dynamic creation of 4 | an effect (instance) is required, since every list gets its own generator. 5 | 6 | Specifically, a **generator** for a list `[x₁, …, xᵢ]` is a map `1 → Option α` 7 | which returns the elements `x₁`, …, `xᵢ` in turn on successive calls (and 8 | nothing after that). 9 | 10 | We implement a function `make_generator : List α → (1 → Option α)` which uses 11 | the iterator over the list to make the generator. 12 | 13 | An important point about this example is that `make_generator` is polymorphic, 14 | and therefore it has to dynamically create effects at different types. 15 | 16 | To test the function, we create two generators 17 | 18 | g1 = make_generator [1, 2, 3, 4] 19 | g2 = make_generator ["a", "b", "c", "d"] 20 | 21 | and simultaneously consume them to create the list of pairs 22 | 23 | [(1,"a"), (2, "b"), (3, "c"), (4, "d")] 24 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | matrix: 2 | include: 3 | 4 | # Multicore OCaml 5 | - language: c 6 | sudo: required 7 | script: bash -ex .travis.sh 8 | os: 9 | - linux 10 | env: 11 | - PROGLANG=multicore-ocaml 12 | - KIND=ocaml 13 | 14 | # Eff 15 | - language: c 16 | sudo: required 17 | script: bash -ex .travis.sh 18 | os: 19 | - linux 20 | env: 21 | - PROGLANG=eff 22 | - KIND=ocaml 23 | 24 | # Frank 25 | - language: c 26 | sudo: required 27 | script: bash -ex .travis.sh 28 | os: 29 | - linux 30 | env: 31 | - PROGLANG=frank 32 | - KIND=haskell 33 | 34 | # Scala Effekt 35 | - language: scala 36 | addons: 37 | apt: 38 | packages: 39 | - oracle-java8-installer 40 | jvm: 41 | - oraclejdk8 42 | script: make all 43 | cache: 44 | directories: 45 | - $HOME/.ivy2/cache 46 | - $HOME/.sbt 47 | env: 48 | - PROGLANG=scala-effekt 49 | -------------------------------------------------------------------------------- /examples/simple-exception/koka/exception.kk: -------------------------------------------------------------------------------- 1 | // Declare an abort effect and operation that is polymorphic in its 2 | // result type. That ensures we can never resume as inside the operation 3 | // it becomes an abstract existential type and we cannot provide an 4 | // argument of such type. Another possibility is to give `abort` the `:void` 5 | // type result which has no inhabitants. 6 | ambient control abort() : a 7 | 8 | // Function that recurses `n` times and then calls `abort` 9 | fun deep( n : int ) { 10 | if (n <= 0) then abort() else deep(n - 1) 11 | } 12 | 13 | // Handle abort. 14 | fun example1() { 15 | with control abort() { 42 } // on abort, return 42 16 | deep(100000) // large `n` works too 17 | 13 // we never get here 18 | } 19 | 20 | // abort, catch it, and re-throw it 21 | fun example2() { 22 | with control abort() { 42 } 23 | with control abort() { abort(); 13 } // this abort goes to the outer handler 24 | val x = 2 25 | abort() 26 | val y = 3 27 | x+y 28 | } 29 | 30 | fun main() { 31 | example1().println 32 | example2().println 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Andrej Bauer 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 | -------------------------------------------------------------------------------- /examples/simple-state/eff/state.eff: -------------------------------------------------------------------------------- 1 | (* Declaration of the operations for state *) 2 | effect Get: int 3 | effect Set: int -> unit 4 | 5 | (* The standard state handler. Note that here we define a function 6 | which takes the initial state as an argument and returns the 7 | handler. 8 | *) 9 | let state initial = handler 10 | | y -> (fun _ -> y) 11 | | effect Get k -> (fun s -> (continue k s) s) 12 | | effect (Set s) k -> (fun _ -> (continue k ()) s) 13 | | finally f -> f initial 14 | ;; 15 | 16 | (* Example 1: compute the answer *) 17 | let example1 = 18 | 19 | with state 10 handle 20 | let x = perform Get in 21 | perform (Set (4 + x)) ; 22 | 3 * perform Get 23 | ;; 24 | 25 | (* Example 2: add numbers from 1 to 100 *) 26 | let example2 = 27 | 28 | (* add k to the state *) 29 | let add_to k = perform (Set (k + perform Get)) in 30 | 31 | (* add numbers k, k-1, ..., 2, 1 to the state *) 32 | let rec loop = function 33 | | 0 -> () 34 | | k -> add_to k ; loop (k - 1) 35 | in 36 | 37 | (* run the loop and read the final state *) 38 | with state 0 handle 39 | loop 100 ; 40 | perform Get 41 | ;; 42 | -------------------------------------------------------------------------------- /examples/simple-state/multicore-ocaml/main.ml: -------------------------------------------------------------------------------- 1 | (* Declaration of the operations for state *) 2 | effect Get: int 3 | effect Set: int -> unit 4 | 5 | (* The standard state handler *) 6 | let run_state f init = 7 | let comp = 8 | match f () with 9 | | v -> (fun _ -> v) 10 | | effect (Set s) k -> (fun _ -> (continue k ()) s) 11 | | effect Get k -> (fun s -> (continue k s) s) 12 | in 13 | comp init 14 | 15 | (* Example 1: compute the answer *) 16 | let example1 = 17 | let f () = 18 | let x = perform Get in 19 | perform (Set (4 + x)); 20 | 3 * perform Get 21 | in 22 | run_state f 10 23 | ;; 24 | 25 | assert (example1 = 42);; 26 | 27 | (* Example 2: add numbers from 1 to 100 *) 28 | let example2 = 29 | 30 | (* add v to the state *) 31 | let add_to v = perform (Set (v + perform Get)) in 32 | 33 | (* add numbers v, v-1, ..., 2, 1 to the state *) 34 | let rec loop = function 35 | | 0 -> () 36 | | v -> add_to v; loop (v -1) 37 | in 38 | 39 | (* run the loop and read the final state *) 40 | run_state (fun () -> 41 | loop 100; 42 | perform Get) 0 43 | ;; 44 | 45 | assert (example2 = 5050) 46 | -------------------------------------------------------------------------------- /examples/recursive-cow/frank/cow.fk: -------------------------------------------------------------------------------- 1 | {-- begin standard stuff --} 2 | map : {{X -> Y} -> List X -> List Y} 3 | map f [] = [] 4 | map f (x :: xs) = f x :: map f xs 5 | 6 | print : {String -> [Console]Unit} 7 | print s = map ouch s; unit 8 | {-- end standard stuff --} 9 | 10 | {-- Frank's bidirectional effect type system requires that the recursion 11 | inherent in the recursive cow be made explicit --} 12 | interface Cow = moo : {[Cow]Unit} 13 | 14 | {-- How many times does a cow moo? --} 15 | cow : {[Cow]Unit} 16 | cow! = (moo!)! 17 | 18 | eat : {Unit -> [Console]Unit} 19 | eat k> = print "moo "; eat (k cow) 20 | eat unit = unit 21 | 22 | main : {[Console]Unit} 23 | main! = eat cow! 24 | 25 | 26 | {-- 27 | 28 | Remarks: 29 | 30 | * The Cow interface has an implicit effect parameter by virtue of the 31 | thunk in the moo command being implicitly effect-polymorphic. The 32 | implicit effect parameter (if any) is always the last one for 33 | interfaces and data types. 34 | 35 | If we wanted to be completely explicit then we'd write: 36 | 37 | interface Cow [E] = moo : {[E | Cow [E |] ]Unit} 38 | 39 | * In the eat handler we must instantiate the implicit effect with an 40 | ability that includes the Console interface in order to be able to 41 | print. As ever in Frank, this ability is itself effect-polymorphic. 42 | 43 | --} 44 | -------------------------------------------------------------------------------- /examples/generator-from-iterator/multicore-ocaml/main.ml: -------------------------------------------------------------------------------- 1 | (* A list iterator *) 2 | let list_iter lst f = 3 | let rec iter = function 4 | | [] -> () 5 | | x :: xs -> f x ; iter xs 6 | in 7 | iter lst 8 | 9 | (* A tree iterator *) 10 | type 'a tree = Empty | Node of 'a * 'a tree * 'a tree 11 | 12 | let leaf x = Node (x, Empty, Empty) 13 | 14 | let tree_iter tree f = 15 | let rec iter = function 16 | | Empty -> () 17 | | Node (x, l, r) -> iter l ; f x ; iter r 18 | in 19 | iter tree 20 | 21 | (* A polymorphic generator from iterator *) 22 | let make_generator (type a) (iter : (a -> unit) -> unit) : (unit -> a option) = 23 | let module M = struct effect Yield : a -> unit end in 24 | let open M in 25 | let step = ref (fun () -> assert false) in 26 | let first_step () = 27 | try 28 | iter (fun x -> perform (Yield x)) ; 29 | None 30 | with effect (Yield v) k -> 31 | step := continue k; 32 | Some v 33 | in 34 | step := first_step; 35 | fun () -> !step () 36 | 37 | (* Example *) 38 | let example = 39 | 40 | let it1 = list_iter [10; 20; 30; 40; 50] in 41 | let it2 = tree_iter (Node (5, Node (3, leaf 1, leaf 4), leaf 6)) in 42 | let g1 = make_generator it1 in 43 | let g2 = make_generator it2 in 44 | let rec loop () = 45 | match g1(), g2() with 46 | | Some x, Some y -> (x, y) :: loop () 47 | | _, _ -> [] 48 | in 49 | loop () 50 | -------------------------------------------------------------------------------- /examples/pipes/README.md: -------------------------------------------------------------------------------- 1 | # Pipes 2 | 3 | Standard deep handlers hardwire a particular structural recursion 4 | scheme over a computation tree - namely a *fold* or *catamorphism*. 5 | 6 | Shallow handlers, in contrast, perform a case split on the computation 7 | tree, so the programmer has to make explicit recursive calls. This 8 | however has the advantage that other recursion schemes are naturally 9 | expressed. 10 | 11 | Pipes provide such an example. They are naturally expressed using 12 | mutual recursion. 13 | 14 | ## The handler(s) 15 | 16 | Given two computations 17 | 18 | M : 1 ! {send : S -> 1} 19 | N : A ! {receive : 1 -> S} 20 | 21 | the handler (or handlers) should handle them synchronously such that 22 | messages sent by M are received by N, and if N tries to receive when M 23 | has finished an exception `abort ()` is raised: 24 | 25 | pipe M N : {abort : forall a.1 -> a} ! A 26 | 27 | ## Example 1 28 | 29 | 1. Implement a computation `sends` that sends a list of values. 30 | 31 | 2. Implement a computation `catter` that receives and concatenates a 32 | nil-terminated list of lists of values. 33 | 34 | 3. Pipe a nil-terminated list of strings between `sends` and `catter`. 35 | 36 | ## Example 2 37 | 38 | 1. Define a spacer computation of type 39 | 40 | 1 ! {receive : 1 -> String; send : String -> 1} 41 | 42 | that repeatedly receives a string and then sends the string followed 43 | by a space. 44 | 45 | 2. Interpose the spacer between `sends` and `catter` using two pipes. 46 | -------------------------------------------------------------------------------- /examples/generator-from-list/koka/gen-strict.kk: -------------------------------------------------------------------------------- 1 | // This file mimics the Multicore-OCaml version closest. Since we have 2 | // effect types, we can see that it is stateful `st` in a heap `h` to 3 | // implement the generator. That is not great, but it is more flexible as 4 | // the returned stepper function is not limited to the lexical scope. 5 | // Note that it is inferred that `main` is not stateful anymore (since 6 | // heap `h` no longer occurs in any types when generalizing `main` and 7 | // can thus no longer be observed) 8 | 9 | // We declare as `control` (versus `fun` or `val`) since we need `resume` inside `yield` 10 | ambient control yield(x:a) : () 11 | 12 | fun generator( xs : list ) : > (() -> ,div|e> maybe) { 13 | val todo = ref(fun(){ Nothing }) // create a mutable reference 14 | fun first-step() { 15 | with control yield(x) { 16 | todo := (fun(){ resume(()) }) // store the resumption for next time 17 | Just(x) // and return 18 | } 19 | xs.foreach( yield ) 20 | Nothing 21 | } 22 | todo := first-step 23 | (fun(){ (!todo)() }) 24 | } 25 | 26 | fun main() : () { // inferred 27 | val g1 = [1,2,3,4].generator 28 | val g2 = ['a','b','c','d'].generator 29 | fun zipstep() { 30 | match((g1(),g2())) { 31 | (Just(x),Just(y)) -> Cons((x,y), zipstep()) // keep stepping until done 32 | _ -> Nil 33 | } 34 | } 35 | zipstep().show.println() 36 | } 37 | 38 | 39 | fun show( xs : list<(int,char)> ) : string { 40 | xs.show-list( fun(x) { "(" + x.fst.show + "," + x.snd.show + ")" }) 41 | } 42 | -------------------------------------------------------------------------------- /examples/generator-from-iterator/README.md: -------------------------------------------------------------------------------- 1 | # Generator from iterator 2 | 3 | The goal of this example is to make a generator from an iterator. Dynamic 4 | creation of effect instances is required, since each iterator gets its own 5 | effect. 6 | 7 | Since there is a lot of confusion regarding precise definition of terms, let us 8 | make them concrete. Given a collection of elements `x₁`, …, `xᵢ` (presumably 9 | stored in a container such as a list or a tree), 10 | 11 | * an **iterator** is a function of type `(a → 1) → 1` that applies the given 12 | function to each element `x₁`, …, `xᵢ`, 13 | * a **generator** is a function of type `1 → Option a` which retuns a 14 | function that can successively yields the elements `x₁`, …, `xᵢ` upon 15 | repeated applications. 16 | 17 | We implement a function 18 | 19 | make_generator : ((α → 1) → 1) → (1 → Option α) 20 | 21 | If possible, the function should be polymorphic in `α`, otherwise instantiate it 22 | at `int` so that it is applicable to the example below. 23 | 24 | We test the function as follows: 25 | 26 | 1. Create an iterator `i1` for the list `[10,20,30,40,50]`. 27 | 2. Create a left-to-right iterator `i2` for the tree 28 | 29 | 5 30 | / \ 31 | 3 6 32 | / \ 33 | 1 4 34 | 35 | This step may require a simple implementation of trees, which is fine. 36 | 37 | 3. Create generators `g1` and `g2` from `i1` and `i2`, respectively. 38 | 4. Use the generators to simultaneously traverse the list and the tree. 39 | For each `x` from the list and `y` from the tree, compute `x + y` 40 | and accumulate the sums in a list. 41 | 5. Return the list. 42 | -------------------------------------------------------------------------------- /examples/recursive-cow/koka/cow.kk: -------------------------------------------------------------------------------- 1 | // Declare a non-(co)inductive effect `:moo` with one operation `moo` 2 | ambient rec fun moo() : (() -> ()) 3 | 4 | fun main() { // inferred type: () -> () 5 | with fun moo() { // bind `moo` dynamically over the rest of the scope 6 | println("moo") 7 | (fun(){ moo()() }) // return (=tail-resume) with a function that calls `moo` again 8 | } 9 | moo()() // and call moo 10 | } 11 | 12 | 13 | /* -------------------------------------------------------------------- 14 | The above ambient declaration is equivalent to: 15 | > effect rec moo { 16 | > fun moo() : (() -> ) () 17 | > } 18 | 19 | Since Koka tracks potential divergence as an effect (modeled 20 | as an ambient `fix` combinator), we need to declare the `:moo` 21 | effect as an arbitrarily recursive type. If we leave it out, 22 | the compiler complains: 23 | 24 | test\algeff\eff-rec1a.kk(1, 1): error: Type .ops-moo is declared 25 | as being (co)inductive but it occurs recursively in a negative position. 26 | hint: declare it as a 'type rec' (or 'effect rec)' to allow negative occurrences 27 | 28 | Similarly, we need to declare the divergence effect in the `moo` 29 | operation signature or we get an error when we try to resume. 30 | If we leave it out, we get: 31 | 32 | test\algeff\eff-rec1a.kk(6,13): error: types do not match 33 | context : resume( { moo()() } ) 34 | term : { moo()() } 35 | inferred type: () -> () 36 | expected type: () -> moo () 37 | 38 | Good! We won't accidentally write a crazy cow program :-) 39 | */ 40 | -------------------------------------------------------------------------------- /examples/generator-from-list/koka/gen.kk: -------------------------------------------------------------------------------- 1 | // See `main-strict` for a closer copy of the multicore-Ocaml example, 2 | // However, we think the version shown here is nicer as it does not use heap mutation, 3 | // (but less flexible as the generators are now scoped). 4 | 5 | // Declare an ambient instance. These are essentially named handlers. 6 | // Usually you declare them under an umbrella effect, like `heap` for 7 | // references, or `file-system` for files, but you can leave it out (as we 8 | // do here) for trivial cases and then it defaults to the `inst` effect 9 | // which is handled by `main`. 10 | // So, the type of `step` becomes `forall (gen) -> maybe` 11 | // Unfortunately with `exn` as we currently do not check statically if 12 | // an instance name escapes its dynamic scope. 13 | ambient instance gen { fun step() : maybe } 14 | 15 | fun generator( xs : list, action : gen -> e b ) : e b { 16 | var todo := xs // local unobservable state (not heap allocated, it works with backtracking too) 17 | with g = instance fun step() { // fresh instance binding named `g` 18 | match(todo) { 19 | Nil { Nothing } 20 | Cons(x,xx) { todo := xx; Just(x) } // update and return (=resume) 21 | } 22 | } 23 | action(g) 24 | } 25 | 26 | fun main() : () { // inferred 27 | with g1 = [1,2,3,4].generator // this `with` just passes the rest of the scope as a function: `generator([1,2,3,4], fun(g1){...})` 28 | with g2 = ['a','b','c','d'].generator 29 | fun zipstep() { 30 | match((g1.step(),g2.step())) { 31 | (Just(x),Just(y)) -> Cons((x,y), zipstep()) // keep stepping until done 32 | _ -> Nil 33 | } 34 | } 35 | zipstep().show.println() 36 | } 37 | 38 | 39 | fun show( xs : list<(int,char)> ) : string { 40 | xs.show-list( fun(x) { "(" + x.fst.show + "," + x.snd.show + ")" }) 41 | } 42 | -------------------------------------------------------------------------------- /examples/pipes/frank/pipes.fk: -------------------------------------------------------------------------------- 1 | {--- Pipes: multihandlers ---} 2 | 3 | {-- begin standard stuff --} 4 | append : {List X -> List X -> List X} 5 | append [] ys = ys 6 | append (x :: xs) ys = x :: (append xs ys) 7 | 8 | map : {{X -> Y} -> List X -> List Y} 9 | map f [] = [] 10 | map f (x :: xs) = f x :: map f xs 11 | {-- end standard stuff --} 12 | 13 | 14 | interface Abort = abort X : X 15 | interface Send X = send : X -> Unit 16 | interface Receive X = receive : X 17 | 18 | -- send a list of values 19 | sends : {List X -> [Send X]Unit} 20 | sends xs = map send xs; unit 21 | 22 | -- receive and concatenate a nil-terminated list of lists 23 | catter : {[Receive (List X)]List X} 24 | catter! = case receive! { [] -> [] 25 | | xs -> append xs catter!} 26 | 27 | -- pipe as a multihandler 28 | pipe : {Unit -> Y -> [Abort]Y} 29 | pipe s> r> = pipe (s unit) (r x) 30 | pipe <_> y = y 31 | pipe unit r> = abort! 32 | 33 | -- a simple pipe 34 | example1 : {[Abort]String} 35 | example1! = pipe (sends ["do", "be", "do", "be", "do", ""]) catter! 36 | 37 | spacer : [Send String, Receive String]Unit 38 | spacer! = send receive!; send " "; spacer! 39 | 40 | -- pipes compose 41 | example2 : {[Abort]String} 42 | example2! = pipe (sends ["do", "be", "do", "be", "do", ""]) (pipe spacer! catter!) 43 | 44 | 45 | {-- 46 | 47 | Remarks: 48 | 49 | * Abort is a polymorphic command. 50 | 51 | * Multihandlers support a particularly concise implementation of 52 | pipes. 53 | 54 | * The <_> pattern matches all commands and all values specified by 55 | the type of the corresponding port. In the case of the pipe 56 | multihandler it matches the send command and any value. It is never 57 | possible to match a command that is not mentioned in the type of the 58 | corresponding port. 59 | 60 | * The pattern y matches only values. 61 | 62 | --} 63 | -------------------------------------------------------------------------------- /examples/simple-state/koka/state.kk: -------------------------------------------------------------------------------- 1 | // A state effect that is polymorphic in the state of type `s` 2 | effect state { 3 | fun get() : s 4 | fun put( x : s) : () 5 | } 6 | 7 | // The standard state handler as a state monad. 8 | // Note: you would never do this in practice in Koka, 9 | // but instead use local variables or parameterized handlers instead (see below) 10 | // Inferred type: `forall (init: s, action: () -> |e> a) -> e a` 11 | fun state(init,action) { 12 | (handle(action) { 13 | get() -> fun(st){ resume(st)(st) } 14 | put(x) -> fun(st){ resume(())(x) } 15 | return x -> fun(_st){ x } 16 | })(init) 17 | } 18 | 19 | fun example1() { 20 | // An anonymous function without arguments can be written 21 | // with just braces (and passed outside of the argument parenthesis) 22 | state(10) { 23 | put(4 + get()) 24 | 3*get() 25 | } 26 | } 27 | 28 | fun example2() { 29 | // The `with` syntactic sugar makes the rest of the scope 30 | // into an anonymous function argument, and the following is 31 | // equivalent to the previous example. 32 | with state(10) 33 | put(4 + get()) 34 | 3*get() 35 | } 36 | 37 | fun example3() { 38 | with state(0) 39 | for(1,100) fun(x){ // `for` is just a library function 40 | put(get() + x) 41 | } 42 | get() 43 | } 44 | 45 | // -------------------------------------------------------------------- 46 | // The recommended way to have stateful handlers is to use ambient functions 47 | // with a mutable local variable (which is builtin using the semantics of `state`). 48 | ambient fun read() : s 49 | ambient fun write( x : s) : () 50 | 51 | // Inferred type: forall (init : s, action : () -> ,write|e> a) -> e a 52 | fun astate(init,action) { 53 | var s := init // local unobservable state 54 | with fun read() { s } // `with` is also used to dynamically bind ambients over the rest of the scope 55 | with fun write(x) { s := x } 56 | action() 57 | } 58 | 59 | fun example4() { 60 | with astate(10) 61 | write(4 + read()) 62 | 3*read() 63 | } 64 | 65 | // -------------------------------------------------------------------- 66 | // Yet another cool way to encode state which unfortunately 67 | // uses more and more stack space unless we improve on our 68 | // runtime system to remove handlers followed by a mask. 69 | ambient val peek : s 70 | ambient control poke(x : s) : () 71 | 72 | fun cstate(init,action) { 73 | with val peek = init // initial state as a value 74 | with control poke(x) { 75 | with mask // mask `:peek` to ignore the previous/inner peek binding 76 | with val peek = x // re-bind `peek` to the new state 77 | resume(()) // and resume under the new binding for `peek` 78 | } 79 | action() 80 | } 81 | 82 | fun example5() { 83 | with cstate(10) 84 | poke(4 + peek) 85 | 3*peek 86 | } 87 | 88 | // -------------------------------------------------------------------- 89 | // Here we use a parameterised handler with a local state `st` 90 | // The last argument to `resume` contains the new local state 91 | // under which to resume. This is much more efficient than 92 | // the previous `state` handler that builds explicit functions. 93 | // Inferred type: `forall (st : s, action : () -> |e> a) -> e a` 94 | val pstate = handler(st) { 95 | get() -> resume(st,st) 96 | put(x) -> resume((),x) 97 | } 98 | 99 | fun example6() { 100 | with pstate(10) 101 | put(4 + get()) 102 | 3*get() 103 | } 104 | 105 | fun main() { 106 | example1().println 107 | example2().println 108 | example3().println 109 | example4().println 110 | example5().println 111 | example6().println 112 | } 113 | -------------------------------------------------------------------------------- /.travis-ocaml.sh: -------------------------------------------------------------------------------- 1 | ## basic OCaml and opam installation 2 | 3 | full_apt_version () { 4 | package=$1 5 | version=$2 6 | case "${version}" in 7 | latest) echo -n "${package}" ;; 8 | *) echo -n "${package}=" 9 | apt-cache show "$package" \ 10 | | sed -n "s/^Version: \(${version}\)/\1/p" \ 11 | | head -1 12 | esac 13 | } 14 | 15 | set -uex 16 | 17 | if [ "${INSTALL_LOCAL+x}" = x ] ; then 18 | if [ "$TRAVIS_OS_NAME" = osx ] ; then 19 | echo INSTALL_LOCAL not permitted for macOS targets 20 | exit 1 21 | fi 22 | 23 | if [ "${OPAM_SWITCH:=system}" != system ] ; then 24 | echo "INSTALL_LOCAL requires OPAM_SWITCH=system (or unset/null)" 25 | exit 1 26 | fi 27 | fi 28 | 29 | # the ocaml version to test 30 | OCAML_VERSION=${OCAML_VERSION:-4.06.1+multicore} 31 | OPAM_VERSION=${OPAM_VERSION:-1.2.2} 32 | OPAM_INIT=${OPAM_INIT:-true} 33 | 34 | # the base opam repository to use for bootstrapping and catch-all namespace 35 | BASE_REMOTE=${BASE_REMOTE:-git://github.com/ocaml/opam-repository} 36 | 37 | # whether we need a new gcc and binutils 38 | UPDATE_GCC_BINUTILS=${UPDATE_GCC_BINUTILS:-"0"} 39 | 40 | # Install Trusty remotes 41 | UBUNTU_TRUSTY=${UBUNTU_TRUSTY:-"0"} 42 | 43 | # Install XQuartz on OSX 44 | INSTALL_XQUARTZ=${INSTALL_XQUARTZ:-"true"} 45 | 46 | install_on_linux () { 47 | case "$OCAML_VERSION,$OPAM_VERSION" in 48 | 4.02.2+multicore,1.2.2) 49 | OCAML_VERSION=4.02; OCAML_FULL_VERSION=4.02.2+multicore 50 | ppa=avsm/ocaml42+opam12 ;; 51 | 4.04.2+multicore,1.2.2) 52 | OCAML_VERSION=4.02; OCAML_FULL_VERSION=4.04.2+multicore 53 | ppa=avsm/ocaml42+opam12 ;; 54 | 4.06.1+multicore,1.2.2) 55 | OCAML_VERSION=4.02; OCAML_FULL_VERSION=4.06.1+multicore 56 | ppa=avsm/ocaml42+opam12 ;; 57 | *) echo "Unknown OCAML_VERSION=$OCAML_VERSION OPAM_VERSION=$OPAM_VERSION" 58 | echo "(An unset OCAML_VERSION used to default to \"latest\", but you must now specify it." 59 | echo "Try something like \"OCAML_VERSION=3.12\", \"OCAML_VERSION=4.06\", or see README-travis.md at https://github.com/ocaml/ocaml-ci-scripts )" 60 | exit 1 ;; 61 | esac 62 | 63 | sudo add-apt-repository --yes ppa:${ppa} 64 | sudo apt-get update -qq 65 | if [ "${INSTALL_LOCAL:=0}" = 0 ] ; then 66 | sudo apt-get install -y \ 67 | "$(full_apt_version ocaml $OCAML_VERSION)" \ 68 | "$(full_apt_version ocaml-base $OCAML_VERSION)" \ 69 | "$(full_apt_version ocaml-native-compilers $OCAML_VERSION)" \ 70 | "$(full_apt_version ocaml-compiler-libs $OCAML_VERSION)" \ 71 | "$(full_apt_version ocaml-interp $OCAML_VERSION)" \ 72 | "$(full_apt_version ocaml-base-nox $OCAML_VERSION)" \ 73 | "$(full_apt_version ocaml-nox $OCAML_VERSION)" \ 74 | "$(full_apt_version camlp4 $OCAML_VERSION)" \ 75 | "$(full_apt_version camlp4-extra $OCAML_VERSION)" \ 76 | opam 77 | else 78 | sudo apt-get install -y opam 79 | fi 80 | 81 | TRUSTY="deb mirror://mirrors.ubuntu.com/mirrors.txt trusty main restricted universe" 82 | 83 | if [ "$UPDATE_GCC_BINUTILS" != "0" ] ; then 84 | echo "installing a recent gcc and binutils (mainly to get mirage-entropy-xen working!)" 85 | sudo add-apt-repository "${TRUSTY}" 86 | sudo add-apt-repository --yes ppa:ubuntu-toolchain-r/test 87 | sudo apt-get -qq update 88 | sudo apt-get install -y gcc-4.8 89 | sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 90 90 | sudo add-apt-repository -r "${TRUSTY}" 91 | fi 92 | 93 | if [ "$UBUNTU_TRUSTY" != "0" ] ; then 94 | echo "Adding Ubuntu Trusty mirrors" 95 | sudo add-apt-repository "${TRUSTY}" 96 | sudo apt-get -qq update 97 | fi 98 | 99 | if [ "$INSTALL_LOCAL" != 0 ] ; then 100 | echo -en "travis_fold:start:build.ocaml\r" 101 | echo "Building a local OCaml; this may take a few minutes..." 102 | wget "http://caml.inria.fr/pub/distrib/ocaml-${OCAML_FULL_VERSION%.*}/ocaml-$OCAML_FULL_VERSION.tar.gz" 103 | tar -xzf "ocaml-$OCAML_FULL_VERSION.tar.gz" 104 | cd "ocaml-$OCAML_FULL_VERSION" 105 | ./configure -prefix /usr/local ${OCAML_CONFIGURE_ARGS:=--with-debug-runtime} 106 | make world.opt 107 | sudo make install 108 | cd .. 109 | echo -en "travis_fold:end:build.ocaml\r" 110 | fi 111 | } 112 | 113 | install_on_osx () { 114 | case $INSTALL_XQUARTZ in 115 | true) 116 | curl -OL "http://xquartz.macosforge.org/downloads/SL/XQuartz-2.7.6.dmg" 117 | sudo hdiutil attach XQuartz-2.7.6.dmg 118 | sudo installer -verbose -pkg /Volumes/XQuartz-2.7.6/XQuartz.pkg -target / 119 | ;; 120 | esac 121 | brew update &> /dev/null 122 | case "$OCAML_VERSION,$OPAM_VERSION" in 123 | 4.02.2+multicore,1.2.2) OCAML_VERSION=4.02; OCAML_FULL_VERSION=4.02.2+multicore 124 | brew unlink python; brew install opam ;; 125 | 4.04.2+multicore,1.2.2) OCAML_VERSION=4.02; OCAML_FULL_VERSION=4.04.2+multicore 126 | brew unlink python; brew install opam ;; 127 | 4.06.1+multicore,1.2.2) OCAML_VERSION=4.02; OCAML_FULL_VERSION=4.06.1+multicore 128 | brew unlink python; brew install opam ;; 129 | *) echo "Unknown OCAML_VERSION=$OCAML_VERSION OPAM_VERSION=$OPAM_VERSION" 130 | exit 1 ;; 131 | esac 132 | } 133 | 134 | case $TRAVIS_OS_NAME in 135 | osx) install_on_osx ;; 136 | linux) install_on_linux ;; 137 | esac 138 | 139 | OPAM_SWITCH=${OPAM_SWITCH:-${OCAML_VERSION}.3} 140 | 141 | export OPAMYES=1 142 | 143 | case $OPAM_INIT in 144 | true) 145 | opam init -a "$BASE_REMOTE" --comp="$OPAM_SWITCH" 146 | opam remote add multicore https://github.com/ocamllabs/multicore-opam.git 147 | opam switch $OCAML_FULL_VERSION 148 | eval $(opam config env) 149 | ;; 150 | esac 151 | 152 | echo OCAML_VERSION=$OCAML_VERSION > .travis-ocaml.env 153 | echo OPAM_SWITCH=$OPAM_SWITCH >> .travis-ocaml.env 154 | 155 | ocaml -version 156 | opam --version 157 | opam --git-version 158 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Effects and handlers Rosetta stone 2 | 3 | A collection of examples demonstrating programming with effects and handlers in 4 | various programming languages. 5 | 6 | [![Build Status](https://travis-ci.org/effect-handlers/effects-rosetta-stone.svg?branch=master)](https://travis-ci.org/effect-handlers/effects-rosetta-stone) 7 | 8 | ## How to contribute 9 | 10 | ### General guidelines 11 | 12 | 1. Each example instance should be self-contained. 13 | 14 | 2. Each example should demonstrate *one* aspect of effectful programming. 15 | This does not mean it has to be simple. 16 | 17 | 3. In the naming of making comparisons possible, different implementations of a 18 | single example should do *the same thing* as far as that is possible. For 19 | instance, if there are *two* ways to implement an effect, e.g., state using 20 | the usual monad and state using parametrized effects, then there should be 21 | *two* examples which are identical, except for the implementation. If you 22 | feel that the implementation is unnatural, please comment on this, but still 23 | do it. 24 | 25 | 4. Do *not* print things out in examples that do not call for printing. Instead, 26 | just compute whatever needs to be computed and store the results in top-level 27 | values. Many languages will show the values anyhow, and that is as good as 28 | printing. 29 | 30 | 5. All folder names are in lower case, with words separated by dashes, e.g., 31 | `cooperative-threads`, `multicore-ocaml`, `algol-68`. 32 | 33 | ### Repository structure 34 | 35 | The repository contains examples in the `examples` folder, with a subfolder 36 | `examples/example-x` for each *"Example X"*, and further containing: 37 | 38 | * `README.md` contains a general description of the example 39 | * `language-y` contains the example implemented in *"Language Y"*, which further 40 | contains: 41 | 42 | * an optional `README.md` contains further comments on the language 43 | implementation of the example, 44 | * a `Makefile` whose target `all` runs all the source code 45 | * self-contained source code, following the established standards for the 46 | programming language 47 | 48 | ### How to contribute a new programming language 49 | 50 | To add a new programming language *My Language*, follow these steps: 51 | 52 | 1. Pick a lower-case name for the folders which contain examples written in My 53 | Language, e.g., `my-language`. 54 | 2. Describe the language in the section *Languages* below. Use `my-language` as 55 | the section name. Please provide links to the language home page and 56 | installation instructions. 57 | 3. Implement at least one [example 58 | instance](#how-to-contribute-an-example-instance) in your language. 59 | 60 | ### How to contribute a new example 61 | 62 | To add a completely new example, follow these steps: 63 | 64 | 1. Pick a lower case folder name for the example, e.g., `example-x`. 65 | 2. Create a lower-case folder `examples/example-x` that will contain the example 66 | and put in it `README.md` with a general description of the example. Please 67 | follow the format of existing example descriptions. 68 | 3. Implement at least one [example 69 | instance](#how-to-contribute-an-example-instance) so that it is clear what 70 | needs to be implemented. 71 | 72 | ### How to contribute an example instance 73 | 74 | To implement an example instance of `example-x` in `language-y`, create 75 | `examples/example-x/language-y` and populate it, following the [repository 76 | structure](#repository-structure) guidelines. 77 | 78 | You may show several variants of the example. We recommend that you put them in 79 | separate files, comment on them on the `README.md` file, and clearly mark one of 80 | the examples as the *main* one. The main example is the one that people should 81 | look at first. 82 | 83 | ### How to contribute an example non-instance 84 | 85 | If something cannot be naturally implemented in your language, we want to know 86 | about it! Create the folder `examples/example-x/language-y` and put in it 87 | `README.md` which explains what the problem is. 88 | 89 | You may add a *partial*, *simplified*, or *modified* implementation, in which 90 | case you should explicitly describe the changes you made in `README.md`. 91 | 92 | ### May I improve other people's code? 93 | 94 | Yes, we strongly encourage sharing and cooperation. 95 | 96 | ## Languages 97 | 98 | An alphabetically ordered list of the languages used in the examples. The 99 | section name must match the folder name used for that language. 100 | 101 | ### `eff` 102 | 103 | The [Eff](http://www.eff-lang.org/) programming language can be installed 104 | [through OPAM](https://github.com/matijapretnar/eff/#installing-with-opam), 105 | compiled from [source code](https://github.com/matijapretnar/eff/), or [run in a 106 | browser](http://www.eff-lang.org/try/). 107 | 108 | To run Eff code contained in a file `example.eff`, run from the command line: 109 | 110 | ```bash 111 | eff example.eff 112 | ``` 113 | 114 | You can also run `make` in the example directory and it will run all examples for you. 115 | 116 | ### `frank` 117 | 118 | Install Frank from the [Frank GitHub repository](https://github.com/frank-lang/frank). To run 119 | a Frank program contained in a file `example.fk`, run from the command line: 120 | 121 | ```bash 122 | frank example.fk 123 | ``` 124 | 125 | 126 | ### `koka` 127 | 128 | 129 | [Koka](https://github.com/koka-lang/koka) (meaning "effective" in Japanese) can be installed from the [Koka Github repository](https://github.com/koka-lang/koka). It is best to clone the repository next to the 130 | rosetta repository as the make files assume the Koka binary is located there, e.g. 131 | from the same directory do: 132 | ``` 133 | > git clone https://github.com/effect-handlers/effects-rosetta-stone.git 134 | > git clone https://github.com/koka-lang/koka.git 135 | ``` 136 | 137 | ### `multicore-ocaml` 138 | 139 | [Multicore OCaml](https://github.com/ocamllabs/ocaml-multicore) can be installed 140 | through [OPAM](https://opam.ocaml.org): 141 | 142 | ```bash 143 | $ opam remote add multicore https://github.com/ocamllabs/multicore-opam.git 144 | $ opam switch 4.06.1+multicore 145 | ``` 146 | 147 | ## About the repository 148 | 149 | The participants of Dagstuhl seminar [Algebraic effect handlers go 150 | mainstream](https://www.dagstuhl.de/en/program/calendar/semhp/?semnr=18172) have 151 | initiated this repository of examples that show how to program with algebraic 152 | effects and handlers in various languages. 153 | --------------------------------------------------------------------------------