I guess the question is how likely are they to be passed to the APIs that expect CGFloat arguments. It sounds to me that most of the side computations are happening outside of the API calls and only their results are used, where with operators the probability is high that they would be inlined into the CGFloat APIs.
I guess I simply disagree strongly with this kind of probabilistic reasoning to justify the design choice. I'd rather it be a more commonly encountered caveat that users are aware of than smoothed over only partially such that it's encountered rarely, but where the consequences can be more serious.
That sounds fair to me, that's why I made the pitch the way it is :)
With the proposed rules including the operator conversion restrictions, what would happen in this case?
let x: CGFloat = 0.3
let y: Double = 13.4
takesCGFloat(calculation(x: x, y: y))
// Assuming calculation has both CGFloat and Double
// signatures to choose from
// do we end up in the same boat as?:
takesCGFloat(x * y)
Yes, for any homogeneous overload i.e. (Double, Double) -> Double to be used in CGFloat context it would be required to have two conversions - takesCGFloat(CGFloat(calculation(x: Double(x), y: y)) or a single narrowing conversion with CGFloat overload - takesCGFloat(calculation(x: x, y: CGFloat(y))), either way there is a narrowing conversion used, so to figure out what is better we'd have to form a complete solution and rank based depth.
I'm going to try to see whether I can mix AST depth into the score for narrowing conversions, theoretically it should be possible to stably rank any narrowing, which happens deeper in expression, lower than the one that happens higher up, so we'd bubble up narrowing if it's contextually required.
Good news! Looks like using expression depth to stabilize scores worked out the way I have anticipated, so I managed to make everything work in the most desirable way possible - any number of widenings is better than one narrowing, and narrowing should happen as late as possible. I have also added more tests to the branch if somebody wants to take a look.
Source compatibility suite is currently running, let's see if these changes caused any perf or other regressions ![]()
![]()
No so great news from the source compatibility suite - there are a couple of ambiguities in existing code now - CareKit and IBAnimatable, both fail to disambiguate / operator reference in the operator chain. I'm going to look into that tomorrow.
I think this is quite common unfortunately. Itâs easy to declare something as CGFloat not because it necessarily represents something on screen, but because it will be used in a calculation sometime later when shown on screen. So I donât think our strict rule of "make everything Double and only convert to CGFloat when rendering" is representative of broader usage.
If implicit conversions make Double the path of least resistance instead, that could help make CGFloat much more clearly "a graphics thing," and something you shouldnât really declare variables of yourself. So counter-intuitively, not having to convert to CGFloat might actually make people use it less. (I guess this is essentially what @scanon said upthread.)
Some projects might even want to lint for CGFloat declarations.
Following up regarding the status of new rules - I tweaked favoring to avoid prioritizing narrowing conversions and to account the fact that unary operators don't have CGFloat overloads, so locally both CareKit and IBAnimateable pass, re-running whole suite again.
I know the operator rules around this are a bit of a hack anyway, so that makes sense. If I understand, you're basically disabling narrowing conversions for operators, which is fine, because I think we can safely assume that any operator available for CGFloat will also be available for Double. If a user defines a custom operator specifically for CGFloat, at worst they will have to use explicit conversions to narrow Double values.
Here's hoping the suite passes. Do the new rules include the operator-only restrictions? Or are the expression-depth adjustments used instead of those?
@xwu It's not actually disabling narrowing but avoids preferring narrowing e.g. let _: CGFloat = x / y could prefer /(_: CGFloat, _: CGFloat) -> CGFloat overload because it matches on the contextual type and just stop attempting any other choices.
@James_Dempsey New changes are for expression depth adjustments to bubble up narrowing to the top.
Source compatibility suite passed! I'm going to start working on the proposal that incorporates all of the feedback from this pitch. Thank you everybody who participated, especially @xwu, @scanon, @Ben_Cohen, @hborla and @James_Dempsey!