Comparative Analysis of Record Types across Programming Languages

Table of Contents

Tutorial: Comparing Record Types in Guile, Clojure, Python, and TypeScript

Objective:

By the end of this tutorial, you'll understand how to define and use record types in Guile (SRFI-9), Clojure (defrecord), Python (data classes), and TypeScript. We'll cover the syntax, creation, usage, and modification of records in each language.

Contents:

  • Introduction to Record Types
  • Guile SRFI-9 Records
  • Clojure defrecord
  • Python Data Classes
  • TypeScript Classes
  • Comparison and Summary

Part 1: Introduction to Record Types

Duration: 5 minutes

Record types are a way to group related data together. They provide a structure with named fields, making it easier to manage and use data objects.

Part 2: Guile SRFI-9 Records

Duration: 20 minutes

Example:

  (use-modules (srfi srfi-9))

  (define-record-type <employee>
    (make-employee name age salary)
    employee?
    (name    employee-name)
    (age     employee-age    set-employee-age!)
    (salary  employee-salary set-employee-salary!))

  (define fred (make-employee "Fred" 45 20000.00))

  (employee? fred)        ;; #t
  (employee-age fred)     ;; 45
  (set-employee-salary! fred 25000.00)  ;; pay rise

Part 3: Clojure defrecord

Duration: 20 minutes

Example:

  (defrecord Employee [name age salary])

  (def fred (->Employee "Fred" 45 20000.00))

  (:age fred)          ;; 45
  (assoc fred :salary 25000.00)  ;; pay rise, creates a new record with updated salary
  • Explanation:
    • defrecord defines a new record type Employee.
    • ->Employee is the constructor.
    • :age is the accessor.
    • assoc is used to create a new record with updated fields (immutable update).

Part 4: Python Data Classes

Duration: 20 minutes

Example:

  from dataclasses import dataclass

  @dataclass
  class Employee:
      name: str
      age: int
      salary: float

  fred = Employee("Fred", 45, 20000.00)

  print(fred.age)        # 45
  fred.salary = 25000.00  # pay rise

  print(fred)
  • Explanation:
    • @dataclass is used to define a data class.
    • Fields are defined with type annotations.
    • Fields can be accessed and modified directly.

Part 5: TypeScript Classes

Duration: 20 minutes

Example:

  class Employee {
    constructor(public name: string, public age: number, public salary: number) {}

    setSalary(newSalary: number) {
      this.salary = newSalary;
    }
  }

  let fred = new Employee("Fred", 45, 20000.00);

  console.log(fred.age);  // 45
  fred.setSalary(25000.00);  // pay rise
  • Explanation:
    • class defines a class with a constructor.
    • Fields are defined in the constructor.
    • Methods can be added for field modifications.

Part 6: Comparison and Summary

Duration: 25 minutes

Syntax Comparison

  • Guile (SRFI-9):

        (define-record-type <employee> ...)
    
  • Clojure (defrecord):

        (defrecord Employee [name age salary])
    
  • Python (data classes):

        @dataclass
        class Employee:
    
  • TypeScript (classes):

        class Employee {
          constructor(public name: string, public age: number, public salary: number) {}
        }
    

Field Access and Modification

  • Guile:

        (employee-age fred)
        (set-employee-salary! fred 25000.00)
    
  • Clojure:

        (:age fred)
        (assoc fred :salary 25000.00)
    
  • Python:

        fred.age
        fred.salary = 25000.00
    
  • TypeScript:

        fred.age
        fred.setSalary(25000.00)
    

Immutability

  • Guile: Mutable by default, can be made immutable with define-immutable-record-type.
  • Clojure: Immutable by default, use assoc for updates.
  • Python: Mutable by default, use frozen=True in @dataclass for immutability.
  • TypeScript: Mutable by default, use techniques like Readonly for immutability.

Summary

  • Guile (SRFI-9): Strong syntax for functional programming, requires explicit accessors and modifiers.
  • Clojure (defrecord): Emphasizes immutability and functional updates.
  • Python (data classes): Simple and flexible, easy to use with direct field access.
  • TypeScript (classes): Combines object-oriented and functional programming features, supports methods for field manipulation.

Conclusion

By understanding these differences, you can choose the appropriate record type implementation for your project, whether you prefer the functional style of Clojure and Guile or the simplicity and flexibility of Python and TypeScript. This knowledge will help you effectively manage structured data in various programming paradigms.

Related Languages and Record Types

1. Rust Structs

Duration: 20 minutes

Rust uses structs to define record-like data structures.

Example:

  struct Employee {
      name: String,
      age: u32,
      salary: f64,
  }

  fn main() {
      let mut fred = Employee {
          name: String::from("Fred"),
          age: 45,
          salary: 20000.00,
      };

      println!("{}", fred.age);  // 45
      fred.salary = 25000.00;    // pay rise
  }
  • Explanation:
    • struct defines a new struct type.
    • Fields are defined with types.
    • Fields can be accessed and modified directly if mutable.

2. Haskell Data Types

Duration: 20 minutes

