2014-09-10 17:17:03 +04:00
- - -
2014-09-10 19:35:30 +04:00
language : OCaml
2017-08-25 11:07:26 +03:00
filename : learnocaml . ml
2014-09-10 17:17:03 +04:00
contributors :
- [ " Daniil Baturin " , " http://baturin.org/ " ]
2023-06-30 15:00:21 +03:00
- [ " Stanislav Modrak " , " https://stanislav.gq/ " ]
2014-09-10 17:17:03 +04:00
- - -
OCaml is a strictly evaluated functional language with some imperative
features .
Along with StandardML and its dialects it belongs to ML language family .
2014-09-12 15:39:15 +04:00
F # is also heavily influenced by OCaml .
2014-09-12 16:46:41 +04:00
Just like StandardML , 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 .
2014-09-10 17:17:03 +04:00
It is strongly and statically typed , but instead of using manually written
2014-09-10 18:33:41 +04:00
type annotations , it infers types of expressions using Hindley - Milner algorithm .
2014-09-10 17:17:03 +04:00
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 .
2014-09-10 19:35:30 +04:00
2014-09-10 17:17:03 +04:00
` ` `
# let inc x = x + 1 ;;
val inc : int -> int = < fun >
# let a = 99 ;;
val a : int = 99
` ` `
For a source file you can use " ocamlc -i /path/to/file.ml " command
2014-09-12 16:34:50 +04:00
to print all names and type signatures .
2014-09-10 17:17:03 +04:00
` ` `
2015-10-08 06:11:24 +03:00
$ cat sigtest . ml
2014-09-10 17:17:03 +04:00
let inc x = x + 1
let add x y = x + y
2015-10-08 06:11:24 +03:00
let a = 1
2014-09-10 17:17:03 +04:00
2015-10-08 06:11:24 +03:00
$ ocamlc - i . / sigtest . ml
2014-09-10 17:17:03 +04:00
val inc : int -> int
val add : int -> int -> int
val a : int
` ` `
2014-09-10 19:35:30 +04:00
2014-09-10 17:17:03 +04:00
Note that type signatures of functions of multiple arguments are
2014-09-12 16:34:50 +04:00
written in curried 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 .
2014-09-12 21:36:15 +04:00
Hence the " int -> int -> int " signature .
2014-09-12 16:34:50 +04:00
2014-09-10 17:17:03 +04:00
` ` ` ocaml
2014-09-10 18:33:41 +04:00
(* * * Comments * * *)
2014-09-10 17:17:03 +04:00
(* Comments are enclosed in (* and *) . It's fine to nest comments. *)
2014-09-10 18:33:41 +04:00
(* There are no single-line comments. *)
2014-09-10 17:17:03 +04:00
2014-09-10 18:33:41 +04:00
(* * * Variables and functions * * *)
2014-09-10 17:17:03 +04:00
2014-09-10 18:33:41 +04:00
(* Expressions can be separated by a double semicolon symbol, ";;".
2014-09-10 17:17:03 +04:00
In many cases it's redundant , but in this tutorial we use it after
2014-09-11 18:40:15 +04:00
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 . * )
2014-09-10 17:17:03 +04:00
(* Variable and function declarations use "let" keyword. *)
let x = 10 ;;
2014-09-11 18:40:15 +04:00
(* OCaml allows single quote characters in identifiers.
Single quote doesn't have a special meaning in this case , it's often used
in cases when in other languages one would use names like " foo_tmp " . * )
let foo = 1 ;;
let foo' = foo * 2 ;;
2014-09-10 18:33:41 +04:00
(* Since OCaml compiler infers types automatically, you normally don't need to
2014-09-12 16:42:50 +04:00
specify argument types explicitly . However , you can do it if
you want or need to . * )
2014-09-12 21:46:46 +04:00
let inc_int ( x : int ) : int = x + 1 ;;
(* One of the cases when explicit type annotations may be needed is
resolving ambiguity between two record types that have fields with
the same name . The alternative is to encapsulate those types in
modules , but both topics are a bit out of scope of this
tutorial . * )
2014-09-10 17:17:03 +04:00
(* You need to mark recursive function definitions as such with "rec" keyword. *)
let rec factorial n =
if n = 0 then 1
2014-10-18 07:44:44 +04:00
else n * factorial ( n - 1 )
2014-09-10 17:17:03 +04:00
;;
2014-09-10 18:33:41 +04:00
(* Function application usually doesn't need parentheses around arguments *)
2014-09-10 17:17:03 +04:00
let fact_5 = factorial 5 ;;
2014-09-10 18:33:41 +04:00
(* ...unless the argument is an expression. *)
2014-09-10 17:17:03 +04:00
let fact_4 = factorial ( 5 - 1 ) ;;
let sqr2 = sqr ( - 2 ) ;;
(* Every function must have at least one argument.
2016-02-27 01:43:41 +03:00
Since some functions naturally don't take any arguments , there's
2014-09-10 17:17:03 +04:00
" 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. *)
print_hello () ;;
(* Calling a function with 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 *)
2014-09-10 18:33:41 +04:00
(* You can use multiple expressions in 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 . * )
let print_and_return x =
print_endline ( string_of_int x ) ;
x
;;
2014-09-10 17:17:03 +04:00
(* 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 . * )
(* Definitions can be chained with "let ... in" construct.
This is roughly the same to assigning values to multiple
variables before using them in expressions in imperative
languages . * )
let x = 10 in
let y = 20 in
x + y ;;
2014-09-10 18:33:41 +04:00
(* Alternatively you can use "let ... and ... in" construct.
2014-09-10 17:17:03 +04:00
This is especially useful for mutually recursive functions ,
with ordinary " let .. in " the compiler will complain about
2015-02-12 11:54:26 +03:00
unbound values . * )
let rec
is_even = function
| 0 -> true
| n -> is_odd ( n - 1 )
and
is_odd = function
| 0 -> false
| n -> is_even ( n - 1 )
;;
2014-09-10 17:17:03 +04:00
2014-09-12 21:54:23 +04:00
(* Anonymous functions use the following syntax: *)
let my_lambda = fun x -> x * x ;;
2014-09-10 17:17:03 +04:00
2014-09-10 18:33:41 +04:00
(* * * Operators * * *)
2014-09-10 17:17:03 +04:00
2019-01-10 04:06:14 +03:00
(* There is little distinction between operators and functions.
2014-09-10 17:17:03 +04:00
Every operator can be called as a function . * )
( + ) 3 4 (* Same as 3 + 4 *)
2014-09-10 18:33:41 +04:00
(* There's a number of built-in operators. One unusual feature is
2014-09-10 17:17:03 +04:00
that OCaml doesn't just refrain from any implicit conversions
between integers and floats , it also uses different operators
for floats . * )
2014-09-10 18:33:41 +04:00
12 + 3 ;; (* Integer addition. *)
12 . 0 + . 3 . 0 ;; (* Floating point addition. *)
2014-09-10 17:17:03 +04:00
2014-09-10 18:33:41 +04:00
12 / 3 ;; (* Integer division. *)
12 . 0 /. 3 . 0 ;; (* Floating point division. *)
5 mod 2 ;; (* Remainder. *)
2014-09-10 17:17:03 +04:00
(* Unary minus is a notable exception, it's polymorphic.
However , it also has " pure " integer and float forms . * )
- 3 ;; (* Polymorphic, integer *)
2014-09-10 18:33:41 +04:00
- 4 . 5 ;; (* Polymorphic, float *)
2014-09-10 17:17:03 +04:00
~ - 3 (* Integer only *)
~ - 3 . 4 (* Type error *)
~ -. 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
2014-09-10 18:33:41 +04:00
and precedence rules . * )
let ( + ) a b = a - b ;; (* Surprise maintenance programmers. *)
2014-09-10 17:17:03 +04:00
(* More useful: a reciprocal operator for floats.
2014-09-10 18:33:41 +04:00
Unary operators must start with " ~ " . * )
2014-09-10 17:17:03 +04:00
let ( ~ / ) x = 1 . 0 /. x ;;
~ / 4 . 0 (* = 0.25 *)
2015-10-17 15:06:28 +03:00
(* * * Built-in data structures * * *)
2014-09-10 17:17:03 +04:00
(* Lists are enclosed in square brackets, items are separated by
semicolons . * )
2022-01-03 19:18:53 +03:00
let my_list = [ 1 ; 2 ; 3 ] ;; (* Has type "int list". *)
2014-09-10 17:17:03 +04:00
2014-09-10 18:33:41 +04:00
(* Tuples are ( optionally ) enclosed in parentheses, items are separated
by commas . * )
let first_tuple = 3 , 4 ;; (* Has type "int * int". *)
2014-09-10 17:17:03 +04:00
let second_tuple = ( 4 , 5 ) ;;
(* Corollary: if you try to separate list items by commas, you get a list
with a tuple inside , probably not what you want . * )
let bad_list = [ 1 , 2 ] ;; (* Becomes [ ( 1, 2 ) ] *)
2014-09-10 18:33:41 +04:00
(* You can access individual list items with the List.nth function. *)
2014-09-10 17:17:03 +04:00
List . nth my_list 1 ;;
2014-09-12 21:54:23 +04:00
(* There are higher-order functions for lists such as map and filter. *)
List . map ( fun x -> x * 2 ) [ 1 ; 2 ; 3 ] ;;
2016-03-26 15:27:56 +03:00
List . filter ( fun x -> x mod 2 = 0 ) [ 1 ; 2 ; 3 ; 4 ] ;;
2014-09-12 21:54:23 +04:00
2014-09-10 18:33:41 +04:00
(* You can add an item to the beginning of a list with the "::" constructor
2014-09-10 17:17:03 +04:00
often referred to as " cons " . * )
1 :: [ 2 ; 3 ] ;; (* Gives [1; 2; 3] *)
(* Arrays are enclosed in [| |] *)
let my_array = [| 1 ; 2 ; 3 |] ;;
(* You can access array items like this: *)
my_array . ( 0 ) ;;
2014-09-11 18:22:02 +04:00
(* * * Strings and characters * * *)
(* Use double quotes for string literals. *)
let my_str = " Hello world " ;;
(* Use single quotes for character literals. *)
let my_char = 'a' ;;
(* Single and double quotes are not interchangeable. *)
let bad_str = ' syntax error' ;; (* Syntax error. *)
(* This will give you a single character string, not a character. *)
let single_char_str = " w " ;;
(* Strings can be concatenated with the "^" operator. *)
let some_str = " hello " ^ " world " ;;
(* Strings are not arrays of characters.
You can't mix characters and strings in expressions .
You can convert a character to a string with " String.make 1 my_char " .
There are more convenient functions for this purpose in additional
libraries such as Core . Std that may not be installed and / or loaded
by default . * )
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. *)
print_string " hello world \n " ;;
print_endline " hello world " ;;
let line = read_line () ;;
2014-09-10 17:17:03 +04:00
2014-09-10 18:33:41 +04:00
(* * * User-defined data types * * *)
2014-09-10 17:17:03 +04:00
2014-09-10 18:33:41 +04:00
(* You can define types with the "type some_type =" construct. Like in this
2014-09-10 17:17:03 +04:00
useless type alias : * )
type my_int = int ;;
(* More interesting types include so called type constructors.
Constructors must start with a capital letter . * )
type ml = OCaml | StandardML ;;
2014-09-10 18:33:41 +04:00
let lang = OCaml ;; (* Has type "ml". *)
2014-09-10 17:17:03 +04:00
(* Type constructors don't need to be empty. *)
type my_number = PlusInfinity | MinusInfinity | Real of float ;;
2014-09-10 18:33:41 +04:00
let r0 = Real ( - 3 . 4 ) ;; (* Has type "my_number". *)
2014-09-10 17:17:03 +04:00
(* Can be used to implement polymorphic arithmetics. *)
type number = Int of int | Float of float ;;
(* Point on a plane, essentially a type-constrained tuple *)
type point2d = Point of float * float ;;
let my_point = Point ( 2 . 0 , 3 . 0 ) ;;
2014-09-10 18:40:47 +04:00
(* Types can be parameterized, like in this type for "list of lists
2014-09-10 17:17:03 +04:00
of anything " . 'a can be substituted with any type. *)
type ' a list_of_lists = ' a list list ;;
type int_list_list = int list_of_lists ;;
2014-09-10 18:33:41 +04:00
(* Types can also be recursive. Like in this type analogous to
2014-09-10 17:17:03 +04:00
built - in list of integers . * )
type my_int_list = EmptyList | IntList of int * my_int_list ;;
2015-02-12 12:02:16 +03:00
let l = IntList ( 1 , EmptyList ) ;;
2014-09-10 17:17:03 +04:00
2014-09-10 18:33:41 +04:00
(* * * Pattern matching * * *)
2014-09-10 17:17:03 +04:00
(* Pattern matching is somewhat similar to switch statement in imperative
languages , but offers a lot more expressive power .
2015-10-08 06:11:24 +03:00
Even though it may look complicated , it really boils down to matching
2014-09-12 16:42:50 +04:00
an argument against an exact value , a predicate , or a type constructor .
The type system is what makes it so powerful . * )
2014-09-10 17:17:03 +04:00
2014-09-10 18:33:41 +04:00
(* * Matching exact values. * *)
2014-09-10 17:17:03 +04:00
let is_zero x =
match x with
| 0 -> true
2014-09-10 18:33:41 +04:00
| _ -> false (* The "_" pattern means "anything else". *)
2014-09-10 17:17:03 +04:00
;;
2014-09-10 18:33:41 +04:00
(* Alternatively, you can use the "function" keyword. *)
2014-09-12 16:35:56 +04:00
let is_one = function
2014-09-10 17:17:03 +04:00
| 1 -> true
| _ -> false
;;
2014-09-10 18:33:41 +04:00
(* Matching predicates, aka "guarded pattern matching". *)
2015-10-08 06:11:24 +03:00
let abs x =
2014-09-10 17:17:03 +04:00
match x with
| x when x < 0 -> - x
| _ -> x
;;
abs 5 ;; (* 5 *)
abs ( - 5 ) (* 5 again *)
2014-09-10 18:33:41 +04:00
(* * Matching type constructors * *)
2014-09-10 17:17:03 +04:00
type animal = Dog of string | Cat of string ;;
let say x =
match x with
| Dog x -> x ^ " says woof "
| Cat x -> x ^ " says meow "
;;
2014-09-10 18:33:41 +04:00
say ( Cat " Fluffy " ) ;; (* "Fluffy says meow". *)
2014-09-10 17:17:03 +04:00
2015-10-17 15:06:28 +03:00
(* * Traversing data structures with pattern matching * *)
2014-09-10 17:17:03 +04:00
(* Recursive types can be traversed with pattern matching easily.
2015-10-17 15:06:28 +03:00
Let's see how we can traverse a data structure of the built - in list type .
2014-09-12 16:42:50 +04:00
Even though the built - in cons ( " :: " ) looks like an infix operator ,
it's actually a type constructor and can be matched like any other . * )
2014-09-10 17:17:03 +04:00
let rec sum_list l =
match l with
| [] -> 0
| head :: tail -> head + ( sum_list tail )
;;
2014-09-10 18:33:41 +04:00
sum_list [ 1 ; 2 ; 3 ] ;; (* Evaluates to 6 *)
2014-09-10 17:17:03 +04:00
2014-09-10 18:33:41 +04:00
(* Built-in syntax for cons obscures the structure a bit, so we'll make
2014-09-10 17:17:03 +04:00
our own list for demonstration . * )
type int_list = Nil | Cons of int * int_list ;;
let rec sum_int_list l =
match l with
| Nil -> 0
| Cons ( head , tail ) -> head + ( sum_int_list tail )
;;
let t = Cons ( 1 , Cons ( 2 , Cons ( 3 , Nil ) ) ) ;;
sum_int_list t ;;
` ` `
# # Further reading
2014-09-10 19:37:17 +04:00
* Visit the official website to get the compiler and read the docs : < http : // ocaml . org />
2023-06-30 15:00:21 +03:00
* Quick tutorial on OCaml : < https : // ocaml . org / docs / up - and - running >
* Complete online OCaml v5 playground : < 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 >
* Online interactive textbook " OCaml Programming: Correct + Efficient + Beautiful " from Cornell University : < https : // cs3110 . github . io / textbook / cover . html >
2014-09-10 19:37:17 +04:00
* Try interactive tutorials and a web - based interpreter by OCaml Pro : < http : // try . ocamlpro . com />