What is the name of this technique/pattern?

I notice that this pattern shows up quite frequently in the Swift world but I don't know what its name is.

  1. define a new type that conforms to a protocol
  2. solely use the new type as a unique value to create a new "thing"
extension VerticalAlignment {
    struct CustomAlignment: AlignmentID {...}

    static let customAlignment = VerticalAlignment(CustomAlignment.self)
}
1 Like

Define a new LayoutValueKey type only for passing layout value

private struct Flexibility: LayoutValueKey {
    static let defaultValue: CGFloat? = nil
}

extension View {
    func layoutFlexibility(_ value: CGFloat?) -> some View {
        layoutValue(key: Flexibility.self, value: value)
    }
}

Define a new PreferenceKey type only for passing preference value

struct NavigationBarTitleKey: PreferenceKey {
    static var defaultValue: String = ""
    ...
}

extension View {
    func navigationBarTitle(_ title: String) -> some View {
        self.preference(key: NavigationBarTitleKey.self, value: title)
    }
}

i don’t really think there is a name for it, i personally just call them type constants.

Yes, I also vaguely have a name for it, but I want an “official” name to search and read more about it.

Apart from the name, do you have any thoughts about this pattern? I would love to hear about it.

i think they can be useful internally within a module as a way of simulating C++ template constants. i don’t think there is a good reason to expose them (permanently) as public API although i’ve definitely shipped them before due to time constraints. using types as values is just not really using types for what they are meant for.

1 Like

This pattern is often called “phantom types” and is also used in other programming languages too, for example Haskell, Rust, TypeScript, Elm, and Scala.

3 Likes

I can't see how "adding a type parameter to a type definition without changing its structure" relates here. Am I missing something? Can you elaborate on this?

I'm not sure whether this relates to the topic. But at Swift runtime, we can call type(of:) to get the type values, so the phantom type does have effects at runtime.

Can you explain more about this? I'm really confused right now.

It's more that the type won't take any physical storage (metadata notwithstanding) because it's only being used at compile time, the type not existing at all at runtime is either an oversimplification or a Scala-ism.

Of course, if the compiler can prove that you aren't doing anything with the type at runtime it's free to elide the metadata as well.

This may well be a use of Phantom Types, but it's not the usual use since it's being used it to pass around static variables. Normally Phantom Types are used to distinguish between types that would otherwise be identical. Like the Tagged library does for it's newtypes.

3 Likes

You’re right. After looking more closely at the examples in your posts, I see that I was mistaken. These aren't phantom types. The type objects are needed to provide functions (as static methods or properties), presumably so that (unlike bare functions) they can be compared for equality during view reconciliation.