[OCaml MOOC] week1: BASIC TYPES, DEFINITIONS AND FUNCTIONS

1. BASIC DATA TYPES: int, bool

Rich type system and polymorphism in ocaml. Types are infered not declared.

Basic types: int, bool, float, string, char, ...

int

value: $-2^{62}$ ~ $2^{62}-1$ on 64-bit machines.
ops: +, -, *, /, mod (reminder: / is integer division)

# 3+2*4-1;; 
- : int = 10 
# 5/2;; 
- : int = 2 
# -5 mod 3;; 
- : int = -2 

bool

values: true, false ops: &&,||, not comparison ops: <, >, =, <=, =>, <>
note 1: comparisons must have the same type of operands) note 2: equal test is = , = is NOT assigment. == exists but is for something else...

# true && (1<7);; 
- : bool = true 
# 1.0 < 7;; 
Error: This expression has type int but an expression was expected of type 
         float 
# 5 > "hello";; 
Error: This expression has type string but an expression was expected of type 
         int 
# 3=3;; 
- : bool = true 
# 3==3;; 
- : bool = true 
# 3<3.0;; 
Error: This expression has type float but an expression was expected of type 
         int 
# 3.0 < 3.0;; 
- : bool = false 

2. MORE DATA TYPES: float, char, string

float

value: must written with a dot (5.0, 5.), or exponential (5e3, 6e-9) ops: also have a dot in the end +., -., *., /. functions: sqrt, sin, cos, ceil, floor, ...

# 3.0 +. 1e10;; 
- : float = 10000000003. 
# 2 + 3.0;; 
Error: This expression has type float but an expression was expected of type 
         int 
# 3. +. 2.0;; 
- : float = 5. 

All basic types are disjoint: no value belongs to 2 different basic types, **no implicit conversion. **

conversion functions in both directions:

  • float_of_int: int -> float
  • int_of_float: float -> int
# float_of_int(2);; 
- : float = 2. 
# int_of_float(2.4);; 
- : int = 2 
# float_of_int 2;; 
- : float = 2. 
# int_of_float 2.8;; 
- : int = 2 

Parentheses only necessary to indicate structure.

char

values: 256 chars (0~255), can be written as 'a' or '\087' conversion functions (char ↔ int):

  • Char.chr: int ->char
  • Char.code: char -> int
# Char.code 'A';; 
- : int = 65 
# Char.code '\122';; 
- : int = 122 
# Char.chr 233;; 
- : char = '\233' 
# Char.chr 65;; 
- : char = 'A' 
# Char.chr (Char.code 'B');; 
- : char = 'B' 

string

values: written between "" ops: ^ for string concatenation functions: String.length, int_of_string, float_of_string, String.get(s,i) ...

# "abc" ^ "def";; 
- : string = "abcdef" 
# String.length "1234";; 
- : int = 4 
# int_of_string "123";; 
- : int = 123 
# int_of_string "123a";; 
Exception: Failure "int_of_string". 
# string_of_int 123;; 
- : string = "123" 
# String.get "abc" 1;; 
- : char = 'b' 

3. EXPRESSIONS: if-then-else, func-applications and operators

expressions are used to compute values.

conditional expr

if... then... else... ⇒ this gives an expression, not an instruction.

An expression always have a type, type of the if-then-else expression is the type of the expressions in then and else, which must be the same.

If the else is missing → there is a default value, to be discussed later

# if 1<2 then 8 else 9;; 
- : int = 8 
# if 6=8 then 1 else 8;; 
- : int = 8 
# if 6=8 then 1 else 8.;;        
Error: This expression has type float but an expression was expected of type 
         int 
# if(if 1=1 then 2=2 else 3>2) then 2<3 else 3<2;; 
- : bool = true 

function application

again, () are not needed unless indicate structure, and parentheses are NOT function applications...

# String.get;;  
- : string -> int -> char = <fun> 
# String.get "abcde" 2;; 
- : char = 'c' 
# String.get ("Hello"^"world") (3+2);; 
- : char = 'w' 
# String.get (string_of_int 65) (int_of_string "0");; 
- : char = '6' 
# String.get("hello", 2);; 
Error: This expression has type 'a * 'b 
       but an expression was expected of type string 

Polymorphic operators

Some ops have polymorphic(generic) types, like >: 'a → 'a → bool Here the 'a is a type variable, can be instantiated by any type.

# 12 > 23.0;; 
Error: This expression has type float but an expression was expected of type 
         int 
# 'a' > 'b';; 
- : bool = false 
# "hello" > "world";; 
- : bool = false 

4. DEFINITIONS

definitions are used to give names to values, in ocaml there are global defs and local defs.

global definition

effective for rest of session syntax: let name = expr once set, the value of the identifier never changes.

