├── .gitignore ├── .gitmodules ├── 02-exercises ├── 01-introduction │ ├── dune │ ├── dune-project │ └── problem.ml ├── 02-basic_types │ ├── dune │ ├── dune-project │ ├── problem.ml │ └── problem.mli ├── 03-define_functions │ ├── dune │ ├── dune-project │ ├── problem.ml │ └── problem.mli ├── 04-call_functions │ ├── dune │ ├── dune-project │ ├── problem.ml │ └── problem.mli ├── 05-twice │ ├── dune │ ├── dune-project │ ├── problem.ml │ └── problem.mli ├── 06-pattern-matching │ ├── dune │ ├── dune-project │ ├── problem.ml │ └── problem.mli ├── 07-simple_recursion │ ├── dune │ ├── dune-project │ ├── problem.ml │ └── problem.mli ├── 08-list_intro │ ├── dune │ ├── dune-project │ ├── problem.ml │ └── problem.mli ├── 09-list_range │ ├── dune │ ├── dune-project │ ├── problem.ml │ └── problem.mli ├── 10-list_product │ ├── dune │ ├── dune-project │ ├── problem.ml │ └── problem.mli ├── 11-sum_product │ ├── dune │ ├── dune-project │ ├── problem.ml │ └── problem.mli ├── 12-list_functions │ ├── dune │ ├── dune-project │ ├── problem.ml │ └── problem.mli ├── 13-labelled_arguments │ ├── dune │ ├── dune-project │ ├── problem.ml │ └── problem.mli ├── 14-variants │ ├── dune │ ├── dune-project │ ├── problem.ml │ └── problem.mli ├── 15-options │ ├── dune │ ├── dune-project │ ├── problem.ml │ └── problem.mli ├── 16-tuples │ ├── dune │ ├── dune-project │ ├── problem.ml │ └── problem.mli ├── 17-records │ ├── dune │ ├── dune-project │ ├── problem.ml │ └── problem.mli ├── 18-mutable_records │ ├── dune │ ├── dune-project │ ├── problem.ml │ └── problem.mli ├── 19-refs │ ├── dune │ ├── dune-project │ ├── problem.ml │ └── problem.mli ├── 20-anonymous_functions │ ├── dune │ ├── dune-project │ ├── problem.ml │ └── problem.mli ├── 21-reading_sigs │ ├── dune │ ├── dune-project │ ├── problem.ml │ └── problem.mli ├── dune └── dune-project ├── 03-github ├── README.org ├── commands.ml ├── dune └── hub.ml ├── 04-frogger ├── Dockerfile ├── Makefile ├── README.org ├── assets │ ├── background.png │ ├── buggy-left.png │ ├── buggy-right.png │ ├── camel-down.png │ ├── camel-left.png │ ├── camel-right.png │ ├── camel-up.png │ ├── carpet_blue.png │ ├── carpet_green.png │ ├── carpet_red.png │ ├── confetti.png │ ├── police-car-left.png │ ├── police-car-right.png │ ├── red-pickup-left.png │ ├── red-pickup-right.png │ ├── skull.png │ ├── truck-left.png │ └── truck-right.png ├── config.ml ├── draw.ml ├── dune ├── frogger.ml ├── frogger.mli ├── import.ml ├── index.html ├── main.ml ├── scaffold.ml ├── scaffold.mli ├── suggested_frogger.mli └── test-js-of-ocaml-install │ ├── Makefile │ ├── dune │ ├── index.html │ └── main.ml ├── LICENSE.txt ├── Makefile ├── README.org ├── dune-project ├── make_learn_ocaml_directory.sh └── solutions ├── frogger └── frogger.ml └── github └── commands.ml /.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | .merlin 3 | .DS_Store 4 | node_modules 5 | .#* 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "01-install-ocaml"] 2 | path = 01-install-ocaml 3 | url = https://github.com/ocamllabs/install-ocaml 4 | -------------------------------------------------------------------------------- /02-exercises/01-introduction/dune: -------------------------------------------------------------------------------- 1 | ;; -*- Scheme -*- 2 | 3 | (library 4 | (name problem_1) 5 | (libraries base stdio) 6 | (inline_tests) 7 | (preprocess (pps ppx_jane))) 8 | 9 | (env 10 | (dev 11 | (flags (:standard 12 | -w -20 13 | -w -27 14 | -w -32 15 | -w -34 16 | -w -37 17 | -w -39))) 18 | (release 19 | (flags (:standard)))) 20 | -------------------------------------------------------------------------------- /02-exercises/01-introduction/dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 1.2) 2 | -------------------------------------------------------------------------------- /02-exercises/01-introduction/problem.ml: -------------------------------------------------------------------------------- 1 | (* Welcome to OCaml challenges! 2 | 3 | This exercise is just meant to familiarize you with the system. 4 | 5 | Write OCaml code using your favorite text editor; if you aren't already 6 | committed to one, we recommend Visual Studio Code. 7 | 8 | To compile your code and run inline tests, run 9 | 10 | [dune runtest] 11 | 12 | in a terminal session in this problem's directory. 13 | 14 | Try building this code. 15 | 16 | You should see a compilation error because it's missing the end quote. Add 17 | the end quote and re-run. You should see that the code compiled and ran! 18 | 19 | You can also execute code in utop directly. Try pasting this line of code 20 | into utop and running it there. *) 21 | let () = Stdio.printf "Hello, World! 22 | -------------------------------------------------------------------------------- /02-exercises/02-basic_types/dune: -------------------------------------------------------------------------------- 1 | ;; -*- scheme -*- 2 | 3 | (library 4 | (name problem_2) 5 | (libraries base stdio) 6 | (inline_tests) 7 | (preprocess (pps ppx_jane))) 8 | 9 | (env 10 | (dev 11 | (flags (:standard 12 | -w -20 13 | -w -27 14 | -w -32 15 | -w -34 16 | -w -37 17 | -w -39))) 18 | (release 19 | (flags (:standard)))) 20 | -------------------------------------------------------------------------------- /02-exercises/02-basic_types/dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 1.2) 2 | -------------------------------------------------------------------------------- /02-exercises/02-basic_types/problem.ml: -------------------------------------------------------------------------------- 1 | open! Base 2 | 3 | (* In OCaml there are 6 basic types: int, float, char, string, bool, and unit. 4 | 5 | The following exercises and examples will give you a brief introduction to 6 | these types. Feel free to play around with them in utop. 7 | 8 | Note the keyword [let], which is how variable assignment is done in OCaml. 9 | 10 | In OCaml floats are distinguished from ints by their decimal points. 0 is an 11 | int, 0. is a float. 12 | 13 | In addition the basic math operations are also distinguished by a decimal 14 | point. For example, + allows you to add two ints and +. allows you to add two 15 | floats. *) 16 | 17 | (* Signatures 18 | ========== 19 | 20 | four is a value with the type int. We write the signature like this: 21 | 22 | val four : int 23 | 24 | Read it like this: "[four] is a value of type int". 25 | 26 | Signatures are similar to type declarations in other languages. They tell the 27 | compiler (and human readers of your code!) the types of variables and 28 | functions in your program. For example, in C, C++, and Java, the signature 29 | above would be written like so: 30 | 31 | int four; 32 | *) 33 | let four = 4 34 | 35 | (* float_four is a value with the type float. We write the signature like this: 36 | 37 | val float_four : float 38 | 39 | You may have noticed that the two signatures we showed you were in comments. 40 | Signatures are not always required! In many situations, you may omit them, 41 | and the compiler will infer the type of values. 42 | 43 | However, if you do write a signature for a value, the compiler will make sure 44 | to check that it's consistent with how that value is used. 45 | 46 | Try inserting an incorrect signature for [float_four] to see what error the 47 | compiler gives you. *) 48 | let float_four = 4. 49 | 50 | (* Function signatures 51 | =================== 52 | 53 | In OCaml, functions are also values! 54 | 55 | In a signature, the arrow [->] denotes an argument. The last type in a 56 | function signature is the result. 57 | 58 | So the signature for a function that takes two integers and returns an 59 | integer is: 60 | 61 | val int_average : int -> int -> int 62 | 63 | In Ocaml there's no explicit return statement: functions just return the 64 | value of the last statement in that function. *) 65 | let int_average x y = failwith "For you to implement" 66 | 67 | (* val float_average : float -> float -> float *) 68 | let float_average x y = failwith "For you to implement" 69 | 70 | (* There will be more about functions later, but note that in OCaml, there are 71 | no parentheses when applying a function! So the following expression computes 72 | the average of 10 and 20: 73 | 74 | int_average 10 20 75 | *) 76 | 77 | (* As in many languages strings are denoted with "" and chars are denoted with ''. 78 | 79 | String concatenation is done with the ^ operator. 80 | *) 81 | 82 | (* val first_name : string *) 83 | let first_name = "Fred" 84 | 85 | (* You can also write type annotations in definitions *) 86 | let last_name : string = "Flintstone" 87 | 88 | let full_name = first_name ^ " " ^ last_name 89 | 90 | let a_boolean_false : bool = false 91 | 92 | (* You can use 93 | && for logical and 94 | || for logical or 95 | *) 96 | let () = assert (true || a_boolean_false) 97 | 98 | (* The [unit] type 99 | =============== 100 | 101 | unit is a special type in OCaml that has only one possible value written (). 102 | It is generally used for mutation and io-operations such as printing. 103 | 104 | (I/O stands for input/output. Examples: printing to screen, reading a file, 105 | sending and receiving network requests.) 106 | 107 | To combine several unit operations together the ; operator is used. 108 | 109 | When evaluating a unit operation on the toplevel, you should wrap it in a let 110 | binding, as in 111 | 112 | let () = ... 113 | 114 | This isn't actually necessary in all cases, but it will save you from some 115 | frustrating debugging of compiler issues if you just always include it. 116 | 117 | For this reason, idiomatic OCaml code has [let () = ...] as its entrypoint, 118 | similar to the [main] function in languages like C, C++ and Java (however, 119 | in those languages it is required). 120 | *) 121 | let () = 122 | Stdio.print_endline "Hi, My name is "; 123 | Stdio.print_endline full_name; 124 | Stdio.print_endline " and I am 5 years old" 125 | 126 | (* Like many other programming languages, you can use format strings too *) 127 | let () = 128 | Stdio.printf "Hi, My name is %s and I am %d years old\n" full_name 5 129 | 130 | (* The lines that follow are inline tests. Each evaluates a boolean expression. 131 | They are run during the build, and failures -- evaluating to false -- are 132 | treated like compile errors by the build tool and editors. 133 | 134 | We will see other kinds of inline tests later, and some interesting patterns 135 | for using them. *) 136 | 137 | (* While OCaml supports polymorphic comparison, it is good practice to use 138 | equality and comparison functions specific to each type. 139 | 140 | So, [Int.equal] is the [equal] function defined in the [Int] module. Its 141 | signature is 142 | 143 | val equal : int -> int -> bool 144 | 145 | In words: [equal] takes two [int]s and returns a [bool]. The following line 146 | is applying that function to two inputs, [5] and [int_average 5 5]. *) 147 | 148 | let%test "Testing int_average..." = 149 | Int.equal (int_average 5 5) 5 150 | 151 | (* Modules can also contain operators. By convention, the equality operator is 152 | defined and equivalent to the [equal] function. To reference an operator in a 153 | module, we need to surround it with parentheses. Try removing the parentheses 154 | to see what error you get. 155 | 156 | Int.(=) is the same as Int.equal 157 | *) 158 | 159 | let%test "Testing int_average..." = 160 | Int.(=) (int_average 50 100) 75 161 | 162 | let%test "Testing float_average..." = 163 | Float.(=) (float_average 5. 5.) 5. 164 | 165 | let%test "Testing float_average..." = 166 | Float.equal (float_average 5. 10.) 7.5 167 | 168 | (* .mli files 169 | ========== 170 | 171 | Check out the [problem.mli] file in this directory! It declares the types for 172 | the two functions you had to implement. If the types in the [.mli] don't 173 | match the types of the values in the [.ml], the compiler will flag that as an 174 | error. 175 | *) 176 | -------------------------------------------------------------------------------- /02-exercises/02-basic_types/problem.mli: -------------------------------------------------------------------------------- 1 | open! Base 2 | 3 | (* This is an mli, a file that declares the interface that the corresponding 4 | implementation file (problem.ml) exposes to other code. 5 | 6 | The compiler will enforce that the implementations you write for 7 | [int_average] and [float_average] in problem.ml have the type signatures 8 | written below. 9 | *) 10 | 11 | val int_average : int -> int -> int 12 | val float_average : float -> float -> float 13 | -------------------------------------------------------------------------------- /02-exercises/03-define_functions/dune: -------------------------------------------------------------------------------- 1 | ;; -*- scheme -*- 2 | 3 | (library 4 | (name problem_3) 5 | (libraries base stdio) 6 | (inline_tests) 7 | (preprocess (pps ppx_jane))) 8 | 9 | (env 10 | (dev 11 | (flags (:standard 12 | -w -20 13 | -w -27 14 | -w -32 15 | -w -34 16 | -w -37 17 | -w -39))) 18 | (release 19 | (flags (:standard)))) 20 | -------------------------------------------------------------------------------- /02-exercises/03-define_functions/dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 1.2) 2 | -------------------------------------------------------------------------------- /02-exercises/03-define_functions/problem.ml: -------------------------------------------------------------------------------- 1 | open! Base 2 | 3 | (* We use let to define functions. 4 | 5 | Definitions take on the form: 6 | let FUNCTION_NAME ARG1 ARG2 ... = BODY 7 | 8 | In OCaml, outside of strings, whitespace and newlines are the same. 9 | 10 | So, you could also write 11 | let FUNCTION_NAME 12 | ARG1 13 | ARG2 14 | = 15 | BODY 16 | 17 | and it's the same to the compiler. 18 | 19 | For example, here we define a function add1 that takes a single int 20 | argument and returns that argument plus 1. 21 | *) 22 | let add1 arg = arg + 1 23 | 24 | (* This function uses the built-in ^ operator to append strings. *) 25 | let string_append x y = x ^ y 26 | 27 | (* Let's define our own functions using +, -, *, and / below. *) 28 | 29 | let plus x y = failwith "For you to implement" 30 | let times x y = failwith "For you to implement" 31 | let minus x y = failwith "For you to implement" 32 | let divide x y = failwith "For you to implement" 33 | 34 | let%test "Testing plus..." = Int.( = ) 2 (plus 1 1) 35 | let%test "Testing plus..." = Int.( = ) 49 (plus (-1) 50) 36 | let%test "Testing times..." = Int.( = ) 64 (times 8 8) 37 | let%test "Testing times..." = Int.( = ) (-2048) (times (-2) 1024) 38 | let%test "Testing minus..." = Int.( = ) (-4) (minus (-2) 2) 39 | let%test "Testing minus..." = Int.( = ) 1000 (minus 1337 337) 40 | let%test "Testing divide..." = Int.( = ) 512 (divide 1024 2) 41 | let%test "Testing divide..." = Int.( = ) 1010 (divide 31337 31) 42 | -------------------------------------------------------------------------------- /02-exercises/03-define_functions/problem.mli: -------------------------------------------------------------------------------- 1 | open! Base 2 | 3 | val plus : int -> int -> int 4 | val times : int -> int -> int 5 | val minus : int -> int -> int 6 | val divide : int -> int -> int 7 | -------------------------------------------------------------------------------- /02-exercises/04-call_functions/dune: -------------------------------------------------------------------------------- 1 | ;; -*- scheme -*- 2 | 3 | (library 4 | (name problem_4) 5 | (libraries base stdio) 6 | (inline_tests) 7 | (preprocess (pps ppx_jane))) 8 | 9 | (env 10 | (dev 11 | (flags (:standard 12 | -w -20 13 | -w -27 14 | -w -32 15 | -w -34 16 | -w -37 17 | -w -39))) 18 | (release 19 | (flags (:standard)))) 20 | -------------------------------------------------------------------------------- /02-exercises/04-call_functions/dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 1.2) 2 | -------------------------------------------------------------------------------- /02-exercises/04-call_functions/problem.ml: -------------------------------------------------------------------------------- 1 | open! Base 2 | 3 | (* Here are some example functions: *) 4 | let square x = x * x 5 | let half x = x / 2 6 | let add x y = x + y 7 | 8 | (* You can order function invocations with parentheses or let bindings *) 9 | (* Parens *) 10 | let () = 11 | Stdio.printf "(5^2)/2 = %i" (half (square 5)) 12 | 13 | (* Let bindings *) 14 | let () = 15 | let squared = square 5 in 16 | let halved = half squared in 17 | Stdio.printf "(5^2)/2 = %i" halved 18 | 19 | (* Try to write [average] by reusing [add] and [half] *) 20 | let average x y = failwith "For you to implement" 21 | 22 | let%test "Testing average..." = 23 | Int.(=) 5 (average 5 5) 24 | 25 | let%test "Testing average..." = 26 | Int.(=) 75 (average 50 100) 27 | -------------------------------------------------------------------------------- /02-exercises/04-call_functions/problem.mli: -------------------------------------------------------------------------------- 1 | open! Base 2 | 3 | val average : int -> int -> int 4 | -------------------------------------------------------------------------------- /02-exercises/05-twice/dune: -------------------------------------------------------------------------------- 1 | ;; -*- scheme -*- 2 | 3 | (library 4 | (name problem_5) 5 | (libraries base stdio) 6 | (inline_tests) 7 | (preprocess (pps ppx_jane))) 8 | 9 | (env 10 | (dev 11 | (flags (:standard 12 | -w -20 13 | -w -27 14 | -w -32 15 | -w -34 16 | -w -37 17 | -w -39))) 18 | (release 19 | (flags (:standard)))) 20 | -------------------------------------------------------------------------------- /02-exercises/05-twice/dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 1.2) 2 | -------------------------------------------------------------------------------- /02-exercises/05-twice/problem.ml: -------------------------------------------------------------------------------- 1 | open! Base 2 | 3 | (* We can easily write a function that adds 1 to any number. 4 | Recall that the infix operator (+) will add two integers. 5 | *) 6 | 7 | let add1 x = failwith "For you to implement" 8 | 9 | (* Let's write a function that squares its argument (multiplies it by itself) *) 10 | let square x = failwith "For you to implement" 11 | 12 | (* Functions are first class in OCaml. This means that you can take 13 | a function and pass it around as an argument to other functions. 14 | 15 | Let's write a function named twice: it will take a function and apply 16 | that function to itself two times. 17 | 18 | For example, if we wanted to make an "add2" function, we could do it 19 | by writing: 20 | let add2 = twice add1 21 | 22 | It may be necessary to use parenthesis to specify which function is 23 | applied to which value. E.g. 24 | let add2 = add1 add1 x 25 | will not compile, however 26 | let add2 = add1 (add1 x) 27 | will compile. 28 | *) 29 | 30 | let twice f x = failwith "For you to implement" 31 | 32 | (* Now that we have twice, write add2 and raise_to_the_fourth *) 33 | 34 | let add2 = failwith "For you to implement" (* Hint: use add1 *) 35 | let raise_to_the_fourth = failwith "For you to implement" (* Hint: use square *) 36 | 37 | let%test "Testing add1..." = 38 | Int.(=) 5 (add1 4) 39 | 40 | let%test "Testing square..." = 41 | Int.(=) 16 (square 4) 42 | 43 | let%test "Testing square..." = 44 | Int.(=) 16 (square (-4)) 45 | 46 | let%test "Testing add1..." = 47 | Int.(=) 5 (twice add1 3) 48 | 49 | let%test "Testing add2..." = 50 | Int.(=) 1337 (add2 1335) 51 | 52 | let%test "Testing raise_to_the_fourth..." = 53 | Int.(=) 1 (raise_to_the_fourth 1) 54 | 55 | let%test "Testing raise_to_the_fourth..." = 56 | Int.(=) 10000 (raise_to_the_fourth 10) 57 | -------------------------------------------------------------------------------- /02-exercises/05-twice/problem.mli: -------------------------------------------------------------------------------- 1 | (* This file deliberately left empty. *) 2 | -------------------------------------------------------------------------------- /02-exercises/06-pattern-matching/dune: -------------------------------------------------------------------------------- 1 | ;; -*- scheme -*- 2 | 3 | (library 4 | (name problem_6) 5 | (libraries base stdio) 6 | (inline_tests) 7 | (preprocess (pps ppx_jane))) 8 | 9 | (env 10 | (dev 11 | (flags (:standard 12 | -w -20 13 | -w -27 14 | -w -32 15 | -w -34 16 | -w -37 17 | -w -39))) 18 | (release 19 | (flags (:standard)))) 20 | -------------------------------------------------------------------------------- /02-exercises/06-pattern-matching/dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 1.2) 2 | -------------------------------------------------------------------------------- /02-exercises/06-pattern-matching/problem.ml: -------------------------------------------------------------------------------- 1 | open! Base 2 | 3 | (* Pattern matching lets us compare inputs to known values. 4 | Patterns following "|" are tested in order. 5 | On the first match, we use the result following "->". 6 | The "_" pattern means "could be anything". 7 | *) 8 | let is_superman x = 9 | match x with 10 | | "Clark Kent" -> true 11 | | _ -> false 12 | ;; 13 | 14 | (* We can also pattern match on multiple values at the same time. *) 15 | let is_same_person x y = 16 | match x, y with 17 | | "Clark Kent", "Superman" 18 | | "Peter Parker", "Spiderman" -> true 19 | | _ -> false 20 | ;; 21 | 22 | (* Let's use our own pattern matching. Write a function that returns 23 | whether x is non zero by matching on x *) 24 | let non_zero x = failwith "For you to implement" 25 | 26 | let%test "Testing non_zero..." = Bool.( = ) false (non_zero 0) 27 | let%test "Testing non_zero..." = Bool.( = ) true (non_zero 500) 28 | let%test "Testing non_zero..." = Bool.( = ) true (non_zero (-400)) 29 | 30 | (* Now, write a function that returns true if x and y are both 31 | non-zero by matching on both of them. *) 32 | let both_non_zero x y = failwith "For you to implement" 33 | 34 | let%test "Testing both_non_zero..." = Bool.( = ) false (both_non_zero 0 0) 35 | let%test "Testing both_non_zero..." = Bool.( = ) false (both_non_zero 0 1) 36 | let%test "Testing both_non_zero..." = Bool.( = ) false (both_non_zero (-20) 0) 37 | let%test "Testing both_non_zero..." = Bool.( = ) true (both_non_zero 400 (-5)) 38 | -------------------------------------------------------------------------------- /02-exercises/06-pattern-matching/problem.mli: -------------------------------------------------------------------------------- 1 | open! Base 2 | 3 | val non_zero : int -> bool 4 | -------------------------------------------------------------------------------- /02-exercises/07-simple_recursion/dune: -------------------------------------------------------------------------------- 1 | ;; -*- scheme -*- 2 | 3 | (library 4 | (name problem_7) 5 | (libraries base stdio) 6 | (inline_tests) 7 | (preprocess (pps ppx_jane))) 8 | 9 | (env 10 | (dev 11 | (flags (:standard 12 | -w -20 13 | -w -27 14 | -w -32 15 | -w -34 16 | -w -37 17 | -w -39))) 18 | (release 19 | (flags (:standard)))) 20 | -------------------------------------------------------------------------------- /02-exercises/07-simple_recursion/dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 1.2) 2 | -------------------------------------------------------------------------------- /02-exercises/07-simple_recursion/problem.ml: -------------------------------------------------------------------------------- 1 | open! Base 2 | 3 | (* Remember that functions can call functions? 4 | They can call themselves too. But only with a special keyword. 5 | 6 | First try to compile this. We see "Unbound value add_every_number_up_to". 7 | 8 | Now change "let" to "let rec" and recompile. 9 | *) 10 | 11 | let add_every_number_up_to x = 12 | (* make sure we don't call this on negative numbers! *) 13 | assert (x >= 0); 14 | match x with 15 | | 0 -> 0 16 | | _ -> x + add_every_number_up_to (x - 1) 17 | ;; 18 | 19 | (* Let's write a function to multiply every number up to x. Remember: [factorial 0] is 1 *) 20 | let rec factorial x = 21 | assert (x >= 0); 22 | failwith "For you to implement" 23 | ;; 24 | 25 | let%test "Testing factorial..." = Int.( = ) 1 (factorial 0) 26 | let%test "Testing factorial..." = Int.( = ) 120 (factorial 5) 27 | let%test "Testing factorial..." = Int.( = ) 479001600 (factorial 12) 28 | -------------------------------------------------------------------------------- /02-exercises/07-simple_recursion/problem.mli: -------------------------------------------------------------------------------- 1 | open! Base 2 | 3 | val factorial : int -> int 4 | -------------------------------------------------------------------------------- /02-exercises/08-list_intro/dune: -------------------------------------------------------------------------------- 1 | ;; -*- scheme -*- 2 | 3 | (library 4 | (name problem_8) 5 | (libraries base stdio) 6 | (inline_tests) 7 | (preprocess (pps ppx_jane))) 8 | 9 | (env 10 | (dev 11 | (flags (:standard 12 | -w -20 13 | -w -27 14 | -w -32 15 | -w -34 16 | -w -37 17 | -w -39))) 18 | (release 19 | (flags (:standard)))) 20 | -------------------------------------------------------------------------------- /02-exercises/08-list_intro/dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 1.2) 2 | -------------------------------------------------------------------------------- /02-exercises/08-list_intro/problem.ml: -------------------------------------------------------------------------------- 1 | open! Base 2 | 3 | (* OCaml natively supports linked lists as part of the language. 4 | Lists are commonly referred to as having heads and tails. 5 | The head is the first element of the linked list 6 | The tail is everything else. 7 | 8 | To construct a list we use the cons infix operator :: to prepend elements to 9 | the front of a list 10 | 11 | val (::) : 'a -> 'a list -> 'a list 12 | 13 | [] means "the empty list". 14 | hd :: tl means "the element hd added to the front of the list tl". 15 | 16 | When matching on a list, it's either empty or non-empty. To say it another way, it's 17 | either equal to [] or equal to (hd :: tl) where hd is the first element of the list 18 | and tl is all the rest of the elements of the list (which may itself be empty). 19 | *) 20 | 21 | let () = assert ([%compare.equal: int list] [ 5; 1; 8; 4 ] (5 :: 1 :: 8 :: 4 :: [])) 22 | 23 | (* This function computes the length of a list. *) 24 | let rec length lst = 25 | match lst with 26 | | [] -> 0 27 | | _ :: tl -> 1 + length tl 28 | ;; 29 | 30 | (* Write a function to add up the elements of a list by matching on it. *) 31 | let rec sum lst = failwith "For you to implement" 32 | 33 | (* The signature for the append operator is 34 | val (@) : 'a list -> 'a list -> 'a list 35 | 36 | It's an infix operator. 37 | *) 38 | let list_append first second = first @ second 39 | 40 | (* The way you put something on the head to the list 41 | uses the same kind of syntax for matching on lists. 42 | val (::) : 'a -> 'a list -> 'a list 43 | *) 44 | let new_head hd rest = hd :: rest 45 | 46 | let%test "Testing sum..." = Int.( = ) 0 (sum []) 47 | let%test "Testing sum..." = Int.( = ) 55 (sum [ 55 ]) 48 | let%test "Testing sum..." = Int.( = ) 0 (sum [ 5; -5; 1; -1 ]) 49 | let%test "Testing sum..." = Int.( = ) 12 (sum [ 5; 5; 1; 1 ]) 50 | -------------------------------------------------------------------------------- /02-exercises/08-list_intro/problem.mli: -------------------------------------------------------------------------------- 1 | open! Base 2 | 3 | val sum : int list -> int 4 | -------------------------------------------------------------------------------- /02-exercises/09-list_range/dune: -------------------------------------------------------------------------------- 1 | ;; -*- scheme -*- 2 | 3 | (library 4 | (name problem_9) 5 | (libraries base stdio) 6 | (inline_tests) 7 | (preprocess (pps ppx_jane))) 8 | 9 | (env 10 | (dev 11 | (flags (:standard 12 | -w -20 13 | -w -27 14 | -w -32 15 | -w -34 16 | -w -37 17 | -w -39))) 18 | (release 19 | (flags (:standard)))) 20 | -------------------------------------------------------------------------------- /02-exercises/09-list_range/dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 1.2) 2 | -------------------------------------------------------------------------------- /02-exercises/09-list_range/problem.ml: -------------------------------------------------------------------------------- 1 | open! Base 2 | 3 | (* When working with two lists it's conveninet to have a way to concatenate them together. 4 | 5 | The append infix operator @ concatenates two lists: 6 | 7 | val (@) : 'a list -> 'a list -> 'a list 8 | 9 | This function is the same as the List.append function. 10 | *) 11 | let () = 12 | assert ([%compare.equal: int list] ([ 5; 1 ] @ [ 8; 4 ]) [ 5; 1; 8; 4 ]); 13 | assert ([%compare.equal: int list] (List.append [ 5; 1 ] [ 8; 4 ]) [ 5; 1; 8; 4 ]) 14 | ;; 15 | 16 | (* TODO: Write a function to construct a list of all integers in the range from [from] to [to_] 17 | 18 | including [from] but excluding [to_] in increasing order. 19 | 20 | val range : int -> int -> int list 21 | *) 22 | let range from to_ = failwith "For you to implement" 23 | 24 | (* Here's a different way of getting the [equal] function for a type [t]: 25 | 26 | [%compare.equal: t] 27 | 28 | For example, [%compare.equal: float] is replaced at compile-time with the 29 | equality function for floats. 30 | 31 | And [%compare.equal: int list] is the equality function for lists of 32 | integers. 33 | 34 | One situation where this is really useful is instantiations of containers 35 | (like the [int list] example above, which is used below in tests). Instead of 36 | writing an equality function by hand, or defining a module specialized to 37 | that type just to use its equality operator, you can ask the [ppx_compare] 38 | syntax extension to create it for you on the fly. 39 | *) 40 | let%test "Testing range..." = [%compare.equal: int list] (range 1 4) [ 1; 2; 3 ] 41 | 42 | let%test "Testing range..." = 43 | [%compare.equal: int list] (range (-5) 3) [ -5; -4; -3; -2; -1; 0; 1; 2 ] 44 | ;; 45 | -------------------------------------------------------------------------------- /02-exercises/09-list_range/problem.mli: -------------------------------------------------------------------------------- 1 | open! Base 2 | 3 | val range : int -> int -> int list 4 | -------------------------------------------------------------------------------- /02-exercises/10-list_product/dune: -------------------------------------------------------------------------------- 1 | ;; -*- scheme -*- 2 | 3 | (library 4 | (name problem_10) 5 | (libraries base stdio) 6 | (inline_tests) 7 | (preprocess (pps ppx_jane))) 8 | 9 | (env 10 | (dev 11 | (flags (:standard 12 | -w -20 13 | -w -27 14 | -w -32 15 | -w -34 16 | -w -37 17 | -w -39))) 18 | (release 19 | (flags (:standard)))) 20 | -------------------------------------------------------------------------------- /02-exercises/10-list_product/dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 1.2) 2 | -------------------------------------------------------------------------------- /02-exercises/10-list_product/problem.ml: -------------------------------------------------------------------------------- 1 | open! Base 2 | 3 | (* Now let's write a function to multiply together the elements of a list. *) 4 | let rec product xs = 5 | match xs with 6 | | [] -> failwith "For you to implement" 7 | | _for_you_to_implement -> failwith "For you to implement" 8 | ;; 9 | 10 | let%test "Testing product..." = Int.equal 1 (product []) 11 | let%test "Testing product..." = Int.equal 55 (product [ 55 ]) 12 | let%test "Testing product..." = Int.equal 25 (product [ 5; -5; 1; -1 ]) 13 | let%test "Testing product..." = Int.equal 25 (product [ 5; 5; 1; 1 ]) 14 | -------------------------------------------------------------------------------- /02-exercises/10-list_product/problem.mli: -------------------------------------------------------------------------------- 1 | open! Base 2 | 3 | val product : int list -> int 4 | -------------------------------------------------------------------------------- /02-exercises/11-sum_product/dune: -------------------------------------------------------------------------------- 1 | ;; -*- scheme -*- 2 | 3 | (library 4 | (name problem_11) 5 | (libraries base stdio) 6 | (inline_tests) 7 | (preprocess (pps ppx_jane))) 8 | 9 | (env 10 | (dev 11 | (flags (:standard 12 | -w -20 13 | -w -27 14 | -w -32 15 | -w -34 16 | -w -37 17 | -w -39))) 18 | (release 19 | (flags (:standard)))) 20 | -------------------------------------------------------------------------------- /02-exercises/11-sum_product/dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 1.2) 2 | -------------------------------------------------------------------------------- /02-exercises/11-sum_product/problem.ml: -------------------------------------------------------------------------------- 1 | open! Base 2 | 3 | let plus x y = x + y 4 | let times x y = x * y 5 | 6 | (* Sometimes, multiple functions look similar: *) 7 | let rec add_every_number_up_to x = 8 | match x with 9 | | 0 -> 0 10 | | _ -> plus x (add_every_number_up_to (x - 1)) 11 | ;; 12 | 13 | let rec factorial x = 14 | match x with 15 | | 0 -> 1 16 | | _ -> times x (factorial (x - 1)) 17 | ;; 18 | 19 | (* These functions have a lot in common: 20 | 21 | let rec NAME x = 22 | match x with 23 | | 0 -> ANSWER 24 | | _ -> COMBINE x (NAME (x-1)) 25 | *) 26 | 27 | (* OCaml lets us write the common parts just once. 28 | We just add an extra input for every part that changes (other than the name): 29 | *) 30 | let rec up_to answer combine x = 31 | match x with 32 | | 0 -> answer 33 | | _ -> combine x (up_to answer combine (x - 1)) 34 | ;; 35 | 36 | (* Now we can write our original functions in one line each! *) 37 | let simpler_add_every_number_up_to x = up_to 0 plus x 38 | let simpler_factorial x = up_to 1 times x 39 | 40 | (* Note that with infix operators like + and *, you can actually pass them as functions! 41 | You can do this by writing ( + ) and ( * ). So another way to write the above two 42 | functions would be: 43 | 44 | let simpler_add_every_number_up_to x = up_to 0 ( + ) x 45 | let simpler_factorial x = up_to 1 ( * ) x 46 | *) 47 | 48 | (* Remember sum and product? *) 49 | let rec sum xs = 50 | match xs with 51 | | [] -> 0 52 | | x :: ys -> plus x (sum ys) 53 | ;; 54 | 55 | let rec product xs = 56 | match xs with 57 | | [] -> 1 58 | | x :: ys -> times x (product ys) 59 | ;; 60 | 61 | (* These functions look pretty similar too: 62 | 63 | let rec NAME xs = 64 | match xs with 65 | | [] -> ANSWER 66 | | x :: ys -> COMBINE x (NAME ys) 67 | *) 68 | 69 | (* Let's write the common parts just once: *) 70 | let rec every answer combine xs = failwith "For you to implement" 71 | 72 | (* Now let's rewrite sum and product in just one line each using [every] *) 73 | let simpler_sum xs = failwith "For you to implement" 74 | let simpler_product xs = failwith "For you to implement" 75 | 76 | let%test "Testing simpler_product..." = Int.( = ) 1 (simpler_product []) 77 | let%test "Testing simpler_product..." = Int.( = ) 55 (simpler_product [ 55 ]) 78 | let%test "Testing simpler_product..." = Int.( = ) 25 (simpler_product [ 5; -5; 1; -1 ]) 79 | let%test "Testing simpler_product..." = Int.( = ) 25 (simpler_product [ 5; 5; 1; 1 ]) 80 | let%test "Testing simpler_sum..." = Int.( = ) 0 (simpler_sum []) 81 | let%test "Testing simpler_sum..." = Int.( = ) 55 (simpler_sum [ 55 ]) 82 | let%test "Testing simpler_sum..." = Int.( = ) 0 (simpler_sum [ 5; -5; 1; -1 ]) 83 | let%test "Testing simpler_sum..." = Int.( = ) 12 (simpler_sum [ 5; 5; 1; 1 ]) 84 | -------------------------------------------------------------------------------- /02-exercises/11-sum_product/problem.mli: -------------------------------------------------------------------------------- 1 | open! Base 2 | 3 | val simpler_sum : int list -> int 4 | val simpler_product : int list -> int 5 | -------------------------------------------------------------------------------- /02-exercises/12-list_functions/dune: -------------------------------------------------------------------------------- 1 | ;; -*- scheme -*- 2 | 3 | (library 4 | (name problem_12) 5 | (libraries base stdio) 6 | (inline_tests) 7 | (preprocess (pps ppx_jane))) 8 | 9 | (env 10 | (dev 11 | (flags (:standard 12 | -w -20 13 | -w -27 14 | -w -32 15 | -w -34 16 | -w -37 17 | -w -39))) 18 | (release 19 | (flags (:standard)))) 20 | -------------------------------------------------------------------------------- /02-exercises/12-list_functions/dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 1.2) 2 | -------------------------------------------------------------------------------- /02-exercises/12-list_functions/problem.ml: -------------------------------------------------------------------------------- 1 | open! Base 2 | 3 | (* Many of the list functions we've been writing by hand are actually available 4 | in the language in a nice first class way. 5 | 6 | Let's take look at some of the useful functions that are given to you. 7 | *) 8 | 9 | (* List.fold 10 | 11 | val fold : 'a list ‑> init:'b ‑> f:('b ‑> 'a ‑> 'b) ‑> 'b 12 | 13 | Maybe this looks familiar? This is the same as the every 14 | function we wrote in the last problem. 15 | 16 | Let's rewrite simpler_sum and simpler_product using List.fold 17 | *) 18 | 19 | let simpler_sum xs = failwith "For you to implement" 20 | let simpler_product xs = failwith "For you to implement" 21 | 22 | (* List.map 23 | 24 | val map : 'a list ‑> f:('a ‑> 'b) ‑> 'b list 25 | 26 | [map] allows us to transforms lists from one type to lists 27 | of another type by applying some function (f) to every element 28 | of the list. 29 | 30 | Let's write a function that takes in an int list and transforms 31 | it into a float list 32 | *) 33 | 34 | let float_of_int xs = failwith "For you to implement" 35 | 36 | (* List.init 37 | 38 | val init : int -> f:(int -> 'a) -> 'a t 39 | 40 | [init] allows you to construct new lists. Given a number 41 | representing the number of elements to generate and a function to 42 | construct a new element, it returns a new list 43 | 44 | Let's rewrite the range function we wrote in problem 9 to use [init] 45 | *) 46 | 47 | let range from to_ = failwith "For you to implement" 48 | 49 | (* List.range 50 | 51 | Turns out this special case of [List.init] is useful enough that it has it's own 52 | function: 53 | 54 | val range : 55 | ?stride:int 56 | -> ?start:[ `exclusive | `inclusive ] 57 | -> ?stop:[ `exclusive | `inclusive ] 58 | -> int 59 | -> int 60 | -> int list *) 61 | 62 | (* List.iter 63 | 64 | val iter : 'a list -> f:('a -> unit) -> unit 65 | 66 | Sometimes you want to do something side-effecting to all the elements of a list, 67 | such as printing them out. [iter] allows you to run a side-effecting 68 | function on every element of a list 69 | 70 | Lets use [iter] to print a list of ints 71 | *) 72 | 73 | let print_int_list xs = failwith "For you to implement" 74 | 75 | (* There are many more useful List functions but a couple that are worth noting are 76 | 77 | * List.find 78 | 79 | val find : 'a list -> f:('a -> bool) -> 'a option 80 | 81 | This allows you to find the first element in a list that satifies some condition f 82 | 83 | * List.filter 84 | 85 | val filter : 'a list -> f:('a -> bool) -> 'a list 86 | 87 | This allows you to remove all elements from a list that do not satisfy some condition f 88 | 89 | * List.mapi 90 | 91 | val mapi : 'a list -> f:(int -> 'a -> 'b) -> 'b list 92 | 93 | This is just like map, but it also tells you the index of the element in the list 94 | 95 | * List.zip 96 | 97 | val zip : 'a list -> 'b list -> ('a * 'b) list option 98 | 99 | This allows you to combine two lists pairwise. It will return None if the lists are not 100 | equal in length 101 | *) 102 | 103 | let%test "Testing simpler_product..." = Int.( = ) 1 (simpler_product []) 104 | let%test "Testing simpler_product..." = Int.( = ) 55 (simpler_product [ 55 ]) 105 | let%test "Testing simpler_product..." = Int.( = ) 25 (simpler_product [ 5; -5; 1; -1 ]) 106 | let%test "Testing simpler_product..." = Int.( = ) 25 (simpler_product [ 5; 5; 1; 1 ]) 107 | let%test "Testing simpler_sum..." = Int.( = ) 0 (simpler_sum []) 108 | let%test "Testing simpler_sum..." = Int.( = ) 55 (simpler_sum [ 55 ]) 109 | let%test "Testing simpler_sum..." = Int.( = ) 0 (simpler_sum [ 5; -5; 1; -1 ]) 110 | let%test "Testing simpler_sum..." = Int.( = ) 12 (simpler_sum [ 5; 5; 1; 1 ]) 111 | 112 | let%test "Testing float_of_int..." = [%compare.equal: float list] (float_of_int [1; 2; 3]) [ 1.0; 2.0; 3.0 ] 113 | 114 | let%test "Testing range..." = [%compare.equal: int list] (range 1 4) [ 1; 2; 3 ] 115 | 116 | let%test "Testing range..." = 117 | [%compare.equal: int list] (range (-5) 3) [ -5; -4; -3; -2; -1; 0; 1; 2 ] 118 | ;; 119 | -------------------------------------------------------------------------------- /02-exercises/12-list_functions/problem.mli: -------------------------------------------------------------------------------- 1 | open! Base 2 | 3 | val print_int_list : int list -> unit -------------------------------------------------------------------------------- /02-exercises/13-labelled_arguments/dune: -------------------------------------------------------------------------------- 1 | ;; -*- scheme -*- 2 | 3 | (library 4 | (name problem_13) 5 | (libraries base stdio) 6 | (inline_tests) 7 | (preprocess (pps ppx_jane))) 8 | 9 | (env 10 | (dev 11 | (flags (:standard 12 | -w -20 13 | -w -27 14 | -w -32 15 | -w -34 16 | -w -37 17 | -w -39))) 18 | (release 19 | (flags (:standard)))) 20 | -------------------------------------------------------------------------------- /02-exercises/13-labelled_arguments/dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 1.2) 2 | -------------------------------------------------------------------------------- /02-exercises/13-labelled_arguments/problem.ml: -------------------------------------------------------------------------------- 1 | open! Base 2 | (* The following function has the signature: 3 | 4 | val divide : int -> int -> int 5 | 6 | Looking at just the signature, it's not obvious which int argument is 7 | the dividend and which is the divisor. 8 | *) 9 | let divide dividend divisor = dividend / divisor 10 | 11 | (* We can fix this using labelled arguments. 12 | 13 | To label an argument in a signature, "NAME:" is put before the type. 14 | When defining the function, we put a tilde (~) before the name of the argument. 15 | 16 | The following function has the signature: 17 | 18 | val divide : dividend:int -> divisor:int -> int 19 | *) 20 | let divide ~dividend ~divisor = dividend / divisor 21 | 22 | (* We can then call it using: 23 | 24 | divide ~dividend:9 ~divisor:3 25 | 26 | Labelled arguments can be passed in in any order (!) 27 | 28 | We can also pass variables into the labelled argument: 29 | 30 | let dividend = 9 in 31 | let divisor = 3 in 32 | divide ~dividend:dividend ~divisor:divisor 33 | 34 | If the variable name happens to be the same as the labelled argument, we 35 | don't even have to write it twice: 36 | 37 | let dividend = 9 in 38 | let divisor = 3 in 39 | divide ~dividend ~divisor 40 | *) 41 | 42 | (* Now implement [modulo ~dividend ~divisor] using our version of divide with labelled 43 | arguments (e.g. [modulo ~dividend:7 ~divisor:2] should equal 1) *) 44 | let modulo ~dividend ~divisor = failwith "For you to implement" 45 | 46 | let%test "Testing modulo..." = 47 | Int.(=) 2 (modulo ~dividend:17 ~divisor:5) 48 | 49 | let%test "Testing modulo..." = 50 | Int.(=) 0 (modulo ~dividend:99 ~divisor:9) 51 | 52 | -------------------------------------------------------------------------------- /02-exercises/13-labelled_arguments/problem.mli: -------------------------------------------------------------------------------- 1 | open! Base 2 | 3 | val modulo : dividend:int -> divisor:int -> int 4 | -------------------------------------------------------------------------------- /02-exercises/14-variants/dune: -------------------------------------------------------------------------------- 1 | ;; -*- scheme -*- 2 | 3 | (library 4 | (name problem_14) 5 | (libraries base stdio) 6 | (inline_tests) 7 | (preprocess (pps ppx_jane))) 8 | 9 | (env 10 | (dev 11 | (flags (:standard 12 | -w -20 13 | -w -27 14 | -w -32 15 | -w -34 16 | -w -37 17 | -w -39))) 18 | (release 19 | (flags (:standard)))) 20 | -------------------------------------------------------------------------------- /02-exercises/14-variants/dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 1.2) 2 | -------------------------------------------------------------------------------- /02-exercises/14-variants/problem.ml: -------------------------------------------------------------------------------- 1 | open! Base 2 | 3 | (* As in most languages, you can define your own types. 4 | The keyword "type" introduces a type definition. 5 | 6 | One of the non-basic types in OCaml is called the variant type. 7 | Variant types are similar to Enums in other languages. They are 8 | types which may take on multiple forms, where each form is marked 9 | by an explicit tag. A variant type is defined as follows: 10 | *) 11 | type color = 12 | | Red 13 | | Green 14 | | Blue 15 | 16 | (* Variants are very useful in combination with pattern matching *) 17 | let to_string color = 18 | match color with 19 | | Red -> "red" 20 | | Green -> "green" 21 | | Blue -> "blue" 22 | 23 | (* OCaml variants are in many ways more powerful than Enums because the different 24 | constructors of your variant can include data in them. Here's an example: 25 | *) 26 | type card_value = 27 | | Ace 28 | | King 29 | | Queen 30 | | Jack 31 | | Number of int 32 | 33 | let one_card_value : card_value = Queen 34 | let another_card_value : card_value = Number 8 35 | 36 | let card_value_to_string card_value = 37 | match card_value with 38 | | Ace -> "Ace" 39 | | King -> "King" 40 | | Queen -> "Queen" 41 | | Jack -> "Jack" 42 | | Number i -> Int.to_string i 43 | 44 | (* Write a function that computes the score of a card (aces should score 11 45 | and face cards should score 10). *) 46 | let card_value_to_score card_value = 47 | failwith "For you to implement" 48 | 49 | let%test "Testing card_value_to_score..." = 50 | Int.(=) 11 (card_value_to_score Ace) 51 | 52 | let%test "Testing card_value_to_score..." = 53 | Int.(=) 10 (card_value_to_score King) 54 | 55 | let%test "Testing card_value_to_score..." = 56 | Int.(=) 10 (card_value_to_score Queen) 57 | 58 | let%test "Testing card_value_to_score..." = 59 | Int.(=) 10 (card_value_to_score Jack) 60 | 61 | let%test "Testing card_value_to_score..." = 62 | Int.(=) 5 (card_value_to_score (Number 5)) 63 | 64 | -------------------------------------------------------------------------------- /02-exercises/14-variants/problem.mli: -------------------------------------------------------------------------------- 1 | open! Base 2 | 3 | type card_value 4 | 5 | val card_value_to_score : card_value -> int 6 | -------------------------------------------------------------------------------- /02-exercises/15-options/dune: -------------------------------------------------------------------------------- 1 | ;; -*- scheme -*- 2 | 3 | (library 4 | (name problem_15) 5 | (libraries base stdio) 6 | (inline_tests) 7 | (preprocess (pps ppx_jane))) 8 | 9 | (env 10 | (dev 11 | (flags (:standard 12 | -w -20 13 | -w -27 14 | -w -32 15 | -w -34 16 | -w -37 17 | -w -39))) 18 | (release 19 | (flags (:standard)))) 20 | -------------------------------------------------------------------------------- /02-exercises/15-options/dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 1.2) 2 | -------------------------------------------------------------------------------- /02-exercises/15-options/problem.ml: -------------------------------------------------------------------------------- 1 | open! Base 2 | 3 | (* Many languages have a concept of "Null", which describes that some data is 4 | absent. In OCaml, we can model the presence/absence data using ordinary 5 | variants. 6 | 7 | Note: we're defining the [option] type here to show you that it isn't magic. 8 | In real life you would always use the [option] type provided by the standard 9 | library. [Base] comes with a convenient [Option] module with many useful 10 | functions. *) 11 | type 'a option = 12 | | None 13 | | Some of 'a 14 | 15 | (* An ['a option] is either [None], meaning absence of data, or [Some x] meaning 16 | the data exists, and that data specifically is [x]. Here's an example: *) 17 | 18 | let what_number_am_i_thinking (my_number : int option) = 19 | match my_number with 20 | | None -> "I'm not thinking of any number!" 21 | | Some number -> "My number is: " ^ (Int.to_string number) 22 | 23 | let%test _ = 24 | String.(=) (what_number_am_i_thinking None) "I'm not thinking of any number!" 25 | 26 | let%test _ = 27 | String.(=) (what_number_am_i_thinking (Some 7)) "My number is: 7" 28 | 29 | (* Implement the function [safe_divide ~dividend ~divisor], which takes two ints 30 | and returns an int option. It should return None if [divisor = 0], and 31 | otherwise returns [Some x] where [x] is the division result *) 32 | let safe_divide ~dividend ~divisor = 33 | failwith "For you to implement" 34 | 35 | let%test "Testing safe_divide..." = 36 | match (safe_divide ~dividend:3 ~divisor:2) with 37 | | Some 1 -> true 38 | | _ -> false 39 | 40 | let%test "Testing safe_divide..." = 41 | match safe_divide ~dividend:3 ~divisor:0 with 42 | | None -> true 43 | | _ -> false 44 | 45 | (* Implement a function [concatenate string1 string2], which takes two 46 | [string option]s and returns a [string option] that is [Some x] 47 | where x is the concatenation of the two strings, if they exist, and 48 | [None] if either of the strings is [None]. *) 49 | let option_concatenate string1 string2 = 50 | failwith "For you to implement" 51 | 52 | let%test "Testing option_concatenate..." = 53 | match option_concatenate (Some "hello") (Some "world") with 54 | | Some "helloworld" -> true 55 | | _ -> false 56 | 57 | let%test "Testing option_concatenate..." = 58 | match option_concatenate None (Some "world") with 59 | | None -> true 60 | | _ -> false 61 | 62 | let%test "Testing option_concatenate..." = 63 | match option_concatenate (Some "hello") None with 64 | | None -> true 65 | | _ -> false 66 | -------------------------------------------------------------------------------- /02-exercises/15-options/problem.mli: -------------------------------------------------------------------------------- 1 | open! Base 2 | 3 | type 'a option = 4 | | None 5 | | Some of 'a 6 | 7 | val safe_divide : dividend:int -> divisor:int -> int option 8 | -------------------------------------------------------------------------------- /02-exercises/16-tuples/dune: -------------------------------------------------------------------------------- 1 | ;; -*- scheme -*- 2 | 3 | (library 4 | (name problem_16) 5 | (libraries base stdio) 6 | (inline_tests) 7 | (preprocess (pps ppx_jane))) 8 | 9 | (env 10 | (dev 11 | (flags (:standard 12 | -w -20 13 | -w -27 14 | -w -32 15 | -w -34 16 | -w -37 17 | -w -39))) 18 | (release 19 | (flags (:standard)))) 20 | -------------------------------------------------------------------------------- /02-exercises/16-tuples/dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 1.2) 2 | -------------------------------------------------------------------------------- /02-exercises/16-tuples/problem.ml: -------------------------------------------------------------------------------- 1 | open! Base 2 | 3 | (* Another non-basic type in OCaml is a tuple. A tuple is an ordered collection 4 | of values that can each be of a different type. The signature for a tuple is 5 | written by separating all the types within the tuple by a *. 6 | *) 7 | type int_and_string_and_char = int * string * char 8 | 9 | (* Tuples are created by joining values with a comma: *) 10 | let example : int_and_string_and_char = 5, "hello", 'A' 11 | 12 | (* You can also extract the components of a tuple: *) 13 | let i, s, c = example 14 | 15 | let () = 16 | assert (i = 5); 17 | assert (String.( = ) s "hello"); 18 | assert (Char.( = ) c 'A') 19 | ;; 20 | 21 | (* Consider a coordinate type containing the x and y values of a coordinate. 22 | Write a function that computes the sum of two coordinates. 23 | *) 24 | type coordinate = int * int 25 | 26 | let add coord1 coord2 = failwith "For you to implement" 27 | 28 | (* Now consider a name type containing strings representing first and last names *) 29 | type name = string * string 30 | 31 | (* Or an initials type containing chars representing first and last initials *) 32 | type initials = char * char 33 | 34 | (* Say we want to write a function that extracts the first element from a coordinate, 35 | name, or initials. We currently can't write that because they all have different 36 | types. 37 | 38 | Lets define a new pair type which is parameterized over the type contained in 39 | the pair. We write this as 40 | *) 41 | type 'a pair = 'a * 'a 42 | 43 | (* Our types defined above could be rewritten as 44 | 45 | type coordinate = int pair 46 | type name = string pair 47 | type initials = char pair 48 | *) 49 | 50 | (* We can construct pairs just like we construct regular tuples *) 51 | let int_pair : int pair = 5, 7 52 | let string_pair : string pair = "foo", "bar" 53 | let nested_char_pair : char pair pair = ('a', 'b'), ('c', 'd') 54 | 55 | (* Write functions to extract the first and second elements from a pair. *) 56 | (* val first : 'a pair -> 'a *) 57 | let first pair = failwith "For you to implement" 58 | 59 | (* val second : 'a pair -> 'a *) 60 | let second pair = failwith "For you to implement" 61 | 62 | (* Notice the cool [%compare.equal: int*int] here! *) 63 | let%test "Testing add..." = [%compare.equal: int * int] (4, 7) (add (5, 3) (-1, 4)) 64 | let%test "Testing first..." = String.( = ) "foo" (first ("foo", "bar")) 65 | let%test "Testing second..." = Char.( = ) 'b' (second ('a', 'b')) 66 | -------------------------------------------------------------------------------- /02-exercises/16-tuples/problem.mli: -------------------------------------------------------------------------------- 1 | open! Base 2 | 3 | val add : int * int -> int * int -> int * int 4 | 5 | type 'a pair 6 | val first : 'a pair -> 'a 7 | val second : 'a pair -> 'a 8 | 9 | -------------------------------------------------------------------------------- /02-exercises/17-records/dune: -------------------------------------------------------------------------------- 1 | ;; -*- scheme -*- 2 | 3 | (library 4 | (name problem_17) 5 | (libraries base stdio) 6 | (inline_tests) 7 | (preprocess (pps ppx_jane))) 8 | 9 | (env 10 | (dev 11 | (flags (:standard 12 | -w -20 13 | -w -27 14 | -w -32 15 | -w -34 16 | -w -37 17 | -w -39))) 18 | (release 19 | (flags (:standard)))) 20 | -------------------------------------------------------------------------------- /02-exercises/17-records/dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 1.2) 2 | -------------------------------------------------------------------------------- /02-exercises/17-records/problem.ml: -------------------------------------------------------------------------------- 1 | open! Base 2 | 3 | (* OCaml allows you to define record types. 4 | These are like structs in C, or data members of a class in python/ruby/java. 5 | *) 6 | 7 | type person = (* The name of the type is [person] *) 8 | (* it contains four fields *) 9 | (* The first field, called "age" is of type int. *) 10 | { age : int 11 | ; first_name : string 12 | ; last_name : string 13 | ; number_of_cars : int 14 | } [@@deriving compare] 15 | 16 | (* We can create a [person] like this. 17 | When defining and matching on a record, the fields 18 | can be listed in any order. 19 | *) 20 | let an_example : person = 21 | { first_name = "Cotton-eyed" 22 | ; last_name = "Joe" 23 | ; age = 22 24 | ; number_of_cars = 0 25 | } 26 | 27 | (* In order to get a field out of a record we use the "." operator: 28 | VARIABLE.FIELD 29 | *) 30 | let age : int = an_example.age 31 | let () = assert (age = 22) 32 | 33 | (* We can also match on records to get field information. *) 34 | let print_info {first_name; last_name; age; number_of_cars} = 35 | Stdio.print_endline first_name; 36 | Stdio.print_endline last_name; 37 | Stdio.printf "Age: %d, # of cars: %d\n" age number_of_cars 38 | ;; 39 | 40 | (* If we don't care about an argument we can ignore it using "= _" *) 41 | let print_name ({first_name; last_name; age = _; number_of_cars = _}) = 42 | Stdio.print_endline first_name; 43 | Stdio.print_endline last_name 44 | 45 | (* Finally, we can perform "functional updates" by replacing the value of a field, 46 | yielding a brand new record. We use the "with" keyword to do this. *) 47 | 48 | (* val add_one_to_age : person -> person *) 49 | let add_one_to_age person = 50 | { person with age = person.age + 1 } 51 | 52 | let () = assert (23 = (add_one_to_age an_example).age) 53 | 54 | (* Write a function that does different things for different people: 55 | When the person's first name is "Jan", 56 | you should return a record with the age set to 30. 57 | 58 | Otherwise, you should increase the number of cars by 6. 59 | *) 60 | 61 | (* val modify_person : person -> person *) 62 | 63 | let modify_person (person : person) = 64 | failwith "For you to implement" 65 | 66 | module For_testing = struct 67 | let test_ex1 : person = { 68 | first_name = "Jan"; 69 | last_name = "Saffer"; 70 | age = 55; 71 | number_of_cars = 0; 72 | };; 73 | 74 | let test_ex1' : person = {test_ex1 with age = 30};; 75 | 76 | let test_ex2 : person = { 77 | first_name = "Hugo"; 78 | last_name = "Heuzard"; 79 | age = 4; 80 | number_of_cars = 55; 81 | };; 82 | 83 | let test_ex2' : person = { test_ex2 with number_of_cars = 61};; 84 | 85 | let%test "Testing modify_person..." = 86 | [%compare.equal: person] test_ex1' (modify_person test_ex1) 87 | ;; 88 | 89 | let%test "Testing modify_person..." = 90 | [%compare.equal: person] test_ex2' (modify_person test_ex2) 91 | ;; 92 | end 93 | -------------------------------------------------------------------------------- /02-exercises/17-records/problem.mli: -------------------------------------------------------------------------------- 1 | open! Base 2 | 3 | type person 4 | 5 | val modify_person : person -> person 6 | -------------------------------------------------------------------------------- /02-exercises/18-mutable_records/dune: -------------------------------------------------------------------------------- 1 | ;; -*- scheme -*- 2 | 3 | (library 4 | (name problem_18) 5 | (libraries base stdio) 6 | (inline_tests) 7 | (preprocess (pps ppx_jane))) 8 | 9 | (env 10 | (dev 11 | (flags (:standard 12 | -w -20 13 | -w -27 14 | -w -32 15 | -w -34 16 | -w -37 17 | -w -39))) 18 | (release 19 | (flags (:standard)))) 20 | -------------------------------------------------------------------------------- /02-exercises/18-mutable_records/dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 1.2) 2 | -------------------------------------------------------------------------------- /02-exercises/18-mutable_records/problem.ml: -------------------------------------------------------------------------------- 1 | open! Base 2 | 3 | (* Sometimes rather than redefining the record you would like to have a field or 4 | a set of fields that you can modify on the fly. 5 | 6 | In OCaml if you want to have a field in a record that can be updated in place 7 | you must use some additional syntax. The mutable keyword makes the field 8 | modifiable. 9 | 10 | Then you can use <- to set the record value to a new value. *) 11 | type color = 12 | | Red 13 | | Yellow 14 | | Green 15 | 16 | (* You'll get an error about Unbound value compare_color. This is because we 17 | used the [compare] ppx for [stoplight] below, which has a [color] as one of 18 | its fields, but we didn't have [compare] on [color]. 19 | 20 | Fix it by adding this: [@@deriving compare] 21 | *) 22 | 23 | type stoplight = 24 | { location : string (* stoplights don't usually move *) 25 | ; mutable color : color (* but they often change color *) 26 | } 27 | [@@deriving compare] 28 | 29 | (* On creation mutable fields are defined just like normal fields *) 30 | let an_example : stoplight = 31 | { location = "The corner of Vesey Street and the West Side highway"; color = Red } 32 | ;; 33 | 34 | (* Now rather than using a functional update we can use a mutable update. 35 | This doesn't return a new stoplight, it modifies the input stoplight. 36 | *) 37 | let set_color stoplight color = stoplight.color <- color 38 | 39 | (* Since we know that stoplights always go from Green to Yellow, Yellow to 40 | Red, and Red to Green, we can just write a function to advance the color 41 | of the light without taking an input color. *) 42 | let advance_color stoplight = failwith "For you to implement" 43 | 44 | module For_testing = struct 45 | let test_ex_red : stoplight = { location = ""; color = Red } 46 | let test_ex_red' : stoplight = { test_ex_red with color = Green } 47 | let test_ex_yellow : stoplight = { location = ""; color = Yellow } 48 | let test_ex_yellow' : stoplight = { test_ex_red with color = Red } 49 | let test_ex_green : stoplight = { location = ""; color = Green } 50 | let test_ex_green' : stoplight = { test_ex_red with color = Yellow } 51 | 52 | let%test "Testing advance_color..." = 53 | advance_color test_ex_green; 54 | [%compare.equal: stoplight] test_ex_green' test_ex_green 55 | ;; 56 | 57 | let%test "Testing advance_color..." = 58 | advance_color test_ex_yellow; 59 | [%compare.equal: stoplight] test_ex_yellow' test_ex_yellow' 60 | ;; 61 | 62 | let%test "Testing advance_color..." = 63 | advance_color test_ex_red; 64 | [%compare.equal: stoplight] test_ex_red' test_ex_red 65 | ;; 66 | end 67 | -------------------------------------------------------------------------------- /02-exercises/18-mutable_records/problem.mli: -------------------------------------------------------------------------------- 1 | open! Base 2 | 3 | type stoplight 4 | 5 | val advance_color : stoplight -> unit 6 | -------------------------------------------------------------------------------- /02-exercises/19-refs/dune: -------------------------------------------------------------------------------- 1 | ;; -*- scheme -*- 2 | 3 | (library 4 | (name problem_19) 5 | (libraries base stdio) 6 | (inline_tests) 7 | (preprocess (pps ppx_jane))) 8 | 9 | (env 10 | (dev 11 | (flags (:standard 12 | -w -20 13 | -w -27 14 | -w -32 15 | -w -34 16 | -w -37 17 | -w -39))) 18 | (release 19 | (flags (:standard)))) 20 | -------------------------------------------------------------------------------- /02-exercises/19-refs/dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 1.2) 2 | -------------------------------------------------------------------------------- /02-exercises/19-refs/problem.ml: -------------------------------------------------------------------------------- 1 | open! Base 2 | 3 | (* It is sometimes useful to create a single mutable value. We can do this 4 | using a ref. We can create an [int ref] containing 0 as follows: 5 | *) 6 | let x = ref 0 7 | 8 | (* Then we can access the value in the ref using the ! operator, and 9 | we can update it using the := operator. So, we could increment our 10 | ref as follows: 11 | *) 12 | let () = 13 | x := !x + 1 14 | 15 | (* Write a function min_and_max which returns a tuple containing the 16 | minimum and maximum values in a non-empty list of positive 17 | integers. Your function should raise if the list is empty. 18 | 19 | Your function should iterate over the list and maintain refs of the 20 | minimum and maximum values seen so far. *) 21 | let min_and_max lst = 22 | failwith "For you to implement" 23 | 24 | let%test "Testing min_and_max..." = 25 | [%compare.equal: int*int] (min_and_max [5;9;2;4;3]) (2,9) 26 | ;; 27 | 28 | let%test "Testing min_and_max..." = 29 | [%compare.equal: int*int] (min_and_max [11;15;7;34]) (7,34) 30 | ;; 31 | -------------------------------------------------------------------------------- /02-exercises/19-refs/problem.mli: -------------------------------------------------------------------------------- 1 | open! Base 2 | 3 | val min_and_max : int list -> int * int 4 | -------------------------------------------------------------------------------- /02-exercises/20-anonymous_functions/dune: -------------------------------------------------------------------------------- 1 | ;; -*- scheme -*- 2 | 3 | (library 4 | (name problem_20) 5 | (libraries base stdio) 6 | (inline_tests) 7 | (preprocess (pps ppx_jane))) 8 | 9 | (env 10 | (dev 11 | (flags (:standard 12 | -w -20 13 | -w -27 14 | -w -32 15 | -w -34 16 | -w -37 17 | -w -39))) 18 | (release 19 | (flags (:standard)))) 20 | -------------------------------------------------------------------------------- /02-exercises/20-anonymous_functions/dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 1.2) 2 | -------------------------------------------------------------------------------- /02-exercises/20-anonymous_functions/problem.ml: -------------------------------------------------------------------------------- 1 | open! Base 2 | 3 | (* In OCaml, functions are values, so we can pass them in as 4 | arguments to other functions. 5 | 6 | To represent a function in a signature, you wrap its type in parenthesis, 7 | with arrows separating arguments. 8 | 9 | Recall: a function called [add1] which takes an integer and returns an integer has the type 10 | val add1 : int -> int 11 | 12 | So, to use that signature in a type, we'd write 13 | (int -> int) 14 | 15 | We now define a function called [map_option]. 16 | [map_option] takes a function and an option. 17 | 18 | If the option has a value of [None], [map_option] returns [None] 19 | If the option has a value of [Some x], the function is called on x, 20 | and wrapped up in a [Some]. 21 | 22 | It may seem unintuitive, but this kind of function is very useful 23 | because it allows you to continue applying functions to data 24 | without having to explicitly deal with null values or worry about 25 | null pointer exceptions if the data isn't there! 26 | 27 | The signature for the function is 28 | 29 | val map_option : ('a -> 'b) -> 'a option -> 'b option 30 | *) 31 | let map_option f opt = 32 | match opt with 33 | | None -> None 34 | | Some i -> Some (f i) 35 | 36 | let double i = 2 * i 37 | 38 | let () = 39 | assert 40 | ([%compare.equal: int option] 41 | (map_option double None) 42 | None) 43 | 44 | let () = 45 | assert 46 | ([%compare.equal: int option] 47 | (map_option double (Some 2)) 48 | (Some 4)) 49 | 50 | (* Instead of defining the function double beforehand, we can use 51 | an anonymous function. 52 | 53 | To write an anonymous function, the "fun" keyword is used in the following form 54 | 55 | (fun ARG1 ARG2 ... -> BODY) 56 | 57 | The following has the same effect as above: 58 | *) 59 | let () = 60 | assert 61 | ([%compare.equal: int option] 62 | (map_option (fun i -> 2 * i) (Some 2)) 63 | (Some 4)) 64 | 65 | (* Define a function, [apply_if_nonzero], which takes a function from 66 | int to int and an int, and applies the function if the integer 67 | is not zero, and otherwise just returns 0. 68 | *) 69 | let apply_if_nonzero f i = 70 | failwith "For you to implement" 71 | 72 | let%test "Testing apply_if_nonzero..." = 73 | Int.(=) 0 (apply_if_nonzero (fun x -> 10 / x) 0) 74 | 75 | let%test "Testing apply_if_nonzero..." = 76 | Int.(=) 2 (apply_if_nonzero (fun x -> 10 / x) 5) 77 | -------------------------------------------------------------------------------- /02-exercises/20-anonymous_functions/problem.mli: -------------------------------------------------------------------------------- 1 | open! Base 2 | 3 | val apply_if_nonzero : (int -> int) -> int -> int 4 | -------------------------------------------------------------------------------- /02-exercises/21-reading_sigs/dune: -------------------------------------------------------------------------------- 1 | ;; -*- scheme -*- 2 | 3 | (library 4 | (name problem_21) 5 | (libraries base stdio) 6 | (inline_tests) 7 | (preprocess (pps ppx_jane))) 8 | 9 | (env 10 | (dev 11 | (flags (:standard 12 | -w -20 13 | -w -27 14 | -w -32 15 | -w -34 16 | -w -37 17 | -w -39))) 18 | (release 19 | (flags (:standard)))) 20 | -------------------------------------------------------------------------------- /02-exercises/21-reading_sigs/dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 1.2) 2 | -------------------------------------------------------------------------------- /02-exercises/21-reading_sigs/problem.ml: -------------------------------------------------------------------------------- 1 | open! Base 2 | 3 | (* OCaml, like many other languages, provides a way to interact with code via 4 | interfaces. This allows implementation details to be hidden away, and for 5 | grouped units of code to restrict how they are used. 6 | 7 | Here's an example of a module signature coupled with an implementation. The 8 | signature is wrapped in a sig / end pair. The implementation is wrapped in a 9 | struct / end pair. *) 10 | module Example : sig 11 | (* Here, 'val' indicates that we are exposing a value. This value is an integer *) 12 | 13 | val the_meaning_of_life_the_universe_and_everything : int 14 | 15 | (* To declare functions, again we use 'val' - in OCaml, functions are values. 16 | This value takes an integer as a parameter and returns an integer 17 | *) 18 | 19 | val subtract_one : int -> int 20 | end = struct 21 | let the_meaning_of_life_the_universe_and_everything = 42 22 | let subtract_one x = x - 1 23 | end 24 | 25 | (* Here's how we use these values *) 26 | let one_less_than_the_meaning_of_life_etc = 27 | Example.subtract_one Example.the_meaning_of_life_the_universe_and_everything 28 | ;; 29 | 30 | assert (one_less_than_the_meaning_of_life_etc = 41) 31 | 32 | (* Types can be exposed via signatures in OCaml as well. Here's an example of declaring 33 | an "abstract" type - one where the definition of the type is not exposed. 34 | *) 35 | module Abstract_type_example : sig 36 | (* We do not let the user know that [t] is an integer *) 37 | 38 | type t 39 | 40 | (* This function allows [t] to be coerced into an integer *) 41 | 42 | val to_int : t -> int 43 | 44 | (* Users need some way to start with some [t] *) 45 | 46 | val zero : t 47 | val one : t 48 | 49 | (* Let them do something with the [t] *) 50 | 51 | val add : t -> t -> t 52 | end = struct 53 | type t = int 54 | 55 | let to_int x = x 56 | let zero = 0 57 | let one = 1 58 | let add = ( + ) 59 | end 60 | 61 | (* Here's an example of adding 2 and 2 *) 62 | let two = Abstract_type_example.add Abstract_type_example.one Abstract_type_example.one 63 | let four = Abstract_type_example.to_int (Abstract_type_example.add two two);; 64 | 65 | assert (four = 4) 66 | 67 | module Fraction : sig 68 | type t 69 | (* TODO: Add signatures for the create and value functions to expose them in 70 | the Fraction module. *) 71 | end = struct 72 | type t = int * int 73 | 74 | let create ~numerator ~denominator = numerator, denominator 75 | let value (numerator, denominator) = Float.of_int numerator /. Float.of_int denominator 76 | end 77 | 78 | let%test "Testing Fraction.value..." = 79 | Float.( = ) 2.5 (Fraction.value (Fraction.create ~numerator:5 ~denominator:2)) 80 | ;; 81 | 82 | let%test "Testing Fraction.value..." = 83 | Float.( = ) 0.4 (Fraction.value (Fraction.create ~numerator:4 ~denominator:10)) 84 | ;; 85 | -------------------------------------------------------------------------------- /02-exercises/21-reading_sigs/problem.mli: -------------------------------------------------------------------------------- 1 | (* This file deliberately left empty. *) 2 | -------------------------------------------------------------------------------- /02-exercises/dune: -------------------------------------------------------------------------------- 1 | ;; -*- Scheme -*- 2 | 3 | (alias 4 | (name DEFAULT) 5 | (deps 6 | (alias_rec 01-introduction/runtest) 7 | (alias_rec 02-basic_types/runtest) 8 | (alias_rec 03-define_functions/runtest) 9 | (alias_rec 04-call_functions/runtest) 10 | (alias_rec 05-twice/runtest) 11 | (alias_rec 06-pattern-matching/runtest) 12 | (alias_rec 07-simple_recursion/runtest) 13 | (alias_rec 08-list_intro/runtest) 14 | (alias_rec 09-list_range/runtest) 15 | (alias_rec 10-list_product/runtest) 16 | (alias_rec 11-sum_product/runtest) 17 | (alias_rec 12-list_functions/runtest) 18 | (alias_rec 13-labelled_arguments/runtest) 19 | (alias_rec 14-variants/runtest) 20 | (alias_rec 15-options/runtest) 21 | (alias_rec 16-tuples/runtest) 22 | (alias_rec 17-records/runtest) 23 | (alias_rec 18-mutable_records/runtest) 24 | (alias_rec 19-refs/runtest) 25 | (alias_rec 20-anonymous_functions/runtest) 26 | (alias_rec 21-reading_sigs/runtest) 27 | )) 28 | -------------------------------------------------------------------------------- /02-exercises/dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 1.2) 2 | -------------------------------------------------------------------------------- /03-github/README.org: -------------------------------------------------------------------------------- 1 | #+TITLE: Using the GitHub REST API 2 | 3 | Almost the whole of GitHub's functionality can be accessed and controlled using 4 | their REST API. MirageOS includes a library for OCaml which wraps many of these 5 | APIs, providing an easy and convenient way to query and control GitHub from the 6 | command line. 7 | 8 | You will be writing an OCaml reimplementation of some parts of GitHub's own 9 | [[https://github.com/github/hub][hub CLI]]. 10 | 11 | * Preparation 12 | Make sure your switch has the lwt_ssl and github-unix opam packages installed. 13 | You may first want to run =opam depext lwt_ssl= or ensure that pkg-config and 14 | the OpenSSL developement headers are installed. 15 | 16 | * Build and run 17 | Type =dune build hub.exe= to build, and =dune exec hub.exe -- args= to run it. 18 | 19 | * Challenges 20 | It's possible to start off trying out the bindings in utop. Just issue 21 | =#thread;;= to load system threading support and then =#require "github-unix";;= 22 | to get going. 23 | 24 | The bindings live within the Github "namespace" with a module corresponding to 25 | each section of the REST API. For this exercise, you'll mostly be using the 26 | Issue and Pull modules which correspond to the Issues and Pull Requests APIs. 27 | 28 | Like most of Mirage, the library is structured around a monad, which is defined 29 | in the aptly named Monad module. =Commands.show_issue= is a good place to start, 30 | and you will see that =Github.Issue.get= is the main call you'll want. Having 31 | got a promise back, you want to use =Github.Monad.run= in order to get something 32 | you can pass to =Lwt_main.run= in order to get an actual value. 33 | 34 | =Commands.list_issues= and =Commands.list_prs= are similar to each other, but 35 | you'll need to investigate the =Github.Stream= module. Rather than resorting to 36 | =to_list=, see if you can use =Github.Stream.next= to iterate over only the first 37 | 10 issues/prs returned. 38 | 39 | Finally, =checkout_pr= gives an opportunity to interact with the current 40 | directory - it's up to you whether you want to use Lwt_unix to interact with Git, 41 | the vanilla OCaml Unix library (particular Unix.open_process, to query git) or, 42 | for a complete MirageOS experience, the ocaml-git library to determine the state 43 | of the current directory. 44 | 45 | * Extensions 46 | If you look in =hub.ml=, you can see the definition for the command line parameters. 47 | Lots of extensions to these commands can be achieved by adding extra flags similar 48 | to user_t and repo_t. These can include result limiting, sorting orders, and so on. 49 | 50 | Running anonymously, you may quickly hit GitHub's rate limiting on REST API calls. 51 | There are a couple of ways around this - the best is to take advantage of the 52 | =git jar= command installed. All API calls have a =?token= optional parameter which 53 | can be either a login token or an OAUTH token. github-unix installs the git jar command 54 | which allows you create and store OAUTH tokens in your home directory and retrieve 55 | them using the =Github_ookie_jar= module. =Github.Token= also contains functions for 56 | negotiating GitHub's 2 factor login. 57 | -------------------------------------------------------------------------------- /03-github/commands.ml: -------------------------------------------------------------------------------- 1 | (* Four possible subcommands for hub *) 2 | 3 | let show_issue ~user:_ ~repo:_ _number : unit = 4 | failwith "Display summary information for user/repo#number" 5 | 6 | let list_issues ~user:_ ~repo:_ () : unit = 7 | failwith "List issues for user/repo" 8 | 9 | let list_prs ~user:_ ~repo:_ () : unit = 10 | failwith "List pull requests for user/repo" 11 | 12 | let checkout_pr ~user:_ ~repo:_ ?branch:_ _number : unit = 13 | failwith "Checkout user/repo#number either to the name of pull request branch \ 14 | or to the branch name given in ?branch" 15 | -------------------------------------------------------------------------------- /03-github/dune: -------------------------------------------------------------------------------- 1 | (executable 2 | (name hub) 3 | (modules hub commands) 4 | (libraries cmdliner github-unix)) 5 | -------------------------------------------------------------------------------- /03-github/hub.ml: -------------------------------------------------------------------------------- 1 | open Cmdliner 2 | 3 | let user_t = 4 | let doc = "GitHub user/organisation to read" in 5 | let open Arg in 6 | value & opt string "ocaml" & info ["user"; "u"] ~doc 7 | 8 | let repo_t = 9 | let doc = "GitHub repository to read" in 10 | let open Arg in 11 | value & opt string "ocaml" & info ["repository"; "r"] ~doc 12 | 13 | (* This helper takes a list of subcommand and parses the positional 14 | * arguments to return the command (if matched) and any additional arguments *) 15 | let mk_subcommands commands = 16 | let command = 17 | let doc = Arg.info ~docv:"COMMAND" [] in 18 | let commands = 19 | List.fold_left 20 | (fun acc (c,f,_,_) -> (c,f) :: acc) [] commands in 21 | Arg.(value & pos 0 (some & enum commands) None & doc) 22 | in 23 | let params = 24 | let doc = Arg.info ~doc:"Optional parameters." [] in 25 | Arg.(value & pos_right 0 string [] & doc) 26 | in 27 | command, params 28 | 29 | (* The issue command by default lists issues and has a show subcommand *) 30 | let issue_cmd = 31 | let commands = [ 32 | "show", `show, ["NUMBER"], "Displays issue $(i,NUMBER)"; 33 | ] in 34 | let man = 35 | [ `S Manpage.s_description 36 | ; `P "This command manipulates GitHub issues" 37 | ] 38 | in 39 | let doc = "The issue command" in 40 | let command, params = mk_subcommands commands in 41 | let issue command params user repo = 42 | match command, params with 43 | | None, [] -> Commands.list_issues ~user ~repo (); `Ok () 44 | | Some `show, [number] -> Commands.show_issue ~user ~repo (int_of_string number); `Ok () 45 | | Some `show, _ -> `Error (false, "Expect one issue number for hub issue command") 46 | | _ -> `Error (false, "Unrecognised hub issue command") 47 | in 48 | Term.(ret (const issue $command $params $user_t $repo_t), 49 | info "issue" ~man ~doc) 50 | 51 | (* The pr command has a list command (which is also the default) and a checkout command *) 52 | let pr_cmd = 53 | let commands = [ 54 | "checkout", `checkout, ["NUMBER"; "[BRANCH]"], "Checks out the branch for a PR in this clone"; 55 | "list", `list, [], "Lists PRs"; 56 | ] in 57 | let man = 58 | [ `S Manpage.s_description 59 | ; `P "This command manipulates GitHub pull requests" 60 | ] 61 | in 62 | let doc = "The pr command" in 63 | let command, params = mk_subcommands commands in 64 | let issue command params user repo = 65 | match command, params with 66 | | None, _ 67 | | Some `list, _ -> 68 | Commands.list_prs ~user ~repo (); `Ok () 69 | | Some `checkout, [number] -> 70 | Commands.checkout_pr ~user ~repo (int_of_string number); `Ok () 71 | | Some `checkout, [number; branch] -> 72 | Commands.checkout_pr ~user ~repo ~branch (int_of_string number); `Ok () 73 | | Some `checkout, _ -> 74 | `Error (false, "Expect a pr number and optionally a branch for hub pr checkout command") 75 | | _ -> `Error (false, "Unrecognised hub pr command") 76 | in 77 | Term.(ret (const issue $command $params $user_t $repo_t), info "pr" ~man ~doc) 78 | 79 | (* The default command is just the help screen *) 80 | let default_cmd = 81 | let doc = "Hub is a tool that wraps git in order to extend with extra functionality that makes \ 82 | it better when working with GitHub. It's written in pure OCaml which makes it even \ 83 | more cool." in 84 | let sdocs = Manpage.s_common_options in 85 | let man_xrefs = [] in 86 | let man = 87 | [ `S Manpage.s_description 88 | ; `P "The $(tname) tool provides cool features." 89 | ; `P "You'd expect to have more paragraphs in the final man page!" 90 | ] 91 | in 92 | Term.(ret (const (fun _ -> `Help (`Pager, None)) $ pure ()), 93 | info "hub" ~version:"ReasonConf2019" ~doc ~man_xrefs ~sdocs ~man) 94 | 95 | let cmds = [issue_cmd; pr_cmd] 96 | 97 | let () = Term.(exit @@ eval_choice default_cmd cmds) 98 | -------------------------------------------------------------------------------- /04-frogger/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM kyma/docker-nginx 2 | COPY . /var/www 3 | CMD 'nginx' 4 | -------------------------------------------------------------------------------- /04-frogger/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | dune build @DEFAULT 3 | -------------------------------------------------------------------------------- /04-frogger/README.org: -------------------------------------------------------------------------------- 1 | #+TITLE: Frogger in your browser 2 | 3 | If you've never played Frogger, play an online version (picked at random) here: 4 | [[https://denodell.github.io/frogger/]] 5 | 6 | You will be writing a simplified version that discretizes time and space, which 7 | makes the collision and movement logic a lot easier to implement. [[https://ocamllabs.github.io/learn-ocaml-workshop/frogger.html][Here's]] an 8 | example of the finished product. Try pressing the keys ~i~ and ~u~ to see some 9 | fun tricks! 10 | 11 | * ~js_of_ocaml~ 12 | This version of Frogger will run in the browser by using ~js-of-ocaml~ to 13 | transpile OCaml bytecode into Javascript. Fortunately for us, ~dune~ 14 | supports this out of the box: all one needs to do is ask for the ~.bc.js~ 15 | target (see the definition of the ~DEFAULT~ alias in the [[file:dune][dune]] file). 16 | 17 | First, test your installation of ~js-of-ocaml~ by running ~make~ in the 18 | [[file:test-js-of-ocaml-install][test-js-of-ocaml-install]] directory. Then, point your browser at 19 | ~_build/default/03-frogger/test-js-of-ocaml-install/index.html~. 20 | 21 | * Writing a game in functional programming style 22 | We have written a simple scaffold that handles graphics, events and 23 | interactions with the DOM so you can focus on implementing just the game 24 | logic. [[file:scaffold.mli][scaffold.mli]] defines modules and types that you'll need to use, like 25 | the number and kinds of rows in the playing board, and images of characters. 26 | 27 | Take a look at [[file:frogger.mli][frogger.mli]]. This is the interface you will implement by 28 | writing a corresponding ~frogger.ml~. The contract between the scaffold and 29 | your code is that you implement the four functions at the bottom of this 30 | ~.mli~, and the scaffold will call those functions with the appropriate events 31 | at the right times. 32 | 33 | A ~World.t~ represents the entire state of the game at a given point in time. 34 | It is up to you to define what goes in the type -- that's why ~frogger.mli~ 35 | does not specify what's inside the type (we call this an /opaque/ type). 36 | However, to help you get started, we've specified some function signatures in 37 | the ~World~ module [[file:suggested_frogger.mli][here]] that are likely to be useful. 38 | 39 | To specify the logic of the game, you'll need to figure out the following 40 | things (these correspond to the functions in [[file:frogger.mli][frogger.mli]]): 41 | 42 | ** How to create the world 43 | This is the ~create~ function. You may want to use the ~Random~ module (from 44 | ~Base~) to make life interesting. 45 | 46 | ** How to advance the world one timestep 47 | For this first project, we'll say that time advances only in units of 1 48 | second. The ~tick~ function should implement how the ~World.t~ is transformed 49 | when time advances. Note its signature: 50 | 51 | #+BEGIN_SRC ocaml 52 | val tick : World.t -> World.t 53 | #+END_SRC 54 | 55 | It takes a ~World.t~ and returns a new ~World.t~. Writing your game in this 56 | functional style will allow us to do some interesting things later on. 57 | 58 | ** How to respond to player input 59 | Players can press one of the four arrow keys to move their character around. 60 | You specify what to do when they do that by writing a ~handle_input~ 61 | function. 62 | 63 | All this function needs to know is: what the current state of the world is (a 64 | ~World.t~), and what button player pressed (a ~Key.t~). Its output: the 65 | resulting state of the world (a ~World.t~). 66 | 67 | ** ~handle_event~: dispatch to ~tick~ or ~handle_input~ 68 | It's nice to be able to say, "The only things that happen in this game are: 69 | time progressing, and the player doing something." Your ~handle_event~ 70 | function should just ~match~ on the kind of event and dispatch to the 71 | appropriate handler (one of the two above). 72 | 73 | ** How to draw the world 74 | A list of tuples ~(Image.t, Position.t)~ tell the scaffold which images to 75 | draw where, and in what order: images later in the list will be overlaid on 76 | top of earlier ones at the same position. 77 | 78 | * That seems awfully complicated! How should I start? 79 | A reasonable starting point is to just move the frog (well, camel) around the 80 | game board. Read [[file:frogger.ml][frogger.ml]] and follow the suggestions to build this basic game. 81 | 82 | * Build and run 83 | Type =make= to build, and point your browser to 84 | =_build/default/03-frogger/index.html= to play the game! 85 | 86 | * Extensions 87 | ** AI 88 | Write an AI player for your game. Given the initial ~World.t~, it should emit 89 | a sequence of ~Key.t option~, one for every timestep. 90 | 91 | To see your AI in action you will need to modify the scaffold a little: 92 | instead of feeding player input into the ~handle_input~ function, make it 93 | feed in the output of the AI you write. 94 | 95 | *** Some interesting extensions once you've written an AI 96 | 1. Does your ~create~ function ever produce initial states that cannot be 97 | played to a win? 98 | 99 | 2. How would you write AI to deal with potential randomness in the ~tick~ 100 | function? 101 | 102 | ** Continuous time 103 | While the interpolating scaffold is a neat trick, it's not perfect because 104 | the collision detection logic is now out-of-sync with what's going on 105 | visually. Extend the interface, game logic and scaffold to produce a 106 | smoothly-animated Frogger that also plays right. 107 | -------------------------------------------------------------------------------- /04-frogger/assets/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sudha247/learn-ocaml-workshop/b46ee4375098adf7c299a245304516fffed7e0cd/04-frogger/assets/background.png -------------------------------------------------------------------------------- /04-frogger/assets/buggy-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sudha247/learn-ocaml-workshop/b46ee4375098adf7c299a245304516fffed7e0cd/04-frogger/assets/buggy-left.png -------------------------------------------------------------------------------- /04-frogger/assets/buggy-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sudha247/learn-ocaml-workshop/b46ee4375098adf7c299a245304516fffed7e0cd/04-frogger/assets/buggy-right.png -------------------------------------------------------------------------------- /04-frogger/assets/camel-down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sudha247/learn-ocaml-workshop/b46ee4375098adf7c299a245304516fffed7e0cd/04-frogger/assets/camel-down.png -------------------------------------------------------------------------------- /04-frogger/assets/camel-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sudha247/learn-ocaml-workshop/b46ee4375098adf7c299a245304516fffed7e0cd/04-frogger/assets/camel-left.png -------------------------------------------------------------------------------- /04-frogger/assets/camel-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sudha247/learn-ocaml-workshop/b46ee4375098adf7c299a245304516fffed7e0cd/04-frogger/assets/camel-right.png -------------------------------------------------------------------------------- /04-frogger/assets/camel-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sudha247/learn-ocaml-workshop/b46ee4375098adf7c299a245304516fffed7e0cd/04-frogger/assets/camel-up.png -------------------------------------------------------------------------------- /04-frogger/assets/carpet_blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sudha247/learn-ocaml-workshop/b46ee4375098adf7c299a245304516fffed7e0cd/04-frogger/assets/carpet_blue.png -------------------------------------------------------------------------------- /04-frogger/assets/carpet_green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sudha247/learn-ocaml-workshop/b46ee4375098adf7c299a245304516fffed7e0cd/04-frogger/assets/carpet_green.png -------------------------------------------------------------------------------- /04-frogger/assets/carpet_red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sudha247/learn-ocaml-workshop/b46ee4375098adf7c299a245304516fffed7e0cd/04-frogger/assets/carpet_red.png -------------------------------------------------------------------------------- /04-frogger/assets/confetti.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sudha247/learn-ocaml-workshop/b46ee4375098adf7c299a245304516fffed7e0cd/04-frogger/assets/confetti.png -------------------------------------------------------------------------------- /04-frogger/assets/police-car-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sudha247/learn-ocaml-workshop/b46ee4375098adf7c299a245304516fffed7e0cd/04-frogger/assets/police-car-left.png -------------------------------------------------------------------------------- /04-frogger/assets/police-car-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sudha247/learn-ocaml-workshop/b46ee4375098adf7c299a245304516fffed7e0cd/04-frogger/assets/police-car-right.png -------------------------------------------------------------------------------- /04-frogger/assets/red-pickup-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sudha247/learn-ocaml-workshop/b46ee4375098adf7c299a245304516fffed7e0cd/04-frogger/assets/red-pickup-left.png -------------------------------------------------------------------------------- /04-frogger/assets/red-pickup-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sudha247/learn-ocaml-workshop/b46ee4375098adf7c299a245304516fffed7e0cd/04-frogger/assets/red-pickup-right.png -------------------------------------------------------------------------------- /04-frogger/assets/skull.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sudha247/learn-ocaml-workshop/b46ee4375098adf7c299a245304516fffed7e0cd/04-frogger/assets/skull.png -------------------------------------------------------------------------------- /04-frogger/assets/truck-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sudha247/learn-ocaml-workshop/b46ee4375098adf7c299a245304516fffed7e0cd/04-frogger/assets/truck-left.png -------------------------------------------------------------------------------- /04-frogger/assets/truck-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sudha247/learn-ocaml-workshop/b46ee4375098adf7c299a245304516fffed7e0cd/04-frogger/assets/truck-right.png -------------------------------------------------------------------------------- /04-frogger/config.ml: -------------------------------------------------------------------------------- 1 | open! Base 2 | open! Import 3 | 4 | open Scaffold 5 | 6 | type t = 7 | { num_cols : int 8 | ; num_rows : int 9 | ; grid_size_in_px : int 10 | ; render_interval_ms : float 11 | ; logic_interval_ms : float 12 | } 13 | 14 | let default = 15 | { num_rows = List.length Board.rows 16 | ; num_cols = Board.num_cols 17 | ; grid_size_in_px = 50 18 | ; render_interval_ms = 50. 19 | ; logic_interval_ms = 1000. 20 | } 21 | 22 | let width t = t.num_cols * t.grid_size_in_px 23 | let height t = t.num_rows * t.grid_size_in_px 24 | 25 | 26 | -------------------------------------------------------------------------------- /04-frogger/draw.ml: -------------------------------------------------------------------------------- 1 | open! Base 2 | open! Js_of_ocaml 3 | open! Import 4 | 5 | open Scaffold 6 | 7 | module Screen = struct 8 | type t = 9 | { context : Html.canvasRenderingContext2D Js.t 10 | ; width : int 11 | ; height : int 12 | } 13 | end 14 | 15 | module Image_impl = struct 16 | type t = 17 | { image_element : Html.imageElement Js.t 18 | } 19 | 20 | let create path = 21 | let image_element = Html.createImg document in 22 | image_element##.src := Js.string path; 23 | { image_element 24 | } 25 | ;; 26 | 27 | (* If we were using a concurrency library like [Async] or [Lwt], we would want 28 | to make [width] and [height] members of the record. But they can only be 29 | read after the image has loaded. *) 30 | let width t = jsoptdef_value_exn (t.image_element##.naturalWidth ) 31 | let height t = jsoptdef_value_exn (t.image_element##.naturalHeight) 32 | 33 | let draw (screen : Screen.t) t x y img_width img_height = 34 | let f = Int.to_float in 35 | screen.context##drawImage_full 36 | t.image_element 37 | 0. 0. 38 | (width t |> f) (height t |> f) 39 | x y 40 | (f img_width) (f img_height) 41 | end 42 | 43 | module Wad = struct 44 | type t = 45 | { background : Image_impl.t 46 | ; skull_and_crossbones : Image_impl.t 47 | ; frog_up : Image_impl.t 48 | ; frog_down : Image_impl.t 49 | ; frog_left : Image_impl.t 50 | ; frog_right : Image_impl.t 51 | ; car1_left : Image_impl.t 52 | ; car2_left : Image_impl.t 53 | ; car1_right : Image_impl.t 54 | ; car2_right : Image_impl.t 55 | ; car3_left : Image_impl.t 56 | ; car3_right : Image_impl.t 57 | ; confetti : Image_impl.t 58 | ; log1 : Image_impl.t 59 | ; log2 : Image_impl.t 60 | ; log3 : Image_impl.t 61 | } 62 | [@@deriving fields] 63 | 64 | let create (_config : Config.t) = 65 | let background = Image_impl.create "assets/background.png" in 66 | let skull_and_crossbones = Image_impl.create "assets/skull.png" in 67 | let frog_up = Image_impl.create "assets/camel-up.png" in 68 | let frog_down = Image_impl.create "assets/camel-down.png" in 69 | let frog_left = Image_impl.create "assets/camel-left.png" in 70 | let frog_right = Image_impl.create "assets/camel-right.png" in 71 | let car1_left = Image_impl.create "assets/buggy-left.png" in 72 | let car1_right = Image_impl.create "assets/buggy-right.png" in 73 | let car2_left = Image_impl.create "assets/truck-left.png" in 74 | let car2_right = Image_impl.create "assets/truck-right.png" in 75 | let car3_left = Image_impl.create "assets/police-car-left.png" in 76 | let car3_right = Image_impl.create "assets/police-car-right.png" in 77 | let log1 = Image_impl.create "assets/carpet_blue.png" in 78 | let log2 = Image_impl.create "assets/carpet_green.png" in 79 | let log3 = Image_impl.create "assets/carpet_red.png" in 80 | let confetti = Image_impl.create "assets/confetti.png" in 81 | { background 82 | ; skull_and_crossbones 83 | ; frog_up 84 | ; frog_down 85 | ; frog_left 86 | ; frog_right 87 | ; car1_left 88 | ; car2_left 89 | ; car1_right 90 | ; car2_right 91 | ; car3_left 92 | ; car3_right 93 | ; confetti 94 | ; log1 95 | ; log2 96 | ; log3 97 | } 98 | ;; 99 | 100 | let lookup_image t (image : Image.t) = 101 | match image with 102 | | Frog_up -> t.frog_up 103 | | Frog_down -> t.frog_down 104 | | Frog_left -> t.frog_left 105 | | Frog_right -> t.frog_right 106 | 107 | | Car1_left -> t.car1_left 108 | | Car1_right -> t.car1_right 109 | | Car2_left -> t.car2_left 110 | | Car2_right -> t.car2_right 111 | | Car3_left -> t.car3_left 112 | | Car3_right -> t.car3_right 113 | 114 | | Log1 -> t.log1 115 | | Log2 -> t.log2 116 | | Log3 -> t.log3 117 | 118 | | Confetti -> t.confetti 119 | | Skull_and_crossbones -> t.skull_and_crossbones 120 | end 121 | -------------------------------------------------------------------------------- /04-frogger/dune: -------------------------------------------------------------------------------- 1 | (executable 2 | (name main) 3 | (preprocess 4 | (pps js_of_ocaml-ppx ppx_jane)) 5 | (modules_without_implementation suggested_frogger) 6 | (libraries base js_of_ocaml)) 7 | 8 | (alias 9 | (name DEFAULT) 10 | (deps 11 | main.bc.js 12 | index.html 13 | (glob_files assets/*.png) 14 | Dockerfile)) 15 | -------------------------------------------------------------------------------- /04-frogger/frogger.ml: -------------------------------------------------------------------------------- 1 | open Base 2 | open Scaffold 3 | 4 | [@@@warning "-27-32"] 5 | 6 | module Frog = struct 7 | type t = 8 | { position : Position.t 9 | } [@@deriving fields] 10 | 11 | let create = Fields.create 12 | end 13 | 14 | module World = struct 15 | type t = 16 | { frog : Frog.t 17 | } [@@deriving fields] 18 | 19 | let create = Fields.create 20 | end 21 | 22 | let create_frog () = 23 | failwith 24 | "Figure out how to initialize the [Frog.t] at the beginning of the game. \ 25 | Call [Frog.create] with some arguments." 26 | ;; 27 | 28 | let create () = 29 | failwith 30 | "Call [World.create] and [create_frog] to construct the initial state \ 31 | of the game. Try using [Random.int] -- variety is the spice of life!" 32 | ;; 33 | 34 | let tick (world : World.t) = 35 | failwith 36 | "This function will end up getting called every timestep, which happens to \ 37 | be set to 1 second for this game in the scaffold (so you can easily see \ 38 | what's going on). For the first step (just moving the frog/camel around), \ 39 | you can just return [world] here. Later you'll want do interesting things \ 40 | like move all the cars and logs, detect collisions and figure out if the \ 41 | player has died or won. " 42 | ;; 43 | 44 | let handle_input (world : World.t) key = 45 | failwith 46 | "This function will end up getting called whenever the player presses one of \ 47 | the four arrow keys. What should the new state of the world be? Create and \ 48 | return it based on the current state of the world (the [world] argument), \ 49 | and the key that was pressed ([key]). Use either [World.create] or the \ 50 | record update syntax: 51 | { world with frog = Frog.create ... } 52 | " 53 | ;; 54 | 55 | let draw (world : World.t) = 56 | failwith 57 | "Return a list with a single item: a tuple consisting of one of the choices \ 58 | in [Images.t] in [scaffold.mli]; and the current position of the [Frog]." 59 | ;; 60 | 61 | let handle_event world event = 62 | failwith 63 | "This function should probably be just 3 lines long: [match event with ...]" 64 | ;; 65 | 66 | let finished world = 67 | failwith 68 | "This can probably just return [false] in the beginning." 69 | ;; 70 | -------------------------------------------------------------------------------- /04-frogger/frogger.mli: -------------------------------------------------------------------------------- 1 | open Scaffold 2 | 3 | module World : sig 4 | type t 5 | end 6 | 7 | val create : unit -> World.t 8 | val handle_event : World.t -> Event.t -> World.t 9 | val draw : World.t -> Display_list.t 10 | val finished : World.t -> bool 11 | 12 | 13 | -------------------------------------------------------------------------------- /04-frogger/import.ml: -------------------------------------------------------------------------------- 1 | open Js_of_ocaml 2 | 3 | module Html = Dom_html 4 | let document = Html.window##.document 5 | 6 | let jsopt_value_exn x = Js.Opt.get x (fun () -> assert false) 7 | let jsoptdef_value_exn x = Js.Optdef.get x (fun () -> assert false) 8 | -------------------------------------------------------------------------------- /04-frogger/index.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 |
6 |