# Type Safety in Swift: Haskell-style Newtypes with Phantom Types

In software engineering, "primitive obsession" describes a scenario where domain-specific concepts are represented by general types like strings, integers, or timestamps. This lack of specificity often leads to logic errors, for instance, accidentally passing an arrival timestamp into a function that expects a departure timestamp.

Haskell addresses this through the `newtype` construct, which creates a distinct type from an existing one with zero runtime cost. In Swift, we can replicate this rigorous type safety by combining **Phantom Types** with the **RawRepresentable** protocol.

## The Compile-Time Safety Guarantee

The primary advantage of this pattern is that it shifts domain logic errors from runtime to compile time. If we define distinct types for our arrival and departure timestamps, the Swift compiler physically prevents us from swapping them.

Swift

```plaintext
func createFlight(destination: String, arrival: NewType<ArrivalTag>, departure: NewType<DepartureTag>) -> Flight {
    // ❌ Compiler Error: Cannot convert value of type 'NewType<ArrivalTag>' to expected argument type 'NewType<DepartureTag>'
    return Flight(arrival: departure, departure: arrival, destination: destination)
}
```

By explicitly tagging the types, the mistake of passing `departure` into the `arrival` parameter is caught immediately during development.

## The Implementation

The following code demonstrates the generic `NewType` wrapper. By using empty enums as "Tags", we create distinct types for arrival and departure that the compiler will treat as strictly incompatible.

Swift

```plaintext
import Foundation

struct NewType<Tag>: RawRepresentable, Codable {
    let rawValue: TimeInterval
}

extension NewType: Hashable  {}
extension NewType: Equatable {}

enum ArrivalTag {}
enum DepartureTag {}

struct Flight: Hashable, Codable {
    let arrival: NewType<ArrivalTag>
    let departure: NewType<DepartureTag>
    let destination: String
}

let flightJSON = """
{
    "destination": "London LHR",
    "arrival": 2345,
    "departure": 876867
}
"""

func decodeFlight(from jsonString: String) -> Flight? {
    let decoder = JSONDecoder()
    
    guard let data = jsonString.data(using: .utf8) else { return nil }

    do {
        return try decoder.decode(Flight.self, from: data)
    } catch {
        print("❌ Decoding failed: \(error)")
        return nil
    }
}

if let flight = decodeFlight(from: flightJSON) {
    print("✅ Successfully decoded Flight:")
    print("   Destination: \(flight.destination)")
    print("   Arrival    : \(flight.arrival.rawValue)")
    print("   Departure  : \(flight.departure.rawValue)")
}
```

## Functional Programming and Category Theory Concepts

### Phantom Types

The `Tag` parameter in the `NewType` struct is a **Phantom Type**. It exists only in the type signature and does not appear in the stored properties of the struct. Its sole function is to serve as a marker for the compiler. This ensures that `NewType<ArrivalTag>` and `NewType<DepartureTag>` are seen as entirely different entities.

### Isomorphism

From the perspective of category theory, the `NewType` struct is **isomorphic** to the underlying `TimeInterval`. This means there is a total, one-to-one mapping between a `TimeInterval` and a `NewType<Tag>` instance.

By utilising `RawRepresentable`, we preserve this isomorphism. We gain the semantic clarity of a custom type without losing the ability to access and manipulate the data in its original primitive form.

## The Advantage of RawRepresentable and Flat Data

In Swift, defining a standard wrapper struct often results in nested JSON structures. For example, without specific conformance, a `NewType` might encode to a nested object like `{"arrival": {"rawValue": 2345}}`.

By adopting `RawRepresentable`, the Swift `Codable` system recognises the struct as a transparent wrapper around its `rawValue`. This enables **flat data encoding**: the JSON remains a clean, standard numeric timestamp, while the Swift domain model remains strictly typed. This provides the exact safety of Haskell's `newtype` while maintaining perfect compatibility with existing, flat API structures.

## Conclusion

Utilising this pattern offers several critical benefits for architecture:

*   **Strict Type Safety**: Logic errors regarding parameter roles are caught at compile time.
    
*   **No Performance Overhead**: Structs are value types that the Swift compiler can heavily optimise, treating them identically to the underlying primitive at runtime.
    
*   **Declarative Intent**: The code explicitly documents the role of each piece of data through the type system rather than relying on variable naming conventions.
