Swift's Optional Int as NSNumber in Objective-C


(Yarik Arsenkin) #1

Hi,

does anyone know if there is a reason why bridging of the Swift’s `Int?` type doesn’t work for Objective-C? I mean, it could be exposed as an `NSNumber` with nullable attribute, no?

Cheers,
Yarik


(John McCall) #2

I assume you're talking about the type restrictions on exporting Swift functions as @objc. We have the technical ability to bridge any Swift value to Objective-C as an opaque object, but that wouldn't actually produce meaningful APIs on the ObjC side; we have to have some sort of tighter policy than that. Today, that policy is to allow export when there's an obvious, idiomatic analogue in Objective-C. Exporting Int? as an optional NSNumber does not feel obvious and idiomatic when we would export Int as NSInteger. It feels like reaching for an arbitrary solution.

John.

···

On May 12, 2017, at 10:10 AM, Yarik Arsenkin via swift-evolution <swift-evolution@swift.org> wrote:
Hi,

does anyone know if there is a reason why bridging of the Swift’s `Int?` type doesn’t work for Objective-C? I mean, it could be exposed as an `NSNumber` with nullable attribute, no?


(Víctor Pimentel) #3

Actually Swift already export Ints as NSNumbers in other contexts when
types like Int are used in generic constraints, particularly value types
like Array or Dictionary. For example, a variable of type [Int] gets
bridged over as a NSArray<NSNumber *> *.

For such purposes Swift already has types like _SwiftTypePreservingNSNumber
, a subclass of NSNumber.

If that can apply to Array<Int> or Dictionary<Int, Int>, the same reasoning
can be applied to the Optional<Int>.

I mean, I'm not saying that there are no reasons to disallow it, but it
doesn't seem an arbitrary solution if in other contexts Swift is already
doing that.

···

On Fri, May 12, 2017 at 6:56 PM, John McCall via swift-evolution < swift-evolution@swift.org> wrote:

I assume you're talking about the type restrictions on exporting Swift
functions as @objc. We have the technical ability to bridge any Swift
value to Objective-C as an opaque object, but that wouldn't actually
produce meaningful APIs on the ObjC side; we have to have some sort of
tighter policy than that. Today, that policy is to allow export when
there's an obvious, idiomatic analogue in Objective-C. Exporting Int? as
an optional NSNumber does not feel obvious and idiomatic when we would
export Int as NSInteger. It feels like reaching for an arbitrary solution.

--
Víctor Pimentel


(Kenny Leung) #4

I don’t understand this reasoning. I’ve had cause to distinguish 0 from null in both Objective-C and Java, and I would do exactly the same thing.

-Kenny

···

On May 12, 2017, at 9:56 AM, John McCall via swift-evolution <swift-evolution@swift.org> wrote:

Exporting Int? as an optional NSNumber does not feel obvious and idiomatic when we would export Int as NSInteger. It feels like reaching for an arbitrary solution.


(John McCall) #5

I assume you're talking about the type restrictions on exporting Swift functions as @objc. We have the technical ability to bridge any Swift value to Objective-C as an opaque object, but that wouldn't actually produce meaningful APIs on the ObjC side; we have to have some sort of tighter policy than that. Today, that policy is to allow export when there's an obvious, idiomatic analogue in Objective-C. Exporting Int? as an optional NSNumber does not feel obvious and idiomatic when we would export Int as NSInteger. It feels like reaching for an arbitrary solution.

Actually Swift already export Ints as NSNumbers in other contexts when types like Int are used in generic constraints, particularly value types like Array or Dictionary. For example, a variable of type [Int] gets bridged over as a NSArray<NSNumber *> *.

We certainly do this. But we have, historically, driven a line between doing this and allowing it in exports. That's all I'm saying.

John.

···

On May 12, 2017, at 1:18 PM, Víctor Pimentel Rodríguez <vpimentel@tuenti.com> wrote:
On Fri, May 12, 2017 at 6:56 PM, John McCall via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

For such purposes Swift already has types like _SwiftTypePreservingNSNumber , a subclass of NSNumber.

If that can apply to Array<Int> or Dictionary<Int, Int>, the same reasoning can be applied to the Optional<Int>.

I mean, I'm not saying that there are no reasons to disallow it, but it doesn't seem an arbitrary solution if in other contexts Swift is already doing that.

