I'm confused about the compiler's type inference of generics within Swift (and hence the intended utility of generics), as I'm unable to efficiently resolve a use case I would expect to be commonplace.
The code file below demonstrates my issue:
import Foundation
class Playground {}
class Home: Playground
{
static let precision = Double( 1000 )
}
class Garden: Playground
{
static let precision = Double( 10000 )
}
class Other: Playground {}
struct Num<T:Playground>
{
typealias Base = Double
let precision: Base?
let original: Base
let value: Base
init( _ value: Base, _ precision: Base? = nil )
{
self.original = value
self.precision = precision
if let precision = precision
{
self.value = round( value * precision ) / precision
}
else
{
self.value = value
}
}
init( _ value: Base ) where T == Home
{
self.init( value, Home.precision )
}
init( _ value: Base ) where T == Garden
{
self.init( value, Garden.precision )
}
static func + ( _ lhs: Num<T>, _ rhs: Num<T> ) -> Num<T>
{
return Num<T>( lhs.value + rhs.value )
}
static func + ( _ lhs: Num<T>, _ rhs: Num<T> ) -> Num<T> where T == Garden
{
return Num<T>( lhs.value + rhs.value )
}
}
struct test
{
init()
{
let home1 = Num<Home>( 1.0 )
let home2 = Num<Home>( 2.0 )
let oops = home1 + home2
let garden1 = Num<Garden>( 1.0 )
let garden2 = Num<Garden>( 2.0 )
let eureka = garden1 + garden2
}
}
The console debug output of test init() is show below:
(lldb) p eureka
(Testing.Num<Testing.Garden>) $R0 = (precision = 10000, original = 3, value = 3)
(lldb) p oops
(Testing.Num<Testing.Home>) $R2 = {
precision = nil
original = 3
value = 3
}
(lldb)
eureka has the correct precision (10000): the compiler utilised the explicit types of Num<Garden> to choose the specialised + (where T == Garden), and then used the known type (T == Garden) to select the specialised init where T == Garden and apply the precision.
oops has a precision of nil: the compiler utilised the explicit types of Num<Home> to choose the unconstrained + (since no other T == constraints matched for +), but then selected the unconstrained init instead of utilising the fact that for this path T == Home is known.
I expected oops to be resolved at compile time to utilise the correct specialised type init, and yield the correct precision.
This can be fixed by introducing a constrained version of + for the case T == Home, after which oops will have the correct precision. However that is an unusable pattern, requiring every subtype of T be explicitly catered for in every init (and every related function that eventually propagates to init - a combinatoric mess), entirely defeating the utility of generics.
What am I missing - is this a known constraint of Swift, or is this not the Swift way to handle this pattern?