When I try to compile the following expression:
let colorRectangles: [Rectangle] = (0..<colors.count).map { i in
return Rectangle(
x: 20.0 + 100.0 * Float(i % 7) + 10.0 * Float(i % 7),
y: 80.0 + 100.0 * Float(i / 7) + 10.0 * Float(i / 7),
width: 100.0,
height: 100.0
)
}
I get the familiar error: error: the compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions
. What confuses me is that this seems like a simple case where all types should be obvious. Breaking it into smaller parts or explicitly typing the closure parameter as (i: Int) in
instead of i in
fixes it. Another solution is to replace colors.count
with an intermediate variable, like this:
let maxColorsCount = colors.count
let colorRectangles: [Rectangle] = (0..<maxColorsCount).map { i in
return Rectangle(
x: 20.0 + 100.0 * Float(i % 7) + 10.0 * Float(i % 7),
y: 80.0 + 100.0 * Float(i / 7) + 10.0 * Float(i / 7),
width: 100.0,
height: 100.0
)
}
All variables in this expression, except i, have explicit types. Rectangle has a single constructor that takes four Float parameters. Since colors.count is an Int, the range 0..<colors.count is inferred as Range. Given that map accepts a closure of type (Int) -> T, the type of i should be unambiguously inferred as Int. Yet, the compiler still struggles.
I see the same issue here:
let numbers = [Int](repeating: 0, count: 20)
let myFloats: [Float] = (0..<numbers.count).map { i in
return 20.0 + 100.0 * Float(i % 2) + 10.0 * Float(i * 5)
}
Again, using an intermediate variable for numbers.count
resolves it. I know type inference can have exponential worst-case complexity, but these expressions feel straightforward. Even something like let result: Double = -(-(1 + 1) + -(1 + 1) - 1) + (-1 + 1 - (-1) + (-1))
takes too long to compile, despite its simplicity.
Can anyone explain what’s happening? Also, I’ve noticed that switching Double
to Float
in that last example allows it to compile, though it’s still slow (4.26 seconds on my machine). Why does this change make a difference?
Lastly, has anyone confirmed that Swift’s type inference algorithm is as efficient as it could be? Could tweaks like adjusting evaluation order or adding memoization improve it in cases like these?