--
Víctor Pimentel


(TJ Usiyan) #6

My understanding of the reasoning is that `NSNumber` is an object in
Objective-C and not a struct. There is already one level of decision when
translating to objc in that regard. Switching between reference
semantics/class and value semantics because of optionality is surprising.

···

On Mon, May 15, 2017 at 5:52 AM, Kenny Leung via swift-evolution < swift-evolution@swift.org> wrote:

> On May 12, 2017, at 9:56 AM, John McCall via swift-evolution < > swift-evolution@swift.org> wrote:

> Exporting Int? as an optional NSNumber does not feel obvious and
idiomatic when we would export Int as NSInteger. It feels like reaching
for an arbitrary solution.

I don’t understand this reasoning. I’ve had cause to distinguish 0 from
null in both Objective-C and Java, and I would do exactly the same thing.

-Kenny

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Charlie Monroe) #7

This is not much of an argument given that NSString is an object in ObjC (heap-allocated), String in Swift is an struct and also given that most NSNumber's nowadays are not really allocated, but just tagged pointers.

Given that NSNumber is immutable, you get the value semantics anyway...

···

On May 15, 2017, at 1:09 PM, T.J. Usiyan via swift-evolution <swift-evolution@swift.org> wrote:

My understanding of the reasoning is that `NSNumber` is an object in Objective-C and not a struct. There is already one level of decision when translating to objc in that regard. Switching between reference semantics/class and value semantics because of optionality is surprising.

On Mon, May 15, 2017 at 5:52 AM, Kenny Leung via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
> On May 12, 2017, at 9:56 AM, John McCall via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

> Exporting Int? as an optional NSNumber does not feel obvious and idiomatic when we would export Int as NSInteger. It feels like reaching for an arbitrary solution.

I don’t understand this reasoning. I’ve had cause to distinguish 0 from null in both Objective-C and Java, and I would do exactly the same thing.

-Kenny

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(TJ Usiyan) #8

The argument is not about whether or not it should come through as an
object. The argument is about the fact that *sometimes* it would come
through as an object and other times it would not. Optionality isn't an
obvious way to make that decision.

TJ

···

On Mon, May 15, 2017 at 3:03 PM, Charlie Monroe <charlie@charliemonroe.net> wrote:

This is not much of an argument given that NSString is an object in ObjC
(heap-allocated), String in Swift is an struct and also given that most
NSNumber's nowadays are not really allocated, but just tagged pointers.

Given that NSNumber is immutable, you get the value semantics anyway...

On May 15, 2017, at 1:09 PM, T.J. Usiyan via swift-evolution < > swift-evolution@swift.org> wrote:

My understanding of the reasoning is that `NSNumber` is an object in
Objective-C and not a struct. There is already one level of decision when
translating to objc in that regard. Switching between reference
semantics/class and value semantics because of optionality is surprising.

On Mon, May 15, 2017 at 5:52 AM, Kenny Leung via swift-evolution < > swift-evolution@swift.org> wrote:

> On May 12, 2017, at 9:56 AM, John McCall via swift-evolution < >> swift-evolution@swift.org> wrote:

> Exporting Int? as an optional NSNumber does not feel obvious and
idiomatic when we would export Int as NSInteger. It feels like reaching
for an arbitrary solution.

I don’t understand this reasoning. I’ve had cause to distinguish 0 from
null in both Objective-C and Java, and I would do exactly the same thing.

-Kenny

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Kenny Leung) #9

But my argument *is* that optionality is an obvious way to make that decision.

If I was writing in pure Objective-C (outside the context of Swift), sometimes I would have methods that take or return int, and sometimes I would have methods that take or return NSNumber. There is never really a surprise as to why. So why would there be a surprise when bridging from Swift?

-Kenny

···

On May 15, 2017, at 7:24 AM, T.J. Usiyan <griotspeak@gmail.com> wrote:

The argument is not about whether or not it should come through as an object. The argument is about the fact that *sometimes* it would come through as an object and other times it would not. Optionality isn't an obvious way to make that decision.

TJ

