Expressible Dynamic Phantom Types

Photo by Tandem X Visuals on UnsplashA phantom type is a custom type that has one or more unused type parameters.The simplest example would be this:struct Phantom<Context, WrappedValue> { let wrappedValue: WrappedValue}Why is this useful? It hel…


This content originally appeared on Level Up Coding - Medium and was authored by Chris Nevin

Photo by Tandem X Visuals on Unsplash

A phantom type is a custom type that has one or more unused type parameters.

The simplest example would be this:

struct Phantom<Context, WrappedValue> {
let wrappedValue: WrappedValue
}

Why is this useful? It helps us ensure that developers can’t accidentally put an unexpected value in a field, for example putting a firstName in a lastName field could be restricted like so…

enum PhantomTypes {
enum FirstName {}
enum LastName {}
}
typealias FirstName = Phantom<PhantomTypes.FirstName, String>
typealias LastName = Phantom<PhantomTypes.LastName, String>
struct Client {
let firstName: FirstName
let lastName: LastName
}

Type-Safety

So now a developer can’t actually do firstName = lastName without the compiler throwing a type-mismatch error.

What if we had two entities with firstName properties? What’s to stop me setting a client’s firstName to a producer’s firstName? We can introduce nesting of the Phantom type on the first unused property.

enum PhantomTypes {
enum FirstName {}
enum LastName {}
}
protocol FirstNameHaving {}
extension FirstNameHaving {
typealias FirstName = Phantom<Phantom<Self, FirstName>, String>
}
struct Client: FirstNameHaving {
let firstName: FirstName
}
struct Producer: FirstNameHaving {
let firstName: FirstName
}
client.firstName = producer.firstName // type-mismatch

This is great! We can ensure our values will be set correctly at compile-time without having to write any tests of our value mappers.

However, these are still a bit bulky to use. If you try to create a Producer above you’d have to write:

Producer(firstName: FirstName("firstName"))

If only there was a way that we could hide away that we are using a custom type and just provide the guts to the firstName property...

ExpressibleBy…Literal

Thankfully, there is! Here’s an example of ExpressibleByStringLiteral.

extension Phantom: ExpressibleByStringLiteral where WrappedValue: ExpressibleByStringLiteral {
init(stringLiteral value: StringLiteralType) {
self = Self(WrappedValue(stringLiteral: value as! WrappedValue.StringLiteralType))
}
}

Now we could write the above initializer like:

Producer(firstName: "firstName")
Note: It’s still a FirstName and you can still pass in a FirstName object.

There’s still one downside, if we wanted to check if the firstName was not empty (for example) we’d have to write something like:

producer.firstName.wrappedValue.isEmpty

It would be great if we could get rid of that wrappedValue so our use of phantom-types are transparent…

@dynamicMemberLookup

Syntactic sugar completes the final piece of the puzzle, here’s how we could add it to our Phantom struct:

@dynamicMemberLookup
struct
Phantom<Context, WrappedValue> {
var wrappedValue: WrappedValue
init(_ wrappedValue: WrappedValue) {
self.wrappedValue = wrappedValue
}
  subscript<T>(dynamicMember member: KeyPath<WrappedValue, T>) -> T {
get { return wrappedValue[keyPath: member] }
}
}

This allows us to access the properties of the WrappedValue directly from the Phantom type, for example:

producer.firstName.isEmpty

@propertyWrapper

We can take this one step further by introducing @propertyWrappers which take a WrappedType.

struct Producer: FirstNameHaving {
@Truncated(maxLength: 10)
var firstName: FirstName
}
producer.firstName = "Christopher"
print(producer.firstName) // "Christophe"

Thanks for reading!

I’ve created a Swift Package which contains the above plus many more protocol conformances, check it out… https://github.com/cjnevin/PhantomTypes


Expressible Dynamic Phantom Types was originally published in Level Up Coding on Medium, where people are continuing the conversation by highlighting and responding to this story.


This content originally appeared on Level Up Coding - Medium and was authored by Chris Nevin


Print Share Comment Cite Upload Translate Updates
APA

Chris Nevin | Sciencx (2022-10-22T12:06:16+00:00) Expressible Dynamic Phantom Types. Retrieved from https://www.scien.cx/2022/10/22/expressible-dynamic-phantom-types/

MLA
" » Expressible Dynamic Phantom Types." Chris Nevin | Sciencx - Saturday October 22, 2022, https://www.scien.cx/2022/10/22/expressible-dynamic-phantom-types/
HARVARD
Chris Nevin | Sciencx Saturday October 22, 2022 » Expressible Dynamic Phantom Types., viewed ,<https://www.scien.cx/2022/10/22/expressible-dynamic-phantom-types/>
VANCOUVER
Chris Nevin | Sciencx - » Expressible Dynamic Phantom Types. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2022/10/22/expressible-dynamic-phantom-types/
CHICAGO
" » Expressible Dynamic Phantom Types." Chris Nevin | Sciencx - Accessed . https://www.scien.cx/2022/10/22/expressible-dynamic-phantom-types/
IEEE
" » Expressible Dynamic Phantom Types." Chris Nevin | Sciencx [Online]. Available: https://www.scien.cx/2022/10/22/expressible-dynamic-phantom-types/. [Accessed: ]
rf:citation
» Expressible Dynamic Phantom Types | Chris Nevin | Sciencx | https://www.scien.cx/2022/10/22/expressible-dynamic-phantom-types/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.