Basic OCaml Notes
Compilation
xxx.ml -> ocamlbuild xxx.native -> ./xxx.native
Function
Calling functions
No brackets and no comma between the arguments
( “hello”, 3 )is type of (’a * ‘ b)
Define functions
let average a b =
(a+. b) /. 2.0 ;;
(*val average: float -> float -> float = <fun>*)
- OCaml is a strongly statically typed languageå
- OCaml uses type inference to work out the types, so you don’t have to.
- OCaml doesn’t do any implicit casting. If you want a float, you have to write
2.0because2is an integer. No automatic conversion - As a side-effect of type inference in OCaml, functions (including operators) can’t have overloaded definitions. OCaml defines
+as the integer addition function. To add floats, use+.(note the trailing period). Similarly, use-.,*.,/.for other float operations - OCaml doesn’t have a
returnkeyword — the last expression in a function becomes the result of the function automatically.
Recursive Functions
let rect - please explicitly say
let rec range a b =
if a > b then []
else a :: range (a+1) b
(*val range: int -> int -> int list = fun*)
Type
Int 31-bit signed int - 32 processors / 63-bit signed int - 64 processors
use 1 bit to automatically manage the memory use (garbage collection)
no unsigned interger type - using nativeint
Float IEEE double-precison (same as C)
Bool true or false
Char 8-bit char (dont support Unicode or UTF-8) Serious flaw
Unit written as ( ) (same as void in C)
Never implicit casts - 1+2.5 is a type error - + operator requires two ints as arguments.
(float_of_int i) +. f - shorter alias: float i +. f
Implicit vs. explicit casting? which is better?
- Type reference is such a wondeful time-saving feature
- errors caused by implicit casts are hard to find
- Some casts are expensive operations (i.e. int <-> float)->
Types of Functions
Because of type inference you will rarely if ever need to explicitly write down the type of functions.
currying: f: arg1 -> arg2 -> ... -> argn -> returnType
If return nothing (void): output_char: out_channel -> char -> unit
polymorphic functions take anything as an argument
let give_me_a_three x = 3
(*give_me_a_three: 'a -> int*)
'a means any type
Type Inference
Dont need to declare the types of functions and variables.
OCaml figures them out && checks all types match up (even across different files!)
Structure of OCaml
Local “Variables”
let average a b =
let sum = a +. b in
sum /. 2.0
(*val average: float -> float -> float = <fun>*)
let name = expression in define a named local expression, and name can then be used later on in the function instead of expression, till a ;;
This sum is just a shorthand name for the expression a +.b, you can’t assign it later or change its value in any way.
Global “Variables”
Define at the top level - still a shorthand name for things
Let-bindings
Reference: a real variable. (It is better to create a reference and name it)
let my_ref = ref 0
(*val my_ref: int ref = {contents = 0}*)
my_ref := 100;;
(* unit = ()*)
!my_ref;;
(*int = 100*)
:= is used to assign, ! dereferences
Nested Functions
let read_whole_channel chan =
let buf = Buffer.create 4096 in
let rec loop () =
let newline = input_line chan in
Buffer.add_string buf newline;
Buffer.add_char buf '\n';
loop ()
in
try
loop ()
with
End_of_file -> Buffer.contents buf;;
val read_whole_channel : in_channel -> string = <fun>
let name arguments = function-definition in
Match
match foo with ...
The following code is perfectly legal and all do the same thing
let f x b y = if b then x+y else x+0
let f x b y = x + (if b then y else 0)
let f x b y = x + (match b with true -> y | false -> 0)
let f x b y = x + (let g z = function true -> z | false -> 0 in g y b)
let f = fun x -> fun b -> fun y -> if b then x+y else x+0
let f x b y = x + (let _ = y + 3 in (); if b then y else 0);;
(* val f : int -> bool -> int -> int = <fun> *)