On Mon, May 15, 2017 at 3:03 PM, Charlie Monroe <charlie@charliemonroe.net <mailto:charlie@charliemonroe.net>> wrote:
This is not much of an argument given that NSString is an object in ObjC (heap-allocated), String in Swift is an struct and also given that most NSNumber's nowadays are not really allocated, but just tagged pointers.

Given that NSNumber is immutable, you get the value semantics anyway...

On May 15, 2017, at 1:09 PM, T.J. Usiyan via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

My understanding of the reasoning is that `NSNumber` is an object in Objective-C and not a struct. There is already one level of decision when translating to objc in that regard. Switching between reference semantics/class and value semantics because of optionality is surprising.

On Mon, May 15, 2017 at 5:52 AM, Kenny Leung via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
> On May 12, 2017, at 9:56 AM, John McCall via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

> Exporting Int? as an optional NSNumber does not feel obvious and idiomatic when we would export Int as NSInteger. It feels like reaching for an arbitrary solution.

I don’t understand this reasoning. I’ve had cause to distinguish 0 from null in both Objective-C and Java, and I would do exactly the same thing.

-Kenny

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(David Waite) #10

A second argument is that NSNumber does not represent an integer - only that it can be *initialized* by an integer. So the semantics of Int? -> NSNumber as an outbound result are completely different than the semantics of NSNumber -> Int? as an inbound parameter.

-DW

···

On May 15, 2017, at 8:24 AM, T.J. Usiyan via swift-evolution <swift-evolution@swift.org> wrote:

The argument is not about whether or not it should come through as an object. The argument is about the fact that *sometimes* it would come through as an object and other times it would not. Optionality isn't an obvious way to make that decision.


(Kenny Leung) #11

Once again, the counter-argument is the the same. Before there was Swift, this is exactly what we would do, and it was never a surprise.

I would say the semantics of wrapped API should be taken from the viewpoint of the target language (ObjC), and not from the viewpoint of Swift.

-Kenny

···

On May 16, 2017, at 3:04 PM, David Waite via swift-evolution <swift-evolution@swift.org> wrote:

On May 15, 2017, at 8:24 AM, T.J. Usiyan via swift-evolution <swift-evolution@swift.org> wrote:

The argument is not about whether or not it should come through as an object. The argument is about the fact that *sometimes* it would come through as an object and other times it would not. Optionality isn't an obvious way to make that decision.

A second argument is that NSNumber does not represent an integer - only that it can be *initialized* by an integer. So the semantics of Int? -> NSNumber as an outbound result are completely different than the semantics of NSNumber -> Int? as an inbound parameter.

-DW

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Jon Hull) #12

I have to side with Kenny on this one. I would find losing nil vs 0 more surprising than NSInteger vs NSNumber. In fact, I was surprised that this doesn’t already cross to a NSNumber. That would be the behavior I expect.

Thanks,
Jon

···

On May 16, 2017, at 11:51 AM, Kenny Leung via swift-evolution <swift-evolution@swift.org> wrote:

But my argument *is* that optionality is an obvious way to make that decision.

If I was writing in pure Objective-C (outside the context of Swift), sometimes I would have methods that take or return int, and sometimes I would have methods that take or return NSNumber. There is never really a surprise as to why. So why would there be a surprise when bridging from Swift?

-Kenny

On May 15, 2017, at 7:24 AM, T.J. Usiyan <griotspeak@gmail.com <mailto:griotspeak@gmail.com>> wrote:

The argument is not about whether or not it should come through as an object. The argument is about the fact that *sometimes* it would come through as an object and other times it would not. Optionality isn't an obvious way to make that decision.

TJ

On Mon, May 15, 2017 at 3:03 PM, Charlie Monroe <charlie@charliemonroe.net <mailto:charlie@charliemonroe.net>> wrote:
This is not much of an argument given that NSString is an object in ObjC (heap-allocated), String in Swift is an struct and also given that most NSNumber's nowadays are not really allocated, but just tagged pointers.

Given that NSNumber is immutable, you get the value semantics anyway...

On May 15, 2017, at 1:09 PM, T.J. Usiyan via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

My understanding of the reasoning is that `NSNumber` is an object in Objective-C and not a struct. There is already one level of decision when translating to objc in that regard. Switching between reference semantics/class and value semantics because of optionality is surprising.

On Mon, May 15, 2017 at 5:52 AM, Kenny Leung via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
> On May 12, 2017, at 9:56 AM, John McCall via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

