diff options
Diffstat (limited to 'ocaml.html.markdown')
-rw-r--r-- | ocaml.html.markdown | 205 |
1 files changed, 169 insertions, 36 deletions
diff --git a/ocaml.html.markdown b/ocaml.html.markdown index 70668611..2c5e13d5 100644 --- a/ocaml.html.markdown +++ b/ocaml.html.markdown @@ -3,26 +3,31 @@ language: OCaml filename: learnocaml.ml contributors: - ["Daniil Baturin", "http://baturin.org/"] + - ["Stanislav Modrak", "https://stanislav.gq/"] + - ["Luke Tong", "https://lukert.me/"] --- - OCaml is a strictly evaluated functional language with some imperative features. -Along with StandardML and its dialects it belongs to ML language family. +Along with Standard ML and its dialects it belongs to ML language family. F# is also heavily influenced by OCaml. -Just like StandardML, OCaml features both an interpreter, that can be +Just like Standard ML, OCaml features both an interpreter, that can be used interactively, and a compiler. -The interpreter binary is normally called "ocaml" and the compiler is "ocamlopt". -There is also a bytecode compiler, "ocamlc", but there are few reasons to use it. +The interpreter binary is normally called `ocaml` and the compiler is `ocamlopt`. +There is also a bytecode compiler, `ocamlc`, but there are few reasons to use it. + +It also includes a package manager, `opam`, and a build system, `dune`. It is strongly and statically typed, but instead of using manually written -type annotations, it infers types of expressions using Hindley-Milner algorithm. +type annotations, it infers types of expressions using the +[Hindley-Milner](https://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_system) +algorithm. It makes type annotations unnecessary in most cases, but can be a major source of confusion for beginners. When you are in the top level loop, OCaml will print the inferred type -after you enter an expression. +after you enter an expression ``` # let inc x = x + 1 ;; @@ -31,8 +36,8 @@ val inc : int -> int = <fun> val a : int = 99 ``` -For a source file you can use "ocamlc -i /path/to/file.ml" command -to print all names and type signatures. +For a source file you can use the `ocamlc -i /path/to/file.ml` command +to print all names and type signatures ``` $ cat sigtest.ml @@ -48,12 +53,12 @@ val a : int ``` Note that type signatures of functions of multiple arguments are -written in curried form. A function that takes multiple arguments can be +written in [curried](https://en.wikipedia.org/wiki/Currying) form. +A function that takes multiple arguments can be represented as a composition of functions that take only one argument. -The "f(x,y) = x + y" function from the example above applied to -arguments 2 and 3 is equivalent to the "f0(y) = 2 + y" function applied to 3. -Hence the "int -> int -> int" signature. - +The `f(x,y) = x + y` function from the example above applied to +arguments 2 and 3 is equivalent to the `f0(y) = 2 + y` function applied to 3. +Hence the `int -> int -> int` signature. ```ocaml (*** Comments ***) @@ -65,13 +70,14 @@ Hence the "int -> int -> int" signature. (*** Variables and functions ***) -(* Expressions can be separated by a double semicolon symbol, ";;". +(* Expressions can be separated by a double semicolon ";;". In many cases it's redundant, but in this tutorial we use it after every expression for easy pasting into the interpreter shell. Unnecessary use of expression separators in source code files is often considered to be a bad style. *) -(* Variable and function declarations use "let" keyword. *) +(* Variable and function declarations use the "let" keyword. *) +(* Variables are immutable by default in OCaml *) let x = 10 ;; (* OCaml allows single quote characters in identifiers. @@ -109,42 +115,42 @@ let sqr2 = sqr (-2) ;; "unit" type for it that has the only one value written as "()" *) let print_hello () = print_endline "hello world" ;; -(* Note that you must specify "()" as argument when calling it. *) +(* Note that you must specify "()" as the argument when calling it. *) print_hello () ;; -(* Calling a function with insufficient number of arguments +(* Calling a function with an insufficient number of arguments does not cause an error, it produces a new function. *) let make_inc x y = x + y ;; (* make_inc is int -> int -> int *) let inc_2 = make_inc 2 ;; (* inc_2 is int -> int *) inc_2 3 ;; (* Evaluates to 5 *) -(* You can use multiple expressions in function body. +(* You can use multiple expressions in the function body. The last expression becomes the return value. All other expressions must be of the "unit" type. This is useful when writing in imperative style, the simplest - form of it is inserting a debug print. *) + form of which is inserting a debug print. *) let print_and_return x = print_endline (string_of_int x); x ;; (* Since OCaml is a functional language, it lacks "procedures". - Every function must return something. So functions that - do not really return anything and are called solely for their - side effects, like print_endline, return value of "unit" type. *) + Every function must return something. So functions that do not + really return anything and are called solely for their side + effects, like print_endline, return a value of "unit" type. *) -(* Definitions can be chained with "let ... in" construct. - This is roughly the same to assigning values to multiple +(* Definitions can be chained with the "let ... in" construct. + This is roughly the same as assigning values to multiple variables before using them in expressions in imperative languages. *) let x = 10 in let y = 20 in x + y ;; -(* Alternatively you can use "let ... and ... in" construct. +(* Alternatively you can use the "let ... and ... in" construct. This is especially useful for mutually recursive functions, - with ordinary "let .. in" the compiler will complain about + with ordinary "let ... in" the compiler will complain about unbound values. *) let rec is_even = function @@ -186,9 +192,9 @@ let my_lambda = fun x -> x * x ;; ~-. 3.4 (* Float only *) (* You can define your own operators or redefine existing ones. - Unlike SML or Haskell, only selected symbols can be used - for operator names and first symbol defines associativity - and precedence rules. *) + Unlike Standard ML or Haskell, only certain symbols can be + used for operator names and the operator's first symbol determines + its associativity and precedence rules. *) let (+) a b = a - b ;; (* Surprise maintenance programmers. *) (* More useful: a reciprocal operator for floats. @@ -223,6 +229,10 @@ List.filter (fun x -> x mod 2 = 0) [1; 2; 3; 4] ;; often referred to as "cons". *) 1 :: [2; 3] ;; (* Gives [1; 2; 3] *) +(* Remember that the cons :: constructor can only cons a single item to the front + of a list. To combine two lists use the append @ operator *) +[1; 2] @ [3; 4] ;; (* Gives [1; 2; 3; 4] *) + (* Arrays are enclosed in [| |] *) let my_array = [| 1; 2; 3 |] ;; @@ -258,7 +268,7 @@ let ocaml = (String.make 1 'O') ^ "Caml" ;; (* There is a printf function. *) Printf.printf "%d %s" 99 "bottles of beer" ;; -(* Unformatted read and write functions are there too. *) +(* There's also unformatted read and write functions. *) print_string "hello world\n" ;; print_endline "hello world" ;; let line = read_line () ;; @@ -291,15 +301,59 @@ let my_point = Point (2.0, 3.0) ;; type 'a list_of_lists = 'a list list ;; type int_list_list = int list_of_lists ;; +(* These features allow for useful optional types *) +type 'a option = Some of 'a | None ;; +let x = Some x ;; +let y = None ;; + (* Types can also be recursive. Like in this type analogous to - built-in list of integers. *) + a built-in list of integers. *) type my_int_list = EmptyList | IntList of int * my_int_list ;; let l = IntList (1, EmptyList) ;; +(* or Trees *) +type 'a tree = + | Empty + | Node of 'a tree * 'a * 'a tree + +let example_tree: int tree = + Node ( + Node (Empty, 7, Empty), + 5, + Node (Empty, 9, Empty) + ) +(* + 5 + / \ + 7 9 +*) + +(*** Records ***) + +(* A collection of values with named fields *) + +type animal = + { + name: string; + color: string; + legs: int; + } +;; + +let cow = + { name: "cow"; + color: "black and white"; + legs: 4; + } +;; +val cow : animal + +cow.name ;; +- : string = "cow" (*** Pattern matching ***) -(* Pattern matching is somewhat similar to switch statement in imperative +(* Pattern matching is somewhat similar to the switch statement in imperative languages, but offers a lot more expressive power. Even though it may look complicated, it really boils down to matching @@ -311,7 +365,7 @@ let l = IntList (1, EmptyList) ;; let is_zero x = match x with | 0 -> true - | _ -> false (* The "_" pattern means "anything else". *) + | _ -> false (* The "_" means "anything else". *) ;; (* Alternatively, you can use the "function" keyword. *) @@ -342,6 +396,19 @@ let say x = say (Cat "Fluffy") ;; (* "Fluffy says meow". *) +(* However, pattern matching must be exhaustive *) +type color = Red | Blue | Green ;; +let what_color x = + match x with + | Red -> "color is red" + | Blue -> "color is blue" + (* Won't compile! You have to add a _ case or a Green case + to ensure all possibilities are accounted for *) +;; +(* Also, the match statement checks each case in order. + So, if a _ case appears first, none of the + following cases will be reached! *) + (** Traversing data structures with pattern matching **) (* Recursive types can be traversed with pattern matching easily. @@ -369,9 +436,75 @@ let rec sum_int_list l = let t = Cons (1, Cons (2, Cons (3, Nil))) ;; sum_int_list t ;; +(* Heres a function to tell if a list is sorted *) +let rec is_sorted l = + match l with + | x :: y :: tail -> x <= y && is_sorted (y :: tail) + | _ -> true +;; + +is_sorted [1; 2; 3] ;; (* True *) +(* OCaml's powerful type inference guesses that l is of type int list + since the <= operator is used on elements of l *) + +(* And another to reverse a list *) +let rec rev (l: 'a list) : 'a list = + match l with + | [] -> [] + | x::tl -> (rev tl) @ [x] +;; + +rev [1; 2; 3] ;; (* Gives [3; 2; 1] *) +(* This function works on lists of any element type *) + +(*** Higher Order Functions ***) + +(* Functions are first class in OCaml *) + +let rec transform (f: 'a -> 'b) (l: 'a list) : 'b list = + match l with + | [] -> [] + | head :: tail -> (f head) :: transform f tail +;; + +transform (fun x -> x + 1) [1; 2; 3] ;; (* Gives [2; 3; 4] *) + +(** Lets combine everything we learned! **) +let rec filter (pred: 'a -> bool) (l: 'a list) : 'a list = + begin match l with + | [] -> [] + | x :: xs -> + let rest = filter pred xs in + if pred x then x :: rest else rest + end +;; + +filter (fun x -> x < 4) [3; 1; 4; 1; 5] ;; (* Gives [3; 1; 1]) *) + +(*** Mutability ***) + +(* Records and variables are immutable: you cannot change where a variable points to *) + +(* However, you can create mutable polymorphic fields *) +type counter = { mutable num : int } ;; + +let c = { num: 0 } ;; +c.num ;; (* Gives 0 *) +c.num <- 1 ;; (* <- operator can set mutable record fields *) +c.num ;; (* Gives 1 *) + +(* OCaml's standard library provides a ref type to make single field mutability easier *) +type 'a ref = { mutable contents : 'a } ;; +let counter = ref 0 ;; +!counter ;; (* ! operator returns x.contents *) +counter := !counter + 1 ;; (* := can be used to set contents *) ``` ## Further reading -* Visit the official website to get the compiler and read the docs: <http://ocaml.org/> -* Try interactive tutorials and a web-based interpreter by OCaml Pro: <http://try.ocamlpro.com/> +* Visit the official website to get the compiler and read the docs: [http://ocaml.org/](http://ocaml.org/) +* Quick tutorial on OCaml: [https://ocaml.org/docs/up-and-running](https://ocaml.org/docs/up-and-running) +* Complete online OCaml v5 playground: [https://ocaml.org/play](https://ocaml.org/play) +* An up-to-date (2022) book (with free online version) "Real World OCaml": [https://www.cambridge.org/core/books/real-world-ocaml-functional-programming-for-the-masses/052E4BCCB09D56A0FE875DD81B1ED571](https://www.cambridge.org/core/books/real-world-ocaml-functional-programming-for-the-masses/052E4BCCB09D56A0FE875DD81B1ED571) +* Online interactive textbook "OCaml Programming: Correct + Efficient + Beautiful" from Cornell University: [https://cs3110.github.io/textbook/cover.html](https://cs3110.github.io/textbook/cover.html) +* Try interactive tutorials and a web-based interpreter by OCaml Pro: [http://try.ocamlpro.com/](http://try.ocamlpro.com/) |