Real World OCaml

ISBN 978-7-5123-7637-3

English First Edition PDF

Code Examples

Install Guidence from cornell/cs3110

Part I

based on Core instead of standard library

1 / 3;;
(* - : int = 0 *)

1 / 3.;;
1. /. 3;; 
2. / 3.;;
(* Error: This expression has type float but an expression was expected of type int *)

1. /. 3.;;
(* - : float = 0.333333333333333315 *)

let even x =
  x mod 2 = 0 ;;
(* val even : int -> bool = <fun> *)

Identifiers for variable names:
punctuation is excluded, except for _ and ', must start with a lowercase letter or an underscore.
By default, utop doesn’t print out variables starting with an underscore.

first-class functions

Module names
always start with a capital letter.

type inference

let sum_if_true test first second =
  (if test first then first else 0)
  + (if test second then second else 0)
;;
(* val sum_if_true : (int -> bool) -> int -> int -> int = <fun> *)

let sum_if_true (test : int -> bool) (x : int) (y : int) : int =
  (if test x then x else 0)
  + (if test y then y else 0)
;;

parametric polymorphism

generic type & type variable

let first_if_true test x y =
  if test x then x else y
;;
(* val first_if_true : ('a -> bool) -> 'a -> 'a -> 'a = <fun> *)

* compile time error & runtime exception

built-in simple types:
int, float, bool, char, string, unit

pattern matching

let t = (3, "four", 5.);;
(* val t : int * string * float = (3, "four", 5.) *)
let (x, y, z) = t;;

* tuple: fixed item number, different types

* list: any item number, the same type

~f: labeled argument

let langs = ["ocaml", "cpp", "c"];;
(* a singleton list containing a three-tuple
 * val langs : (string * string * string) list = [("ocaml", "cpp", "c")]
 *)

let langs = ["ocaml"; "cpp"; "c"];;
(* val langs : string list = ["ocaml"; "cpp"; "c"] *)
List.map langs ~f:String.length;;
(* - : int list = [5; 3; 1] *)

"python" :: langs;;
(* - : string list = ["python"; "ocaml"; "cpp"; "c"] *)
langs;;
(* - : string list = ["ocaml"; "cpp"; "c"] *)

["py"; "perl"] @ langs;;
(* - : string list = ["py"; "perl"; "ocaml"; "cpp"; "c"] *)

The bracket notation for lists is really just syntactic sugar for :: (right-associative).

Unlike ::, @ is not a constant-time operation (proportional to the length of the first list).

let my_fav_lang (my_fav :: the_rest) = my_fav;;
(* Here is an example of a case that is not matched: [] *)

let my_fav_lang langs =
  match langs with
  | first :: the_rest -> first
  | [] -> "ocaml"
;;

recursive

let rec sum l =
  match l with
  | [] -> 0
  | hd :: tl -> hd + sum tl
;;

option

let divide x y =
  if y = 0 then None else Some (x/y) ;;
(* val divide : int -> int -> int option = <fun> *)

A let paired with an in can be used to introduce a new binding within any local scope, including a function body.
The in marks the beginning of the scope within which the new variable can be used.

let x = 7 in
  let y = x * x in
    x + y
;;

record

type point2d = {x : float; y : float};;
let p = {x = 3.; y = -4.};;
(* val p : point2d = {x = 3.; y = -4.} *)

variant

type v = 
  | Int of int
  | Point2D of point2d
;;
(* type v = Int of int | Point2D of point2d *)

array

let numbers = [| 1; 2; 3; 4 |];;
(* val numbers : int array = [|1; 2; 3; 4|] *)

numbers.(2) <- 4;;
(* - : unit = () *)

The unit type has only one possible value, written (), generally used as a placeholder.
We use unit for the return value of an operation like setting a mutable field that communicates by side effect rather than by returning a value. It’s also used as the argument to functions that don’t require an input value (like void in C or Java).

mutable record

type running_sum =
{
  mutable sum : float;
  mutable samples : int;
};;

let mean rsum = rsum.sum /. float rsum.samples
let create () = {sum = 0.; samples = 0}
let update rsum x =
  rsum.samples <- rsum.samples + 1;
  rsum.sum <- rsum.sum +. x
;;
(* val mean : running_sum -> float = <fun>
 * val create : unit -> running_sum = <fun>
 * val update : running_sum -> float -> unit = <fun>
 *)

let rsum = create ();;
List.iter [1.;3.;2.;-7.;4.;5.] ~f:(fun x -> update rsum x);;
mean rsum;;

Anonymous functions
are declared using the fun keyword, and don’t need to be ex‐ plicitly named.

ref:
the standard way of simulating the traditional mutable variables

let x = {contents = 0};;
x.contents <- x.contents + 1;;
(* equivalent to *)
let x = ref 0;;
x := !x + 1;;
(* implementations *)
type 'a ref = {mutable contents : 'a}

let ref x = {contents = x}
let (!) r = r.contents
let (:=) r x = r.contents + x
;;

The parentheses around ! and := are needed because these are operators, rather than ordinary functions.

let sum list =
  let sum = ref 0 in
    List.iter list ~f:(fun x -> sum := !sum + x);
  !sum
;;

for & while

out of toplevel

(* sum.ml *)
open Core

let rec read_and_accumulate accum =
  let line = In_channel.input_line In_channel.stdin in
  match line with
  | None -> accum
  | Some x -> read_and_accumulate (accum +. Float.of_string x)

let () =
  printf "Total: %F\n" (read_and_accumulate 0.)
corebuild sum.ml

Part II

anonymous & higer-order function

let increments = [ (fun x -> x + 1); (fun x -> x + 2) ];;
(* val increments : (int -> int) list = [<fun>; <fun>] *)

List.map ~f:(fun g -> g 5) increments;;
(* - : int list = [6; 7] *)

currying

let abs_diff x y = abs (x - y);;
(* equivalent to *)
let abs_diff =
  (fun x -> (fun y -> abs (x - y)));;

(* partial application *)
let dist_from_3 = abs_diff 3;;

or pattern

| [] | [_]

Explicitly marking of recursion doesn’t apply to a pure language like Haskell.

Predetermined identifiers characters set and strings:
! $ % & * + - . / : < = > ? @ ^ | ~
or mod lsl (logical shift left)

(+) 1 2;;
(* - : int = 3 *)

Int.max 3 -4;; (* error *)
Int.max 3 (-4);;

let (|>) x f = f x;;
(* val ( |> ) : 'a -> ('a -> 'b) -> 'b = <fun> *)

Negation has lower precedence than function application.

function keyword

let some_or_zero function
  | Some x -> x
  | None -> 0
;;

__labeled argument __

let ratio ~num ~denom = float num /. float denom;;
(* val ratio : num:int -> denom:int -> float = <fun> *)

ratio ~denom:10 ~num:3;;

Label punning:
you get to drop the text after the : if the name of the label and the name of the variable being used are the same.

TODO

  1. files
  2. modules
  3. functors
  4. objects
  5. classes

Part III

TODO

  1. s-expression
  2. concurrency
  3. FFI
  4. GC
  5. compiler frontend
posted @ 2019-04-09 21:23  Elaphurus  阅读(312)  评论(1编辑  收藏  举报