Pass Values as Generic Types

Generics in Swift currently allows for you to define what type a method, function, or other type will use. This works great in most cases, but every once in a while I wish I could define a static value for a type that way. Here are a few instances that I have run into that I have wanted this for, or examples I have come up with:

Examples

Validates Types

Sometimes you might run into a case where you have a type with a String property, but the value of the property should only be allowed to have n characters. I personally had this when creating a client for the PayPal REST API. They have a lot of object string properties with a maximum length. I managed to create a type that wrapped the value type with a certain validation type, so setting the property would fail if you gave it an invalid value (https://github.com/skelpo/Failable). You might declare the property like this:

let name: Failable<String, Length256Validator> = try Failable("My Valid Name")

The problem with this is that you have to create a separate Length<N>Validator type for each length. It would be much easier if we could just pass the length in as a parameter to a generic LengthValidator type:

let name: Failable<String, LengthValidator<256>> = try Failable("My Valid Name")

Formatted Dates

Sometimes you have an app that uses a certain date encoding for some of your date properties when they are encoded/decoded. If you want to use synthesized encoder/decoder methods, you will have to use a custom type that wraps that actual date because JSONEncoder.date(Decoder|Encoding)Strategy changes the encoding for all dates. If you wanted date with the yyyy-MM-dd format, you might create a TimelessDate type and if you want the yyyy-MM format you might create YearMonthDate type.

Now the only difference between theses types is the date encoding format, so if instead of creating two types we could create one type that takes in a generic value, you only have one type to deal with:

typealias TimelessDate = FormattedDate<"yyyy-MM-dd">
typealias YearMonthDate = FormattedDate<"yyyy-MM">

public struct FormattedDate<format: String>: Codable {
    internal static let formatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.calendar = Calendar(identifier: .iso8601)
        formatter.locale = Locale(identifier: "en_US_POSIX")
        formatter.timeZone = TimeZone(secondsFromGMT: 0)
        formatter.dateFormat = format
        return formatter
    }()
    
    public var date: Date
    
    public init(_ date: Date) {
        self.date = date
    }
    
    public init?(string: String) {
        guard let date = Self.formatter.date(from: string) else { return nil }
        self.date = date
    }

    public init(from decoder: Decoder)throws {
        let string = try decoder.singleValueContainer().decode(String.self)
        guard let date = Self.formatter.date(from: string) else {
            throw DecodingError.typeMismatch(
                Date.self,
                DecodingError.Context(codingPath: [], debugDescription: "Cannot get date from string `\(string)`")
            )
        }
        
        self.date = date
    }

    public func encode(to encoder: Encoder)throws {
        var container = encoder.singleValueContainer()
        let string = Self.formatter.string(from: date)
        try container.encode(string)
    }
}

Fixed Size Arrays

A FixedSizeArray type could be added to the standard library that has a generic Element type and a size value with the size of the array:

FixedSizeArray<Int, 42>

In Other Languages

The closest thing to this feature are templates in C++ which I happened to run across one day when reading this: https://learnxinyminutes.com/docs/c++/

Related Posts

Notes and Feedback

I personally don't have the time or knowledge to implement this myself at the moment, so I can't create an actual evolution pitch, but I thought I'd throw this out so it can either be placed on the table or killed outright :smile:. Thoughts?

5 Likes

Fixed-size arrays and generic multiplicity parameters have been discussed several times on this forum, and it would be helpful to the discussion if you could link to some of those threads for the benefit of people coming into this one.

Also, I think your date formatter example is the first time I have seen a reasonable motivation for string literals as generic parameters. Although, I can’t shake the feeling that even this is rather stringly-typed and a better design might be possible, though I don’t know what it would look like.

3 Likes

I would love to see this. In XDR, arrays can be specified as fixed-length or variable length (with an optional maximum count). Variable length arrays are encoded with the element count, but fixed-length arrays are not. To properly encode and decode such an array, I use a wrapper that knows the capacity. As in your validator example, I need to create a new type for each length.

Even if we don't get fixed-length arrays in the standard library, I would love to be able to instantiate a single wrapper type with different lengths.

1 Like

Related Posts:

1 Like
Terms of Service

Privacy Policy

Cookie Policy