> Exporting Int? as an optional NSNumber does not feel obvious and idiomatic when we would export Int as NSInteger. It feels like reaching for an arbitrary solution.

I don’t understand this reasoning. I’ve had cause to distinguish 0 from null in both Objective-C and Java, and I would do exactly the same thing.

-Kenny

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(David Waite) #13

When I call such a mapped Swift API that expects an Int? parameter from Objc with a NSNumber initialized with 3.5, what should happen?

[NSNumber uint64value: UINT64_MAX] ?

What about the float 1e100?

What about boolean 'true’?

NaN?

-DW

···

On May 19, 2017, at 8:54 PM, Jonathan Hull via swift-evolution <swift-evolution@swift.org> wrote:

I have to side with Kenny on this one. I would find losing nil vs 0 more surprising than NSInteger vs NSNumber. In fact, I was surprised that this doesn’t already cross to a NSNumber. That would be the behavior I expect.

Thanks,
Jon

On May 16, 2017, at 11:51 AM, Kenny Leung via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

But my argument *is* that optionality is an obvious way to make that decision.

If I was writing in pure Objective-C (outside the context of Swift), sometimes I would have methods that take or return int, and sometimes I would have methods that take or return NSNumber. There is never really a surprise as to why. So why would there be a surprise when bridging from Swift?

-Kenny

On May 15, 2017, at 7:24 AM, T.J. Usiyan <griotspeak@gmail.com <mailto:griotspeak@gmail.com>> wrote:

The argument is not about whether or not it should come through as an object. The argument is about the fact that *sometimes* it would come through as an object and other times it would not. Optionality isn't an obvious way to make that decision.

TJ

On Mon, May 15, 2017 at 3:03 PM, Charlie Monroe <charlie@charliemonroe.net <mailto:charlie@charliemonroe.net>> wrote:
This is not much of an argument given that NSString is an object in ObjC (heap-allocated), String in Swift is an struct and also given that most NSNumber's nowadays are not really allocated, but just tagged pointers.

Given that NSNumber is immutable, you get the value semantics anyway...

On May 15, 2017, at 1:09 PM, T.J. Usiyan via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

My understanding of the reasoning is that `NSNumber` is an object in Objective-C and not a struct. There is already one level of decision when translating to objc in that regard. Switching between reference semantics/class and value semantics because of optionality is surprising.

On Mon, May 15, 2017 at 5:52 AM, Kenny Leung via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
> On May 12, 2017, at 9:56 AM, John McCall via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

> Exporting Int? as an optional NSNumber does not feel obvious and idiomatic when we would export Int as NSInteger. It feels like reaching for an arbitrary solution.

I don’t understand this reasoning. I’ve had cause to distinguish 0 from null in both Objective-C and Java, and I would do exactly the same thing.

-Kenny

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Jon Hull) #14

What happens now if you call integerValue if a NSNumber has these values?

···

On May 19, 2017, at 9:00 PM, David Waite <david@alkaline-solutions.com> wrote:

When I call such a mapped Swift API that expects an Int? parameter from Objc with a NSNumber initialized with 3.5, what should happen?

[NSNumber uint64value: UINT64_MAX] ?

What about the float 1e100?

What about boolean 'true’?

NaN?

-DW

On May 19, 2017, at 8:54 PM, Jonathan Hull via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I have to side with Kenny on this one. I would find losing nil vs 0 more surprising than NSInteger vs NSNumber. In fact, I was surprised that this doesn’t already cross to a NSNumber. That would be the behavior I expect.

Thanks,
Jon

On May 16, 2017, at 11:51 AM, Kenny Leung via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

But my argument *is* that optionality is an obvious way to make that decision.

If I was writing in pure Objective-C (outside the context of Swift), sometimes I would have methods that take or return int, and sometimes I would have methods that take or return NSNumber. There is never really a surprise as to why. So why would there be a surprise when bridging from Swift?

-Kenny

On May 15, 2017, at 7:24 AM, T.J. Usiyan <griotspeak@gmail.com <mailto:griotspeak@gmail.com>> wrote:

The argument is not about whether or not it should come through as an object. The argument is about the fact that *sometimes* it would come through as an object and other times it would not. Optionality isn't an obvious way to make that decision.