Haskell uses algebraic data types to define records.

Example:

  data Employee = Employee {
      name :: String,
      age :: Int,
      salary :: Float
  } deriving (Show)

  let fred = Employee "Fred" 45 20000.00

  age fred               -- 45
  fred {salary = 25000}  -- pay rise, creates a new record with updated salary
  • Explanation:
    • data keyword defines a new record type.
    • Fields are defined with types.
    • Records are immutable, creating a new record for updates.

3. Swift Structs

Duration: 20 minutes

Swift uses structs to define record-like data structures.

Example:

  struct Employee {
      var name: String
      var age: Int
      var salary: Double
  }

  var fred = Employee(name: "Fred", age: 45, salary: 20000.00)

  print(fred.age)        // 45
  fred.salary = 25000.00 // pay rise

  • Explanation:
    • struct defines a new struct type.
    • Fields are defined with types.
    • Fields can be accessed and modified directly.

4. Elm Records

Duration: 20 minutes

Elm uses records for data structures.

Example:

  type alias Employee =
      { name : String
      , age : Int
      , salary : Float
      }

  fred : Employee
  fred =
      { name = "Fred"
      , age = 45
      , salary = 20000.00
      }

  fred.age                  -- 45
  { fred | salary = 25000 } -- pay rise, creates a new record with updated salary
  • Explanation:
    • type alias defines a new record type.
    • Fields are defined with types.
    • Records are immutable, creating a new record for updates.

5. JavaScript (ES6 Classes)

Duration: 20 minutes

JavaScript uses classes to define record-like data structures.

Example:

  class Employee {
    constructor(name, age, salary) {
      this.name = name;
      this.age = age;
      this.salary = salary;
    }

    setSalary(newSalary) {
      this.salary = newSalary;
    }
  }

  let fred = new Employee("Fred", 45, 20000.00);

  console.log(fred.age);  // 45
  fred.setSalary(25000.00);  // pay rise
  console.log(fred);

  • Explanation:
    • class defines a new class type.
    • Fields are defined in the constructor.
    • Methods can be added for field modifications.
Person
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
}

class Employee extends Person {
  constructor(name, age, salary) {
    super(name, age); // Call parent constructor
    this.salary = salary;
  }
}

let bob = new Employee("Bob", 30, 50000);
console.log(bob.name); // "Bob" (Inherited from Person)
console.log(bob.salary); // 50000 

Point
class Point {
  constructor(x, y) {
    Object.defineProperty(this, 'x', { value: x, writable: false });
    Object.defineProperty(this, 'y', { value: y, writable: false });
  }
}

let p1 = new Point(3, 4);
// p1.x = 5; // Throws an error, as 'x' is not writable
console.log(p1);

Color
class Color {
  constructor(red, green, blue) {
    this.red = red;
    this.green = green;
    this.blue = blue;
  }

  equals(otherColor) {
    return (
      this.red === otherColor.red &&
      this.green === otherColor.green &&
      this.blue === otherColor.blue
    );
  }
}

let c1 = new Color(255, 0, 0); // Red
let c2 = new Color(255, 0, 0); // Red
let c3 = new Color(0, 255, 0); // Green

console.log(c1.equals(c2)); // true
console.log(c1.equals(c3)); // false

Comparison and Summary

Syntax Comparison

  • Rust:

        struct Employee {
            name: String,
            age: u32,
            salary: f64,
        }
    
  • Haskell:

        data Employee = Employee {
            name :: String,
            age :: Int,
            salary :: Float
        }
    
  • Swift:

        struct Employee {
            var name: String
            var age: Int
            var salary: Double
        }
    
  • Elm:

        type alias Employee = { name : String, age : Int, salary : Float }
    
  • JavaScript (ES6):

        class Employee {
          constructor(name, age, salary) {
            this.name = name;
            this.age = age;
            this.salary = salary;
          }
        }
    

Field Access and Modification

  • Rust:

        fred.age
        fred.salary = 25000.00
    
  • Haskell:

        age fred
        fred {salary = 25000}
    
  • Swift:

        fred.age
        fred.salary = 25000.00
    
  • Elm:

        fred.age
        { fred | salary = 25000 }
    
  • JavaScript (ES6):

        fred.age
        fred.setSalary(25000.00)
    

Immutability

  • Rust: Mutable by default, but can enforce immutability.
  • Haskell: Immutable by default.
  • Swift: Mutable by default, use let for immutability.
  • Elm: Immutable by default.
  • JavaScript: Mutable by default, use libraries like Immutable.js for immutability.

Summary

  • Rust Structs: Provide a balance between performance and safety with strong typing and mutability control.
  • Haskell Data Types: Emphasize immutability and functional programming.
  • Swift Structs: Flexible and easy to use with both mutability and immutability options.
  • Elm Records: Strongly typed and immutable by default, suited for functional programming.
  • JavaScript (ES6 Classes): Object-oriented approach with flexible mutability, widely used in web development.

Author: Jason Walsh

j@wal.sh

Last Updated: 2024-08-14 06:08:50