Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I create a new type-safe primitive type, based on Int?

Problem context: I'm learning functional domain modeling and Swift at the same time. The examples I am working from are in F#. One such example is of declaring single case unions:

//F# code
type CustomerId = CustomerId of int
type OrderId = OrderId of int

let customerId = CustomerId 42
let orderId = OrderId 42

printfn "%b" (orderId = customerId) //produces a compile error

So it appears to be super-easy, in F#, to declare type-safe primitives that are both essentially Ints. In Swift typealias does not do the same thing:

typealias CustomerId = Int
typealias OrderId = Int

let customerId: CustomerId = 42
let orderId: OrderId = 42

customerId == orderId // true!

Now, I understand that all primitive types in Swift are essentially structs, but I'm nowhere near sophisticated enough to understand the Swift code base to figure out how Int was created. Does anyone know of a simple type-safe Swift equivalent to the F# declaration?

like image 752
RdeG Avatar asked Sep 21 '25 00:09

RdeG


1 Answers

typealias is literally just an alias. It does not create a type. It just makes it easier to type the thing being aliased.

The way to do what you're trying to do is:

struct CustomerID {
    let value: Int
}

struct OrderID {
    let value: Int
}

If you want a "bare" initializer (CustomerID(4) rather than CustomerID(value: 4)), you could add that:

struct CustomerID {
    let value: Int
    init(_ value: Int) { self.value = value }
}

Since you'd probably do that a lot, you can extract it into a protocol

protocol Identifier {
    typealias IDType
    init(value: IDType)
}

extension Identifier {
    init(_ value: Int) { self.init(value: value) }
}

extension CustomerID: Identifier {}

And of course you could add similar extensions using ExpressibleByIntegerLiteral if you wanted syntax like = 42.

The specific syntax you're discussing in F# is often referred to in the Swift world by its Haskell name, newtype. Swift doesn't have a feature as easy to use as newtype, and it's been discussed several times. There are several corner cases in Swift where it's a bit complicated to decide how you would automatically conform to protocols (particularly protocols that require Self). But it may be implemented some day.

You can see a much more in-depth discussion of this a 2019 AltConf talk on the subject.

like image 103
Rob Napier Avatar answered Sep 23 '25 11:09

Rob Napier