TJ

On Mon, May 15, 2017 at 3:03 PM, Charlie Monroe <charlie@charliemonroe.net <mailto:charlie@charliemonroe.net>> wrote:
This is not much of an argument given that NSString is an object in ObjC (heap-allocated), String in Swift is an struct and also given that most NSNumber's nowadays are not really allocated, but just tagged pointers.

Given that NSNumber is immutable, you get the value semantics anyway...

On May 15, 2017, at 1:09 PM, T.J. Usiyan via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

My understanding of the reasoning is that `NSNumber` is an object in Objective-C and not a struct. There is already one level of decision when translating to objc in that regard. Switching between reference semantics/class and value semantics because of optionality is surprising.

On Mon, May 15, 2017 at 5:52 AM, Kenny Leung via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
> On May 12, 2017, at 9:56 AM, John McCall via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

> Exporting Int? as an optional NSNumber does not feel obvious and idiomatic when we would export Int as NSInteger. It feels like reaching for an arbitrary solution.

I don’t understand this reasoning. I’ve had cause to distinguish 0 from null in both Objective-C and Java, and I would do exactly the same thing.

-Kenny

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Philippe Hausler) #15

With the introduction of SE-0170 the behavior is a bit more predicable: The rule is that if the value would not loose mantissa representation or significant bits of the representation it will bridge to the target type in all scenarios, no matter if it is created in objc or in swift.

When I call such a mapped Swift API that expects an Int? parameter from Objc with a NSNumber initialized with 3.5, what should happen?

NSNumber(value: 3.5) as? Int == nil

But

NSNumber(value: 3.5).intValue == 3

[NSNumber uint64value: UINT64_MAX] ?

NSNumber(value: UInt64.max) as? Int == nil

What about the float 1e100?

NSNumber(value: 1.0e100) as? Int == nil

What about boolean 'true’?

NSNumber(value: true) as? Int == 1

NaN?

NSNumber(value: Double.nan) as? Int == nil

···

On May 19, 2017, at 9:00 PM, David Waite via swift-evolution <swift-evolution@swift.org> wrote:

-DW

On May 19, 2017, at 8:54 PM, Jonathan Hull via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I have to side with Kenny on this one. I would find losing nil vs 0 more surprising than NSInteger vs NSNumber. In fact, I was surprised that this doesn’t already cross to a NSNumber. That would be the behavior I expect.

Thanks,
Jon

On May 16, 2017, at 11:51 AM, Kenny Leung via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

But my argument *is* that optionality is an obvious way to make that decision.

If I was writing in pure Objective-C (outside the context of Swift), sometimes I would have methods that take or return int, and sometimes I would have methods that take or return NSNumber. There is never really a surprise as to why. So why would there be a surprise when bridging from Swift?

-Kenny

On May 15, 2017, at 7:24 AM, T.J. Usiyan <griotspeak@gmail.com <mailto:griotspeak@gmail.com>> wrote:

The argument is not about whether or not it should come through as an object. The argument is about the fact that *sometimes* it would come through as an object and other times it would not. Optionality isn't an obvious way to make that decision.

TJ

On Mon, May 15, 2017 at 3:03 PM, Charlie Monroe <charlie@charliemonroe.net <mailto:charlie@charliemonroe.net>> wrote:
This is not much of an argument given that NSString is an object in ObjC (heap-allocated), String in Swift is an struct and also given that most NSNumber's nowadays are not really allocated, but just tagged pointers.

Given that NSNumber is immutable, you get the value semantics anyway...

On May 15, 2017, at 1:09 PM, T.J. Usiyan via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

My understanding of the reasoning is that `NSNumber` is an object in Objective-C and not a struct. There is already one level of decision when translating to objc in that regard. Switching between reference semantics/class and value semantics because of optionality is surprising.

On Mon, May 15, 2017 at 5:52 AM, Kenny Leung via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
> On May 12, 2017, at 9:56 AM, John McCall via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

> Exporting Int? as an optional NSNumber does not feel obvious and idiomatic when we would export Int as NSInteger. It feels like reaching for an arbitrary solution.

I don’t understand this reasoning. I’ve had cause to distinguish 0 from null in both Objective-C and Java, and I would do exactly the same thing.

-Kenny

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(David Waite) #16

