1

While investigating/trying to answer mythz's question, "How can we create a generic Array Extension that sums Number types in Swift?" I ran aground of some really odd behavior.

Is this a bug? or am I crazy?

Working examples of casting 0 as a float: (both result in 0.0)

var zero_float1: Float = 0
var zero_float2: Double = 0

Keep in mind that that Float is a typealias of Float32 and Double is a typealias of Float64

extension Array {
    func get_zero() -> T? {
        return 0 as? T
    }
}

returns 0

Array<Int>().get_zero()

but these return nil

Array<Float>().get_zero()
Array<Double>().get_zero()

...ok weird, maybe it's because it's a literal int ...? let's try something else

extension Array {
    func get_zero_float_literal() -> T? {
        return 0.0 as? T
    }
}

The following return nil:

Array<Float>().get_zero_float_literal()
Array<Int>().get_zero_float_literal()
Array<Float32>().get_zero_float_literal()

But these return 0.0 — whaaa?

Array<Float64>().get_zero_float_literal()
Array<Double>().get_zero_float_literal()

My understanding is that if you use Array<Float> you are supposed to be able to substitute T wherever Float would be. But it seems there is some caveat (or bug?)

Community
  • 1
  • 1
Jiaaro
  • 74,485
  • 42
  • 169
  • 190
  • Not a bug or even strange. These literals are being inferred as something and *becoming* that in the compiled code. In this case, an integer and floating point value with the natural width. When you check them against any other type, they are not those things and will return `nil` – Jason Coco Jun 10 '14 at 01:41
  • but if `T` is supposed to be a stand in for the type you give to the generic, then why does `0 as Float` work but not `0 as T` (where `T` is standing in for `Float`)? – Jiaaro Jun 10 '14 at 01:45
  • Honestly, I don't know. I think the entire language is extremely muddled and way too overridable, so maybe it's a bug with all the customization to the syntax the language allows, or some combination of things, or maybe in certain cases the compiler treats literals differently. I'd be interested to see if any of the compiler team would answer this question if asked in the apple forums. – Jason Coco Jun 10 '14 at 01:48
  • @JasonCoco will ask :) – Jiaaro Jun 10 '14 at 01:49
  • So what about using a double-cast `return 0.0 as Float as? T` – Christian Dietrich Jun 15 '14 at 10:50
  • I don't have a problem with the language being overridable. I have a problem with the fact that it's not well done in the case of generics or protocols. Not being able to create extension methods around protocols means that you have to create an extension for each concrete type that implements said protocol. Imagine the convenience (and opportunities for reuse!) of being able to write `extension SequenceType`, as you can do in C# by extending `IEnumerable`. This is truly, truly a shame. – Gregory Higley Oct 07 '14 at 17:53

1 Answers1

0

I think there's two things going on here:

  1. You can't use as to cast between numeric types. (e.g. 0.0 as Int and let f: Float = 0.0; f as Double generate compiler errors.) The supported method for converting between numerics is to use their constructors: Int(3.14) == 3 // true

  2. The compiler decides relatively early at compile time (with the assistance of type inferencing, where possible) what to turn your literals into.

    When given a statement like var zero_float2: Double = 0 it's smart enough to know it should interpret 0 as a double.

    But inside a generic function, it doesn't have that context to aid interpretation. Given the statement return 0.0 as? T the compiler just goes with the default interpretation of 0.0 as a Double, or 0 as an Int, and that type is hard-coded in the generic method, regardless of what T becomes.

Wes Campaigne
  • 4,060
  • 3
  • 22
  • 17