HEY!There’s OCaml In My Rust!
Kel Cecil
@praisechaos
I ❤ Languages
Incredible
Shrinking
Operating System
Steve Jones
DevOps Days 2015
OCaml is pretty cool!
It's a functional programming language...
# let double x = x * 2 in
List.map double [ 1; 2; 3 ];;
- : int list = [2; 4; 6]
• OCaml is an almost pure functional programming
language by default.
• Functions are first-class citizens.
... and an imperative programming language...
let update_collectable_value order new_value =
order.old_value <- order.current_value;
order.current_value <- new_value;
order
• It can be convenient to be able to code in the
imperative style.
• OCaml includes imperative constructs like for and
while loops.
... and an object-oriented language!
class ['a] queue init = object
val mutable q: 'a list = init
method enqueue item =
q <- q @ item
end;;
• OCaml can also be object-oriented if needed.
OCaml powers some pretty sweet stuff.
• MirageOS: Unikernel tech recently acquired by
Docker
• Flow: A static type checker for JavaScript
• Hack: A programming languages for Facebook's
HHVM
• and...
Rust's first compiler!
• rustboot was written in OCaml
• Replaced for Rust 0.7
Rust is pretty excellent too!
• Guaranteed Memory Safety
• Threads without data races
• Incredibly fast
• Crazy awesome potential
Rust was inspired by OCaml
in a few interesting ways!
Let's start with a simple, imperative OCaml program
• Define a type named collectable_value that olds the
previous value and current value of an item.
• Provide a function to update the current value and
update the old value.
• Have a "main" function to create the record and
update the value.
Starting with our type
type collectable_value = {
mutable old_value: float;
mutable current_value: float;
}
• A record is a collection of values stored together as a
data type.
• Values are immutable by default but can be marked
as mutable.
Adding our first function
let update_collectable_value collectable new_value =
collectable.old_value <- collectable.current_value;
collectable.current_value <- new_value;
collectable
• Define a function with a let-binding.
• Accepts a collectablevalue record (collectable) and a
float (newvalue).
• Updates and returns the record.
Adding a "main"
let _ =
let pinball_machine = {
old_value = 0.0;
current_value = 800.00;
} in
update_collectable_value pinball_machine 900.0
• Underscore discards the return value.
• This let-binding allows us to execute code at the top
level.
Anything seem a little Rust-y
to you?
type collectable_value = {
mutable old_value: float;
mutable current_value: float;
}
let update_collectable_value order new_value =
order.old_value <- order.current_value;
order.current_value <- new_value;
order
let _ =
let pinball_machine = {
old_value = 0.0;
current_value = 800.00;
} in
update_collectable_value pinball_machine 900.0
Semicolon Separation in OCaml
let update_collectable_value collectable new_value =
collectable.old_value <- collectable.current_value;
collectable.current_value <- new_value;
collectable
• Notice the semi-colons separating the expressions?
• The last expression is the return value for the
function.
• Look familiar?
Statements in Rust
fn greeting() -> String {
let greeting = "Hello, there!";
String::from(greeting) // Return value from fn
}
• Rust code is mostly statements.
• Most common statements are
• declaring a variable
• using an expression with a semi-colon
• Last expression omitting the semi-colon is returned.
What is Type
Inference?
• Automatic deduction of the data type of an
expression.
Rust's Type Inference
// Rust knows that name is a String.
let name = "Kel!"
• Rust's type inference is loosely based on the
Hindley-Milner algorithm4
.
• Extensions were added to use accommodate
subtyping.
4
https://github.com/rust-lang/rust/blob/master/src/librustc/infer/README.md
OCaml's Type Inference
(* OCaml also knows this is a string. *)
let name = "Kel!"
• ML originally used the Hindley-Milner algorithm.
• OCaml's implementation works through solving
constraints.
• Tons of research hours poured into their
algorithm(s)5
5
https://www.cis.upenn.edu/~sweirich/icfp-plmw15/slides/pottier.pdf
Workshop: Binary Search Tree
• Follow along in your IDEs!
• Learn about binary search trees here:
• https://en.wikipedia.org/wiki/Binary_search_tree
• Check out the examples
• https://github.com/kelcecil/rust-ocaml-workshop
Building our Tree Structure
OCaml, then Rust.
Tree in OCaml
type tree;;
• Types in OCaml are defined with the type keyword.
Tree in OCaml
type tree =
Empty
;;
• We need to represent a node that does not exist.
• We'll represent this as Empty.
Tree in OCaml
type tree =
| Empty
| Node
;;
• We also want to represent a node that exists.
• We'll represent this as node.
Tree in OCaml
type 'a tree =
| Empty
| Node of 'a * 'a tree * 'a tree
;;
• Our node must have a key, a left child, and a right
child.
• Node now contains generic 'a that is our key.
• We also have two values of tree presenting our left
and right leaves.
Algebraic Data Types
• Composite type formed from other types
• Common types of Algebraic Types
• Product Types
• Sum Types
Product Types
• Compound types in a structure
• Operands of the product are types
• Structure is determined by fixed order of operands
(* Record *)
type person = {name: string; age: int};;
let kel = {name = "Kel"; age = 30};;
(* Tuple *)
let next_obsession = ("Dead Rising 4", 59.99);;
Tuples and records are product types in OCaml
let my_love = ("Rust", 9001)
• Tuples are product types in Rust
• There's also the unit () type
• Tuple with no operands.
Sum Types
type 'a tree =
| Empty
| Node of 'a * 'a tree * 'a tree
;;
• Multiple classes of values called variants.
• Each class has it's own constructor.
• Class represents it's own concept
Tree in Rust
enum Tree {
Empty
}
• Algebraic data types are enums in Rust.
• We start with defining Empty.
enum Tree<T>{
Empty,
Node(T, Box<Tree<T>>, Box<Tree<T>>)
}
• Node is a generic variable for the key, a left leaf, and
a right leaf.
• Recursive structures must be boxed1
.
• We want the size of the Tree to be bounded.
• Pointer using Box allocates memory on the heap.
1
https://doc.rust-lang.org/std/boxed/
Recap: Rust and OCaml Tree Structures6
type 'a tree =
| Empty
| Node of 'a * 'a tree * 'a tree
;;
enum Tree<T>{
Empty,
Node(T, Box<Tree<T>>, Box<Tree<T>>)
}
6
https://github.com/kelcecil/rust-ocaml-workshop/tree/master/workshop/01-structure
Writing a depth function
depth (OCaml using Pattern Matching)
let rec depth tree =
match tree with
| Empty -> 0
| Node(_, left, right) -> 1 + max (depth left) (depth right)
;;
• let defines a variable or function in OCaml.
• rec denotes a recursive function.
• The match keyword matches by data type.
depth (OCaml using function)
let rec depth = function
| Empty -> 0
| Node(_, left, right) -> 1 + max (depth left) (depth right)
;;
• OCaml functions can also be defined using the
function keyword.
• function has built in pattern matching for types.
• This syntax is a bit more concise.
Unit Test for depth (OCaml)
open OUnit
(* tree type here *)
(* depth function here *)
let test_tree = Node(5, Node(3, Empty, Empty), Empty)
let tree_depth _ =
assert_equal 2 (depth test_tree)
let suite = "Binary Tree Tests" >::: ["tree_depth" >:: tree_depth]
let _ =
run_test_tt_main suite
Running OCaml Tests
ocamlfind ocamlc -package oUnit -linkpkg -o <output_binary> <source_file>
depth (Rust)
impl<T> Tree<T>
where T: PartialOrd
{
fn depth(&self) -> usize {
match *self {
Tree::Empty => 0,
Tree::Node(_, ref left, ref right) =>
1 + max(left.depth(), right.depth())
}
}
}
• Restricting our generic to PartialOrd lets us choose
our key type similarly to OCaml.
Unit Test for depth (Rust)
#[test]
fn depth_test() {
let tree = Tree::Node(5, Box::new(
Tree::Node(4,Box::new(Tree::Empty),
Box::new(Tree::Empty)
)),
Box::new(Tree::Empty));
assert!(tree.depth() == 2)
}
Running Rust Unit Tests
rustc --test <source_file>
tl;dr
• OCaml's inspiration for Rust can been seen in:
• Semicolon Statement Separation
• Type Inference
• Algebraic Data Types
• Pattern Matching
• Rust and OCaml are both totally awesome.
Time for you to try!
• Adjust the our tree implementations to hold values.
• Write insert functions for OCaml and Rust!
• Write member functions for OCaml and Rust!
Stumped? No problem.
• https://github.com/kelcecil/rust-ocaml-workshop
• Clone the repository and experiment!
• Check out the other random examples in the
repository.
• Ask for help!
Branching in OCaml
OCaml has an if statement like so2
:
if <boolean_condition> then
<expression>
else if <boolean_condition> then
<expression>
else <expression>
2
https://ocaml.org/learn/tutorials/ifstatementsloopsandrecursion.html

Hey! There's OCaml in my Rust!

  • 1.
    HEY!There’s OCaml InMy Rust! Kel Cecil @praisechaos
  • 2.
  • 3.
  • 4.
  • 5.
    It's a functionalprogramming language... # let double x = x * 2 in List.map double [ 1; 2; 3 ];; - : int list = [2; 4; 6] • OCaml is an almost pure functional programming language by default. • Functions are first-class citizens.
  • 6.
    ... and animperative programming language... let update_collectable_value order new_value = order.old_value <- order.current_value; order.current_value <- new_value; order • It can be convenient to be able to code in the imperative style. • OCaml includes imperative constructs like for and while loops.
  • 7.
    ... and anobject-oriented language! class ['a] queue init = object val mutable q: 'a list = init method enqueue item = q <- q @ item end;; • OCaml can also be object-oriented if needed.
  • 8.
    OCaml powers somepretty sweet stuff. • MirageOS: Unikernel tech recently acquired by Docker • Flow: A static type checker for JavaScript • Hack: A programming languages for Facebook's HHVM • and...
  • 9.
    Rust's first compiler! •rustboot was written in OCaml • Replaced for Rust 0.7
  • 10.
    Rust is prettyexcellent too! • Guaranteed Memory Safety • Threads without data races • Incredibly fast • Crazy awesome potential
  • 11.
    Rust was inspiredby OCaml in a few interesting ways!
  • 12.
    Let's start witha simple, imperative OCaml program • Define a type named collectable_value that olds the previous value and current value of an item. • Provide a function to update the current value and update the old value. • Have a "main" function to create the record and update the value.
  • 13.
    Starting with ourtype type collectable_value = { mutable old_value: float; mutable current_value: float; } • A record is a collection of values stored together as a data type. • Values are immutable by default but can be marked as mutable.
  • 14.
    Adding our firstfunction let update_collectable_value collectable new_value = collectable.old_value <- collectable.current_value; collectable.current_value <- new_value; collectable • Define a function with a let-binding. • Accepts a collectablevalue record (collectable) and a float (newvalue). • Updates and returns the record.
  • 15.
    Adding a "main" let_ = let pinball_machine = { old_value = 0.0; current_value = 800.00; } in update_collectable_value pinball_machine 900.0 • Underscore discards the return value. • This let-binding allows us to execute code at the top level.
  • 16.
    Anything seem alittle Rust-y to you? type collectable_value = { mutable old_value: float; mutable current_value: float; } let update_collectable_value order new_value = order.old_value <- order.current_value; order.current_value <- new_value; order let _ = let pinball_machine = { old_value = 0.0; current_value = 800.00; } in update_collectable_value pinball_machine 900.0
  • 17.
    Semicolon Separation inOCaml let update_collectable_value collectable new_value = collectable.old_value <- collectable.current_value; collectable.current_value <- new_value; collectable • Notice the semi-colons separating the expressions? • The last expression is the return value for the function. • Look familiar?
  • 18.
    Statements in Rust fngreeting() -> String { let greeting = "Hello, there!"; String::from(greeting) // Return value from fn } • Rust code is mostly statements. • Most common statements are • declaring a variable • using an expression with a semi-colon • Last expression omitting the semi-colon is returned.
  • 19.
    What is Type Inference? •Automatic deduction of the data type of an expression.
  • 20.
    Rust's Type Inference //Rust knows that name is a String. let name = "Kel!" • Rust's type inference is loosely based on the Hindley-Milner algorithm4 . • Extensions were added to use accommodate subtyping. 4 https://github.com/rust-lang/rust/blob/master/src/librustc/infer/README.md
  • 21.
    OCaml's Type Inference (*OCaml also knows this is a string. *) let name = "Kel!" • ML originally used the Hindley-Milner algorithm. • OCaml's implementation works through solving constraints. • Tons of research hours poured into their algorithm(s)5 5 https://www.cis.upenn.edu/~sweirich/icfp-plmw15/slides/pottier.pdf
  • 22.
    Workshop: Binary SearchTree • Follow along in your IDEs! • Learn about binary search trees here: • https://en.wikipedia.org/wiki/Binary_search_tree • Check out the examples • https://github.com/kelcecil/rust-ocaml-workshop
  • 23.
    Building our TreeStructure OCaml, then Rust.
  • 24.
    Tree in OCaml typetree;; • Types in OCaml are defined with the type keyword.
  • 25.
    Tree in OCaml typetree = Empty ;; • We need to represent a node that does not exist. • We'll represent this as Empty.
  • 26.
    Tree in OCaml typetree = | Empty | Node ;; • We also want to represent a node that exists. • We'll represent this as node.
  • 27.
    Tree in OCaml type'a tree = | Empty | Node of 'a * 'a tree * 'a tree ;; • Our node must have a key, a left child, and a right child. • Node now contains generic 'a that is our key. • We also have two values of tree presenting our left and right leaves.
  • 28.
    Algebraic Data Types •Composite type formed from other types • Common types of Algebraic Types • Product Types • Sum Types
  • 29.
    Product Types • Compoundtypes in a structure • Operands of the product are types • Structure is determined by fixed order of operands
  • 30.
    (* Record *) typeperson = {name: string; age: int};; let kel = {name = "Kel"; age = 30};; (* Tuple *) let next_obsession = ("Dead Rising 4", 59.99);; Tuples and records are product types in OCaml
  • 31.
    let my_love =("Rust", 9001) • Tuples are product types in Rust • There's also the unit () type • Tuple with no operands.
  • 32.
    Sum Types type 'atree = | Empty | Node of 'a * 'a tree * 'a tree ;; • Multiple classes of values called variants. • Each class has it's own constructor. • Class represents it's own concept
  • 33.
    Tree in Rust enumTree { Empty } • Algebraic data types are enums in Rust. • We start with defining Empty.
  • 34.
    enum Tree<T>{ Empty, Node(T, Box<Tree<T>>,Box<Tree<T>>) } • Node is a generic variable for the key, a left leaf, and a right leaf. • Recursive structures must be boxed1 . • We want the size of the Tree to be bounded. • Pointer using Box allocates memory on the heap. 1 https://doc.rust-lang.org/std/boxed/
  • 35.
    Recap: Rust andOCaml Tree Structures6 type 'a tree = | Empty | Node of 'a * 'a tree * 'a tree ;; enum Tree<T>{ Empty, Node(T, Box<Tree<T>>, Box<Tree<T>>) } 6 https://github.com/kelcecil/rust-ocaml-workshop/tree/master/workshop/01-structure
  • 36.
  • 37.
    depth (OCaml usingPattern Matching) let rec depth tree = match tree with | Empty -> 0 | Node(_, left, right) -> 1 + max (depth left) (depth right) ;; • let defines a variable or function in OCaml. • rec denotes a recursive function. • The match keyword matches by data type.
  • 38.
    depth (OCaml usingfunction) let rec depth = function | Empty -> 0 | Node(_, left, right) -> 1 + max (depth left) (depth right) ;; • OCaml functions can also be defined using the function keyword. • function has built in pattern matching for types. • This syntax is a bit more concise.
  • 39.
    Unit Test fordepth (OCaml) open OUnit (* tree type here *) (* depth function here *) let test_tree = Node(5, Node(3, Empty, Empty), Empty) let tree_depth _ = assert_equal 2 (depth test_tree) let suite = "Binary Tree Tests" >::: ["tree_depth" >:: tree_depth] let _ = run_test_tt_main suite
  • 40.
    Running OCaml Tests ocamlfindocamlc -package oUnit -linkpkg -o <output_binary> <source_file>
  • 41.
    depth (Rust) impl<T> Tree<T> whereT: PartialOrd { fn depth(&self) -> usize { match *self { Tree::Empty => 0, Tree::Node(_, ref left, ref right) => 1 + max(left.depth(), right.depth()) } } } • Restricting our generic to PartialOrd lets us choose our key type similarly to OCaml.
  • 42.
    Unit Test fordepth (Rust) #[test] fn depth_test() { let tree = Tree::Node(5, Box::new( Tree::Node(4,Box::new(Tree::Empty), Box::new(Tree::Empty) )), Box::new(Tree::Empty)); assert!(tree.depth() == 2) }
  • 43.
    Running Rust UnitTests rustc --test <source_file>
  • 44.
    tl;dr • OCaml's inspirationfor Rust can been seen in: • Semicolon Statement Separation • Type Inference • Algebraic Data Types • Pattern Matching • Rust and OCaml are both totally awesome.
  • 45.
    Time for youto try! • Adjust the our tree implementations to hold values. • Write insert functions for OCaml and Rust! • Write member functions for OCaml and Rust!
  • 46.
    Stumped? No problem. •https://github.com/kelcecil/rust-ocaml-workshop • Clone the repository and experiment! • Check out the other random examples in the repository. • Ask for help!
  • 47.
    Branching in OCaml OCamlhas an if statement like so2 : if <boolean_condition> then <expression> else if <boolean_condition> then <expression> else <expression> 2 https://ocaml.org/learn/tutorials/ifstatementsloopsandrecursion.html