I believe behavior depends on whether the NSNumber is the objc type or swift subtype, and whether you call the NSNumber.intValue or use Int.init?(exactly: NSNumber).

My point is that there is no intuitive or safe generalized behavior for treating arbitrary NSNumbers as an Int? . If an application or library wants to accept a NSNumber and treat it as an Int?, it should be responsible for declaring that method and implementing that logic.

-DW

···

On May 19, 2017, at 11:04 PM, Jonathan Hull <jhull@gbis.com> wrote:

What happens now if you call integerValue if a NSNumber has these values?

On May 19, 2017, at 9:00 PM, David Waite <david@alkaline-solutions.com <mailto:david@alkaline-solutions.com>> wrote:

When I call such a mapped Swift API that expects an Int? parameter from Objc with a NSNumber initialized with 3.5, what should happen?

[NSNumber uint64value: UINT64_MAX] ?

What about the float 1e100?

What about boolean 'true’?

NaN?

-DW

On May 19, 2017, at 8:54 PM, Jonathan Hull via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I have to side with Kenny on this one. I would find losing nil vs 0 more surprising than NSInteger vs NSNumber. In fact, I was surprised that this doesn’t already cross to a NSNumber. That would be the behavior I expect.

Thanks,
Jon

On May 16, 2017, at 11:51 AM, Kenny Leung via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

But my argument *is* that optionality is an obvious way to make that decision.

If I was writing in pure Objective-C (outside the context of Swift), sometimes I would have methods that take or return int, and sometimes I would have methods that take or return NSNumber. There is never really a surprise as to why. So why would there be a surprise when bridging from Swift?

-Kenny

On May 15, 2017, at 7:24 AM, T.J. Usiyan <griotspeak@gmail.com <mailto:griotspeak@gmail.com>> wrote:

The argument is not about whether or not it should come through as an object. The argument is about the fact that *sometimes* it would come through as an object and other times it would not. Optionality isn't an obvious way to make that decision.

TJ

On Mon, May 15, 2017 at 3:03 PM, Charlie Monroe <charlie@charliemonroe.net <mailto:charlie@charliemonroe.net>> wrote:
This is not much of an argument given that NSString is an object in ObjC (heap-allocated), String in Swift is an struct and also given that most NSNumber's nowadays are not really allocated, but just tagged pointers.

Given that NSNumber is immutable, you get the value semantics anyway...

On May 15, 2017, at 1:09 PM, T.J. Usiyan via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

My understanding of the reasoning is that `NSNumber` is an object in Objective-C and not a struct. There is already one level of decision when translating to objc in that regard. Switching between reference semantics/class and value semantics because of optionality is surprising.

On Mon, May 15, 2017 at 5:52 AM, Kenny Leung via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
> On May 12, 2017, at 9:56 AM, John McCall via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

> Exporting Int? as an optional NSNumber does not feel obvious and idiomatic when we would export Int as NSInteger. It feels like reaching for an arbitrary solution.

I don’t understand this reasoning. I’ve had cause to distinguish 0 from null in both Objective-C and Java, and I would do exactly the same thing.

-Kenny

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(David Waite) #17

as? now has the closest to predictable behavior, but I would argue that in terms of generalizing a transform, the meaning of ‘nil’ for an Int? parameter may not match the behavior one would expect passing in a value which was not an integer value - say using 3.00000000004 as the argument (due to rounding errors) rather than the integer 3.

The safest default behavior (should a nullable NSNumber be used to represent an Int? parameter) would probably be to fatalError out if the user passed a NSNumber with a value which was not exactly an Int. No NSNumber instance would be interpreted as ‘nil’ once bridged into Swift.

This still allows the odd edge case of a boolean value being interpreted as 1, which could cause interface comprehension issues if Bool? was also mapped to NSNumber in Objc interfaces.

-DW

···

On May 20, 2017, at 12:47 PM, Philippe Hausler <phausler@apple.com> wrote:

With the introduction of SE-0170 the behavior is a bit more predicable: The rule is that if the value would not loose mantissa representation or significant bits of the representation it will bridge to the target type in all scenarios, no matter if it is created in objc or in swift.

On May 19, 2017, at 9:00 PM, David Waite via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

When I call such a mapped Swift API that expects an Int? parameter from Objc with a NSNumber initialized with 3.5, what should happen?

