Default values for generic parameters?

In Alamofire 5 we added the ability to pass Encodable types as request parameters. Currently, the methods to do so look like this:

open func request<Parameters: Encodable>(_ convertible: URLConvertible,
                                         method: HTTPMethod = .get,
                                         parameters: Parameters? = nil,
                                         encoder: ParameterEncoder = URLEncodedFormParameterEncoder.default,
                                         headers: HTTPHeaders? = nil,
                                         interceptor: RequestInterceptor? = nil) -> DataRequest

However, I've realized that, without the request overload we provide for compatibility with older, no generic parameters, this method can't be used without providing a parameters value, as otherwise the compiler can't infer that type. Within this method we capture the various parameters into a generic struct that gets passed further into Alamofire, and I could pass that value our Empty type as Optional<Empty>.none, and then offer an additional request overload that doesn't require parameters. However, that seems suboptimal, so I'm wondering if there's a way to get the default value for parameters to work the way I want, that is by allowing any type, but when one isn't provided, using some concrete value to represent no parameters.

Any ideas?

1 Like

I'm not familiar with Alamofire so maybe I'm missing some reasoning behind the feature, but from what I've seen in the source, you are eventually erasing the type anyways. Do you need this generic function behavior at all then?

Yes, all request components are eventually erased into a URLRequestConvertible value. However, JSONEncoder requires a generic Encodable parameter:

func encode<T>(_ value: T) throws -> Data where T : Encodable

Therefore we can't erase the Encodable parameters.

Bump. +1.

I'd also really like to see this addressed in some way. Here's part of an API I'm working on:

public mutating func replaceComponents<Components>(
  _ range: Range<Index>, with newComponents: Components
) -> Range<Index> where Components: Collection, Components.Element: StringProtocol {
  // ...

Unfortunately, this means users can't use empty array literals, because the compiler doesn't know what type Components should be:

replaceComponents(someRange, with: []) // Error: Value of protocol type 'Any' cannot conform to 'StringProtocol'; only struct/enum/class types can conform to protocols

If I wanted to support this, I'd have to replace the generic function with an _impl function, and add 2 entry-points - one from String, another from StringProtocol (the String version cannot call in to the generic version directly, as it itself is a more-specific overload so this would recurse until stack overflow). This function also has a fairly lengthy documentation comment, so I'd have to copy that and keep it synced.

It's manageable, but it's clunky. And if I decide it isn't worth the bother, users will be hit with a cryptic error talking about how Any can't conform to protocols :neutral_face::man_shrugging:

Terms of Service

Privacy Policy

Cookie Policy