# let x = 2+3;; 
val x : int = 5 
# let y = 2*x;; 
val y : int = 10 
# let x = 3;; # **the old definition of x will be shadowed** 
val x : int = 3 
# x = x+1;; 
- : bool = false 

local definition

naming within a delimited scope syntax: let name = expr1 in expr2 the scope of the identifier is expr2, the result's value is also expr2's value. So the identifier should appear in expr2.

# let x = 9 in 2*x;; 
- : int = 18 
# x;; # x is not in global scope  
Error: Unbound value x 
# let y = x+1 in y/3;; 
Error: Unbound value x 
# let x = 17;; # now x is global 
val x : int = 17 
# let y = x+1 in y/3;;           
- : int = 6 

local defs can be nested:

# let x = 4 in  
  let y = x+1 in  
  let x = 2*y in x;; 
Error: Syntax error 
# let x = 4 in                    
  let y = x+1 in 
  let x=2*y in x;; 
- : int = 10 
# let x = 4 in  
  (let x=17 in x+1) + x;; 
- : int = 22 

Simultaneous Definitions

can assign 2 identifiers in the same line:

    # let x1=2 and x2=3-3;; 
    val x1 : int = 2 
    val x2 : int = 0 

Note: expr is evaluated w.r.t. the value bindings before the let.

# let x=2 in 
     let y=x+1 in (*y=2+1*) 
     x*y;; 
- : int = 6 
# let x=1;; 
val x : int = 1 
# let x=2 and y=x+1 in (*y=1+1 because the binding for x before the let is 1!*) 
    x*y;; 
- : int = 4 

5. FUNCTIONS

func definition

global function (with 1 argument) def : let f x = expr

local def: let f x = expr1 in expr2

# let f x = x+1;; 
val f : int -> int = <fun> 
# f 17;; 
- : int = 18 
# let g y = 2*y 
  in g 42;; 
- : int = 84 
# f f 1;; (* interpreted as apply 2 argments to f, need parentheses here*) 
Error: This function has type int -> int 
       It is applied to too many arguments; maybe you forgot a `;'. 
# f (f 1);; 
- : int = 3 

lexical scoping

def. lexical scoping identifiers(names) used in function body refers to the names *that *are visible at the moment of function definition.

def. dynamic scoping names refer to names that are visible at the moment of function invocation.

# let f x = x+1 in 
  let g y = f (f y) in (*here f is the f defined above, which is visible at the moment of g's declaration*) 
  let f x = 2*2 in (*this f is visible after g's declaration, it is visible when g is invocated*) 
  g 5;; (*g uses the first f*) 
Warning 26: unused variable f. 
- : int = 7 
(*the same is true for global scoping*) 
# let f x = x+1;; 
val f : int -> int = <fun> 
# let g y = f (f y);; 
val g : int -> int = <fun> 
# let f x = 2*x;; 
val f : int -> int = <fun> 
# g 5;; 
- : int = 7 

identifiers are NOT variables

A name(identifier) can be hidden (the value is NOT changed) by a new definition of the same name.

The hidden identifier can be accessed with static(lexical) binding:

# let a = 1;; 
val a : int = 1 
# let f x = x+a;; 
val f : int -> int = <fun> 
# f 2 ;; 
- : int = 3 
# let a = 73;; 
val a : int = 73 
# f 2;; (*f still uses a's value as 2, the old value of a is NOT changed, it's hidden --closure? *) 
- : int = 3 

6. RECURSION

rec functions are natural on recursively defined data structures.

example: fact(n)

recursive definition

using the f in let's expr will cause pb as f refers to the previous value of f. ⇒ use key word rec.

# let fact n = if n<=1 then 1 else n*fact(n-1);; 
Error: Unbound value fact 
# let rec fact n = if n<=1 then 1 else n*fact(n-1);; 
val fact : int -> int = <fun> 
# fact 10;; 
- : int = 3628800 

mutually recursive function

ex. 2 funcs calling each other recursively

⇒ use simultaneous defs (let...and...):

# let rec even x = if x=0 then true else odd(x-1) 
  and odd x = if x=0 then false else even(x-1);; 
val even : int -> bool = <fun> 
val odd : int -> bool = <fun> 
# even 17;;  
- : bool = false 
# odd 10;; 
- : bool = false 

exercices

let multiple_of n d =  
  (n mod d) == 0;; 

let integer_square_root n = 
  let sqr = sqrt (float_of_int n) in 
  int_of_float sqr; 

let rec gcd n m =  
  let big = max n m and small = min n m in  
  let r = big mod small in  
  if r=0 then small  
  else gcd r small;; 

let rec multiple_upto n r = 
  if r<2 then false  
  else if multiple_of n r then true 
  else multiple_upto n (r-1);; 

let is_prime n = 
  if n<=1 then false 
  else not (multiple_upto n (n-1));; 
comments powered by Disqus