NSNumber(value: 3.5) as? Int == nil

But

NSNumber(value: 3.5).intValue == 3


(Philippe Hausler) #18

I believe behavior depends on whether the NSNumber is the objc type or swift subtype, and whether you call the NSNumber.intValue or use Int.init?(exactly: NSNumber).

That part of the behavior is no longer the case. The bridge only transacts upon one number type (to leverage tagged pointers) and the subclass was removed (since it caused some gnarly inconsistencies)

My point is that there is no intuitive or safe generalized behavior for treating arbitrary NSNumbers as an Int? . If an application or library wants to accept a NSNumber and treat it as an Int?, it should be responsible for declaring that method and implementing that logic.

But you are absolutely correct; NSNumber should be though of as a AnyNumericValue box where the type of the number is erased but the value is not.

Interesting cases include:

NSNumber(value: 3.0) as? Int == 3

Or

NSNumber(value: UInt64(121)) as? Int8 == Int8(121)

The SE-0170 proposal also added initializers for truncating and exactly that work much more on par with the rest of the numeric system in swift. (However it did un-cover a few nasty floating point bugs in the unit tests but the intent of the behavior is matched)

The other consideration that might be interesting to further your point that nullable NSNumber * as a representation of Int? Is a bit off is that often “optional” integers in objc are actually PoD types with sentry values; e.g. NSNotFound etc. So in reality we would need some sort of annotation on how the bridge of a numeric type should be represented to indicate a nullable value that is exposed as an integer or float etc.

···

On May 20, 2017, at 12:25 AM, David Waite via swift-evolution <swift-evolution@swift.org> wrote:

-DW

On May 19, 2017, at 11:04 PM, Jonathan Hull <jhull@gbis.com <mailto:jhull@gbis.com>> wrote:

What happens now if you call integerValue if a NSNumber has these values?

On May 19, 2017, at 9:00 PM, David Waite <david@alkaline-solutions.com <mailto:david@alkaline-solutions.com>> wrote:

When I call such a mapped Swift API that expects an Int? parameter from Objc with a NSNumber initialized with 3.5, what should happen?

[NSNumber uint64value: UINT64_MAX] ?

What about the float 1e100?

What about boolean 'true’?

NaN?

-DW

On May 19, 2017, at 8:54 PM, Jonathan Hull via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I have to side with Kenny on this one. I would find losing nil vs 0 more surprising than NSInteger vs NSNumber. In fact, I was surprised that this doesn’t already cross to a NSNumber. That would be the behavior I expect.

Thanks,
Jon

On May 16, 2017, at 11:51 AM, Kenny Leung via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

But my argument *is* that optionality is an obvious way to make that decision.

If I was writing in pure Objective-C (outside the context of Swift), sometimes I would have methods that take or return int, and sometimes I would have methods that take or return NSNumber. There is never really a surprise as to why. So why would there be a surprise when bridging from Swift?

-Kenny

On May 15, 2017, at 7:24 AM, T.J. Usiyan <griotspeak@gmail.com <mailto:griotspeak@gmail.com>> wrote:

The argument is not about whether or not it should come through as an object. The argument is about the fact that *sometimes* it would come through as an object and other times it would not. Optionality isn't an obvious way to make that decision.

TJ

On Mon, May 15, 2017 at 3:03 PM, Charlie Monroe <charlie@charliemonroe.net <mailto:charlie@charliemonroe.net>> wrote:
This is not much of an argument given that NSString is an object in ObjC (heap-allocated), String in Swift is an struct and also given that most NSNumber's nowadays are not really allocated, but just tagged pointers.

Given that NSNumber is immutable, you get the value semantics anyway...

On May 15, 2017, at 1:09 PM, T.J. Usiyan via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

My understanding of the reasoning is that `NSNumber` is an object in Objective-C and not a struct. There is already one level of decision when translating to objc in that regard. Switching between reference semantics/class and value semantics because of optionality is surprising.

On Mon, May 15, 2017 at 5:52 AM, Kenny Leung via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
> On May 12, 2017, at 9:56 AM, John McCall via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

> Exporting Int? as an optional NSNumber does not feel obvious and idiomatic when we would export Int as NSInteger. It feels like reaching for an arbitrary solution.

