Comparative Analysis of Record Types across Programming Languages
Table of Contents
- Tutorial: Comparing Record Types in Guile, Clojure, Python, and TypeScript
- Related Languages and Record Types
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 typeEmployee
.->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.