Date has two `init(_:strategy:)` for parsing from some kind of input (I only know of String). Help me understand why the need to have two

#1
init<T, Value>(_ value: Value, strategy: T) throws where T : ParseStrategy, Value : StringProtocol, T.ParseInput == String, T.ParseOutput == Date

#2
init<T>(_ value: T.ParseInput, strategy: T) throws where T : ParseStrategy, T.ParseOutput == Date

My understanding is #1 is for parsing from Stringy input and it's called this way:

try Date(someString, .iso8601)

What's #2? Is it for parsing from some generic ParseInput type? When is it called? What's the call site like?

There is a deprecated failable one:

init?<T>(_ value: T.ParseInput, strategy: T) where T : ParseStrategy, T.ParseOutput == Date

Howcome this doesn't create ambiguity with those other init's?

(1) is a generalization of a special case of (2):

  • (2) creates a Date using a ParseStrategy T, given the ParseInput that T accepts, as long as T produces Dates from parsing (i.e. ParseInput -> T -> ParseOutput == Date). This is the general method: you could create a ParseStrategy whose ParseInput type is anything, and if you called Date.init(_:strategy:) with that strategy, this would get called
  • (1) does the same, but doesn't tie the input Value directly to T.ParseInput: if T expects an input value of String, you can still provide any StringProtocol-conforming value (e.g. String, SubString, StaticString, etc.) to that version of the method as it can convert it to String and then pass it to the strategy (i.e. Value -> ParseInput == String -> T -> ParseOutput == Date)

In other words, (1) lets you pass more String-like values to the initializer and still let you parse them using a ParseStrategy that more strictly expects String input.

(As for ambiguity: the compiler typically prefers "more specific" versions of methods over more general ones [and can take into account deprecation] in order to avoid ambiguity without needing more input.)

2 Likes