I don’t understand this reasoning. I’ve had cause to distinguish 0 from null in both Objective-C and Java, and I would do exactly the same thing.

-Kenny

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Ben Rimmington) #19

The subclass `_SwiftTypePreservingNSNumber` still exists:

<https://github.com/apple/swift/blob/master/stdlib/public/SDK/Foundation/TypePreservingNSNumber.mm>

Maybe the file isn't compiled anymore due to your pull requests:

<https://github.com/apple/swift/pull/9162>
<https://github.com/apple/swift/pull/9293>

You mentioned tagged pointers. My understanding is that:

* `OBJC_TAG_NSNumber` doesn't attempt to store a `float` or `double` in the 60-bit payload;
* `OBJC_TAG_NSDate` is used only if bits 0...3 of the `NSTimeInterval` (aka `double`) are zero.

<https://opensource.apple.com/source/objc4/objc4-709/runtime/objc-internal.h.auto.html>

How often is an NSDate (with "sub-millisecond precision") created as a tagged pointer?

-- Ben

···

On 20 May 2017, at 19:58, Philippe Hausler wrote:

On May 20, 2017, at 12:25 AM, David Waite wrote:

I believe behavior depends on whether the NSNumber is the objc type or swift subtype, and whether you call the NSNumber.intValue or use Int.init?(exactly: NSNumber).

That part of the behavior is no longer the case. The bridge only transacts upon one number type (to leverage tagged pointers) and the subclass was removed (since it caused some gnarly inconsistencies)


(Philippe Hausler) #20

I believe behavior depends on whether the NSNumber is the objc type or swift subtype, and whether you call the NSNumber.intValue or use Int.init?(exactly: NSNumber).

That part of the behavior is no longer the case. The bridge only transacts upon one number type (to leverage tagged pointers) and the subclass was removed (since it caused some gnarly inconsistencies)

The subclass `_SwiftTypePreservingNSNumber` still exists:

<https://github.com/apple/swift/blob/master/stdlib/public/SDK/Foundation/TypePreservingNSNumber.mm>

Hmm it shouldn’t, that looks like an oversight on my part if it is still alive.

Maybe the file isn't compiled anymore due to your pull requests:

<https://github.com/apple/swift/pull/9162>
<https://github.com/apple/swift/pull/9293>

Correct. it should no longer be used (hopefully it isn’t bloating binaries with un-used code). I will look into this here shortly.

You mentioned tagged pointers. My understanding is that:

* `OBJC_TAG_NSNumber` doesn't attempt to store a `float` or `double` in the 60-bit payload;

Integral double or float values are stored (when they can be). So 42.0 will be stored, but 53.5 won’t be. All other storage types (modulo NSDecimals) will store as a tagged pointer if they can; NSNumber(value: Int32.max) on a 64 bit system will be tagged but NSNumber(value: UInt64.max) of course won’t be.

The most common floating point NSNumbers are 0.0, 1.0, -1.0, 10.0, 22.0, 0.5. But it is worth noting that since we can construct NSNumbers as cached values or even as addresses of static structures (see https://github.com/apple/swift-corelibs-foundation/blob/master/CoreFoundation/NumberDate.subproj/CFNumber.c#L377)

* `OBJC_TAG_NSDate` is used only if bits 0...3 of the `NSTimeInterval` (aka `double`) are zero.

NSDates are stored as 56 bits in the tagged pointer.

<https://opensource.apple.com/source/objc4/objc4-709/runtime/objc-internal.h.auto.html>

How often is an NSDate (with "sub-millisecond precision") created as a tagged pointer?

Not very often at all really. The most common dates are usually integral values.

— Ben

To further give a bit of insight on tagged NSNumbers; using those instead of allocating a new instance each time accounts for about 1% on average for property lists. So this wasn’t really just a consistency change alone, it was execution time as well as memory fragmentation improvement.

It is also worth noting that as the overall system usage changes over time we might change our layout of tagged pointers (for both NSDate and NSNumber). So don’t rely on them being a certain way :wink:

···

On May 22, 2017, at 6:07 AM, Ben Rimmington <me@benrimmington.com> wrote:

On 20 May 2017, at 19:58, Philippe Hausler wrote:

On May 20, 2017, at 12:25 AM, David Waite wrote: