Passing a strongly typed identifier throughout the layers of an example application.
C# C# have records which provide immutability and equality testing. The record is sealed to prevent
inheritance. It overrides the built-in ToString() method. This example implementation includes a static method which can be used to initialize a new instance with a randomly generated
globally unique identifier (GUID). /// /// Represents a user identifier. /// /// The user identifier. public sealed record UserId(Guid Id) { /// /// Initializes a new instance of the record. /// /// A new UserId object. public static UserId New() => new(Guid.NewGuid()); public override string ToString() => Id.ToString(); }
C++ C++ has structs but not immutability so here the id field is marked as private with a method named getId() to get the value. import std; using std::string; class UserId { private: const string id; public: explicit UserId(const string& id): id{id} {}
nodiscard string getId() const noexcept { return id; }
nodiscard bool operator==(const UserId& rhs) const noexcept { return id == rhs.getId(); } };
Crystal Crystal's standard library provides the record macro for creating records which are immutable structs and lets you create override the built-in to_s method. require "uuid" • Represents a user identifier. record UserId, id : String do def initialize() @id = UUID.v4.to_s end def to_s(io) io
D D have immutable structs. import std; /** Represents a user identifier. */ immutable struct UserId { immutable UUID id; /** Initializes a new instance of the UserId struct. */ this(immutable string id) { this.id = UUID(id); } public static UserId create() { return UserId(randomUUID.toString()); } string toString() { return this.id.toString(); } }
Dart Dart have classes with operator overloading. import 'package:meta/meta.dart'; /// Represents a user identifier. @immutable final class UserId { final String id; /// Initializes a new instance of the UserId struct. const UserId(this.id); @override operator ==(other) => other is UserId && other.id == id; @override int get hashCode => id.hashCode; @override String toString() => id; }
F# F# lets you create override the Equals, GetHashCode and ToString methods. open System /// /// Represents a user identifier. /// /// The user identifier. type UserId(id: Guid) = member x.id = id static member New() = Guid.NewGuid() static member Empty = Guid.Empty override x.Equals(b) = match b with | :? UserId as p -> id = p.id | _ -> false override x.GetHashCode() = hash id override x.ToString() = id.ToString()
Go Go have structs which provide equality testing. Go however does not provide immutability. // Represents a user identifier. type UserId struct{ id string } // Creates a new user identifier. func NewUserId(id string) UserId { return UserId{id: id} } func (x UserId) String() string { return x.id }
Groovy Groovy have record classes which provide immutability and equality testing. /** * Represents a user identifier. * * @param id The user identifier. */ record UserId(String id) { String toString() { id } }
Haskell Haskell can create user-defined custom data types using the newtype keyword. It provides equality testing using the Eq standard class and printing using the Read and Show standard classes. -- Represents a user identifier. newtype UserId = UserId String deriving (Eq, Read, Show)
Java Java have records which provide equality testing. The record is declared using the final modifier keyword to prevent inheritance. It overrides the built-in toString() method. import java.util.UUID; /** * Represents a user identifier. * @param id The user identifier. */ public final record UserId(UUID id) { /** * Initializes a new instance of the UserId record. * @return A new UserId object. */ public static UserId newId() { return new UserId(UUID.randomUUID()); } public String toString() { return id.toString(); } }
JavaScript This
JavaScript example implementation provides the toJSON method used by the JSON.stringify() function to serialize the class into a simple string instead of a
composite data type. It calls Object.freeze() to make the instance immutable. It overrides the built-in toString() method and the valueOf() method. class UserId { #id; constructor(id) { if (id == undefined) { throw new TypeError("Argument is null or undefined."); } this.#id = id; Object.freeze(this); } static empty = new this.prototype.constructor("00000000-0000-0000-0000-000000000000"); static new() { return new this.prototype.constructor(crypto.randomUUID()); } equals(id) { return id instanceof this.constructor && this.#id === id.valueOf(); } toJSON() { return this.#id; } toString() { return this.#id; } valueOf() { return this.#id; } }
Julia Julia have immutable composite data types. using UUIDs "Represents a user identifier." struct UserId id::UUID end Base.string(userId::UserId) = userId.id
Kotlin Kotlin have "inline classes". /** * Represents a user identifier. * * @property id The user identifier. * @constructor Creates a user identifier. */ @JvmInline public value class UserId(public val id: String) { override fun toString() = id }
Nim Nim have "distinct types". • Represents a user identifier. type UserId* = distinct string
PHP This
PHP example implementation implements the __toString() magic method. Furthermore, it implements the JsonSerializable interface which is used by the built-in json_encode function to serialize the class into a simple string instead of a
composite data type. The class is declared using the final modifier keyword to prevent inheritance. PHP has traits as a way to re-use code. /** * Represents a user identifier. */ final class UserId implements JsonSerializable { use StronglyTypedIdentifier; } /** * Provides methods for use with strongly typed identifiers. */ trait StronglyTypedIdentifier { /** * Initializes a new instance of the UserId object. * @param string $id The user identifier. */ public function __construct(public readonly string $id) {} /** * Creates a new user identifier. */ public static function new(): self { return new self(bin2hex(random_bytes(16))); } public function jsonSerialize(): string { return $this->id; } public function __toString(): string { return $this->id; } }
Python Python has data classes which provides equality testing and can be made immutable using the frozen parameter. It overrides the __str__ dunder method. This example implementation includes a static method which can be used to initialize a new instance with a randomly generated
universally unique identifier (UUID). import uuid from dataclasses import dataclass from uuid import UUID @dataclass(frozen=True) class UserId: """Represents a user identifier.""" id: UUID @staticmethod def new() -> Self: """Create a new user identifier.""" return __class__(uuid.uuid4()) def __str__(self) -> str: return str(self.id) Python also has NewType which can be used to create new data types. from typing import NewType UserId: NewType = NewType('UserId', int)
Raku Raku have classes which provides equality testing and are immutable. It overrides the built-in method. This example overrides the default gist method. It uses roles which are mixed in into classes to be re-usable. role StronglyTypedIdentifier { has Str $.id is required; multi method new(Str $id) { self.bless(:$id); } multi method gist(StronglyTypedIdentifier:U:) { self.^name } multi method gist(StronglyTypedIdentifier:D:) { self.id } method Str() { "$!id" } method empty() { self.new("00000000-0000-0000-0000-000000000000") } } class UserId does StronglyTypedIdentifier {}
Ruby Ruby have data classes which provides equality testing and are immutable. It overrides the built-in to_s method. This example implementation includes a static method which can be used to initialize a new instance with a randomly generated
universally unique identifier (UUID). require 'securerandom' • Represents a user identifier. UserId = Data.define(:id) do # Create a new user identifier. def self.create self.new(SecureRandom.uuid) end def self.empty self.new('00000000-0000-0000-0000-000000000000') end def to_s id end end
Rust In
Rust this can be done using a
tuple struct containing a single value. This example implementation implements the Debug and the PartialEq
traits. The PartialEq trait provides equality testing. // Represents a user identifier. • [derive(Debug, PartialEq)] pub struct UserId(String);
Scala Scala have case classes which provide immutability and equality testing. The case class is sealed to prevent inheritance. import java.util.UUID /** Represents a user identifier. * * @constructor * Create a new user identifier. * @param id * The user identifier. */ sealed case class UserId(id: UUID) object UserId: /** Initializes a new instance of the UserId class. */ def create(): UserId = UserId(UUID.randomUUID())
Swift Swift have the CustomStringConvertible protocol which can be used to provide its own representation to be used when converting an instance to a string, and the Equatable protocol which provides equality testing. import Foundation /// Represents a user identifier. struct UserId: CustomStringConvertible, Equatable { private let id: UUID init(_ id: UUID) { self.id = id } var description: String { return id.uuidString.lowercased } /// Creates a new user identifier. static func new() -> Self { return Self(UUID()) } }
Zig Zig have structs with constants but by design does not have operator overloading and method overriding. /// Represents a user identifier. const UserId = struct { value: i32, /// Initializes a new instance of the UserId struct. pub fn init(value: i32) UserId { return UserId{ .value = value }; } }; == See also ==