Rust versus Haskell

Martin Sulzmann

Overview

Comparing Rust and Haskell:

Data types and pattern matching

Rust

Data types

pub enum Exp {
    Int {
        val: i32
    },
    Plus {
        left: Box<Exp>,
        right: Box<Exp>
    },
    Mult{
        left: Box<Exp>,
        right: Box<Exp>
    },
}

Pattern matching

fn eval(e : &Exp) -> i32 {
    match e {
      Exp::Int { val } => return *val,
      Exp::Plus { left, right } => return eval(left) + eval(right),
      Exp::Mult { left, right } => return eval(left) * eval(right),
    }
}

Examples

fn example12() {
   {
       let e = Exp::Int { val : 1 };
       println!("{}", eval(&e));
   }

   {
       let e = Exp::Plus{left : Box::new(Exp::Int { val : 1 }), right : Box::new(Exp::Int { val : 2})};
       println!("{}", eval(&e));
   }
}

Haskell

Data types

data Exp = Val Int | Plus Exp Exp | Mult Exp Exp

Pattern matching

eval :: Exp -> Int
eval (Val x) = x
eval (Plus l r) = eval l + eval r
eval (Mult l r) = eval l * eval r

Examples

exampleData =
  let e = Plus (Val 1) (Val 2)
  in putStrLn (show $ eval e)

Traits versus type classes

Rust traits

Trait declaration

trait Shape {
    fn area(s : &Self) -> i32;
}

Trait implementation

struct Rectangle {
    x : i32,
    y : i32
}

struct Square {
    x : i32
}

impl Shape for Rectangle {
    fn area(r : &Rectangle) -> i32 {
        return r.x * r.y;
    }
}

impl Shape for Square {
    fn area(s : &Square) -> i32 {
     return s.x * s.x;
    }
}

Examples

fn sum_area<A:Shape,B:Shape>(x : &A, y : &B) -> i32 {
   return Shape::area(x) + Shape::area(y);
}



fn example_trait() {
    let r = Rectangle{x : 1, y : 2};
    let s = Square{x : 3};

    println!("{}",sum_area(&r,&s));

}

Haskell type classes

Type class declaration

class Shape a where
  area :: a -> Int

Instance

data Square = MkSquare Int

data Rectangle = MkRectangle Int Int

instance Shape Square where
   area (MkSquare x) = x * x

instance Shape Rectangle where
   area (MkRectangle x y) = x * y

Examples

sumArea :: (Shape a, Shape b) => a -> b -> Int
sumArea x y = area x + area y

exampleTrait =
  let r = MkRectangle 1 2
      s = MkSquare 3
  in putStrLn (show $ sumArea r s)

Traits/type classes are not types

Rust dyn

fn sum_area3(x : Box<dyn Shape2>, y : Box<dyn Shape2>) -> i32 {
    return x.area2() + y.area2();
}


fn example_trait3() {
    let r = Box::new(Rectangle{x : 1, y : 2});
    let s = Box::new(Square{x : 3});

    println!("{}",sum_area3(r,s));

}

Haskell existential data types

data DynShape = forall a. Shape a => MkDynShape a

sumArea3 :: DynShape -> DynShape -> Int
sumArea3 (MkDynShape x) (MkDynShape y) = area x + area y

exampleTrait3 =
  let s1 = MkDynShape $ MkRectangle 1 2
      s2 = MkDynShape $ MkSquare 3
  in putStrLn (show $ sumArea3 s1 s2)

Appendix: Rust source code




//////////////////////////
// Traits

struct Rectangle {
    x : i32,
    y : i32
}

struct Square {
    x : i32
}

trait Shape {
    fn area(s : &Self) -> i32;
}

impl Shape for Rectangle {
    fn area(r : &Rectangle) -> i32 {
        return r.x * r.y;
    }
}

impl Shape for Square {
    fn area(s : &Square) -> i32 {
     return s.x * s.x;
    }
}

fn sum_area<A:Shape,B:Shape>(x : &A, y : &B) -> i32 {
   return Shape::area(x) + Shape::area(y);
}



fn example_trait() {
    let r = Rectangle{x : 1, y : 2};
    let s = Square{x : 3};

    println!("{}",sum_area(&r,&s));

}

// Method notation known from OO.

trait Shape2 {
    fn area2(&self) -> i32;
}

impl Shape2 for Rectangle {
    fn area2(&self) -> i32 {
        return self.x * self.y;
    }
}

impl Shape2 for Square {
    fn area2(&self) -> i32 {
     return self.x * self.x;
    }
}


fn sum_area2<A:Shape2,B:Shape2>(x : &A, y : &B) -> i32 {
   return x.area2() + y.area2();
}


fn example_trait2() {
    let r = Rectangle{x : 1, y : 2};
    let s = Square{x : 3};

    println!("{}",sum_area2(&r,&s));

}


fn sum_area3(x : Box<dyn Shape2>, y : Box<dyn Shape2>) -> i32 {
    return x.area2() + y.area2();
}


fn example_trait3() {
    let r = Box::new(Rectangle{x : 1, y : 2});
    let s = Box::new(Square{x : 3});

    println!("{}",sum_area3(r,s));

}

////////////////////////////////////////////////
// Data types and pattern matching in Rust

pub enum Exp {
    Int {
        val: i32
    },
    Plus {
        left: Box<Exp>,
        right: Box<Exp>
    },
    Mult{
        left: Box<Exp>,
        right: Box<Exp>
    },
}

fn eval(e : &Exp) -> i32 {
    match e {
      Exp::Int { val } => return *val,
      Exp::Plus { left, right } => return eval(left) + eval(right),
      Exp::Mult { left, right } => return eval(left) * eval(right),
    }
}

fn example_data() {
   {
       let e = Exp::Int { val : 1 };
       println!("{}", eval(&e));
   }

   {
       let e = Exp::Plus{left : Box::new(Exp::Int { val : 1 }), right : Box::new(Exp::Int { val : 2})};
       println!("{}", eval(&e));
   }
}

fn main() {

    example_trait();

    example_trait2();

    example_trait3();

    example_data();
}

Appendix: Haskell source code

{-# LANGUAGE ExistentialQuantification #-}

-- Type classes

data Square = MkSquare Int

data Rectangle = MkRectangle Int Int

class Shape a where
  area :: a -> Int

instance Shape Square where
   area (MkSquare x) = x * x

instance Shape Rectangle where
   area (MkRectangle x y) = x * y

sumArea :: (Shape a, Shape b) => a -> b -> Int
sumArea x y = area x + area y

exampleTrait =
  let r = MkRectangle 1 2
      s = MkSquare 3
  in putStrLn (show $ sumArea r s)

-- Existential types

data DynShape = forall a. Shape a => MkDynShape a

sumArea3 :: DynShape -> DynShape -> Int
sumArea3 (MkDynShape x) (MkDynShape y) = area x + area y

exampleTrait3 =
  let s1 = MkDynShape $ MkRectangle 1 2
      s2 = MkDynShape $ MkSquare 3
  in putStrLn (show $ sumArea3 s1 s2)

-- Pattern matching

data Exp = Val Int | Plus Exp Exp | Mult Exp Exp

eval :: Exp -> Int
eval (Val x) = x
eval (Plus l r) = eval l + eval r
eval (Mult l r) = eval l * eval r

exampleData =
  let e = Plus (Val 1) (Val 2)
  in putStrLn (show $ eval e)