Proposal: failable numeric conversion initializers


(Matthew Johnson) #1

Problem:
Swift numeric types all currently have a family of conversion initializers. In many use cases they leave a lot to be desired. Initializing an integer type with a floating point value will truncate any fractional portion of the number. Initializing with an out-of-range value traps.

Solution:

Sometimes it would be more desirable to convert the runtime value if it can be done without losing information (or possibly with only minimal loss of precision when initializing a floating point type). This could be easily accomplished if the standard library had a family of failable initializers for all numeric types, either returning an Optional or throwing when the initialization was not successful.

I prefer the throwing version because failure can be automatically propagated up the call stack and the error could capture value that was provided and the type that failed to initialize which may be useful when debugging. Also, `try?` allows callers to throw away the error if the detail isn’t necessary. However, the Optional version would provide the basic functionality that is desired and would be sufficient if the community likes it better.

// Conversions from all integer types.
init?(_ value: Int8)
init?(_ value: Int16)
init?(_ value: Int32)
init?(_ value: Int64)
init?(_ value: Int)
init?(_ value: UInt8)
init?(_ value: UInt16)
init?(_ value: UInt32)
init?(_ value: UInt64)
init?(_ value: UInt)

// Conversions from all floating-point types.
init?(_ value: Float)
init?(_ value: Double)
#if arch(i386) || arch(x86_64)
init?(_ value: Float80)
#endif

OR

// Conversions from all integer types.
init(_ value: Int8) throws
init(_ value: Int16) throws
init(_ value: Int32) throws
init(_ value: Int64) throws
init(_ value: Int) throws
init(_ value: UInt8) throws
init(_ value: UInt16) throws
init(_ value: UInt32) throws
init(_ value: UInt64) throws
init(_ value: UInt) throws

// Conversions from all floating-point types.
init(_ value: Float) throws
init(_ value: Double) throws
#if arch(i386) || arch(x86_64)
init(_ value: Float80) throws
#endif


(Dmitri Gribenko) #2

One issue is that these initializers already exist with the signature
'init(_ value: IntXYZ)'. Adding these failable initializers would
make code like the following ambiguous:

var u8 = getUInt8()
var myInt = Int(u8)

We need a label to distinguish these.

Dmitri

···

On Sun, Dec 6, 2015 at 11:28 AM, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

Problem:
Swift numeric types all currently have a family of conversion initializers. In many use cases they leave a lot to be desired. Initializing an integer type with a floating point value will truncate any fractional portion of the number. Initializing with an out-of-range value traps.

Solution:

Sometimes it would be more desirable to convert the runtime value if it can be done without losing information (or possibly with only minimal loss of precision when initializing a floating point type). This could be easily accomplished if the standard library had a family of failable initializers for all numeric types, either returning an Optional or throwing when the initialization was not successful.

I prefer the throwing version because failure can be automatically propagated up the call stack and the error could capture value that was provided and the type that failed to initialize which may be useful when debugging. Also, `try?` allows callers to throw away the error if the detail isn’t necessary. However, the Optional version would provide the basic functionality that is desired and would be sufficient if the community likes it better.

// Conversions from all integer types.
init?(_ value: Int8)
init?(_ value: Int16)
init?(_ value: Int32)
init?(_ value: Int64)
init?(_ value: Int)
init?(_ value: UInt8)
init?(_ value: UInt16)
init?(_ value: UInt32)
init?(_ value: UInt64)
init?(_ value: UInt)

--
main(i,j){for(i=2;;i++){for(j=2;j<i;j++){if(!(i%j)){j=0;break;}}if
(j){printf("%d\n",i);}}} /*Dmitri Gribenko <gribozavr@gmail.com>*/


(thorsten@portableinnovations.de) #3

The throwing ones might use "strict", or something similar, i.e.

init(strict value: Int64) throws

-Thorsten

···

Am 07.12.2015 um 00:55 schrieb Dmitri Gribenko via swift-evolution <swift-evolution@swift.org>:

On Sun, Dec 6, 2015 at 11:28 AM, Matthew Johnson via swift-evolution > <swift-evolution@swift.org> wrote:

Problem:
Swift numeric types all currently have a family of conversion initializers. In many use cases they leave a lot to be desired. Initializing an integer type with a floating point value will truncate any fractional portion of the number. Initializing with an out-of-range value traps.

Solution:

Sometimes it would be more desirable to convert the runtime value if it can be done without losing information (or possibly with only minimal loss of precision when initializing a floating point type). This could be easily accomplished if the standard library had a family of failable initializers for all numeric types, either returning an Optional or throwing when the initialization was not successful.

I prefer the throwing version because failure can be automatically propagated up the call stack and the error could capture value that was provided and the type that failed to initialize which may be useful when debugging. Also, `try?` allows callers to throw away the error if the detail isn’t necessary. However, the Optional version would provide the basic functionality that is desired and would be sufficient if the community likes it better.

// Conversions from all integer types.
init?(_ value: Int8)
init?(_ value: Int16)
init?(_ value: Int32)
init?(_ value: Int64)
init?(_ value: Int)
init?(_ value: UInt8)
init?(_ value: UInt16)
init?(_ value: UInt32)
init?(_ value: UInt64)
init?(_ value: UInt)

One issue is that these initializers already exist with the signature
'init(_ value: IntXYZ)'. Adding these failable initializers would
make code like the following ambiguous:

var u8 = getUInt8()
var myInt = Int(u8)

We need a label to distinguish these.

Dmitri

--
main(i,j){for(i=2;;i++){for(j=2;j<i;j++){if(!(i%j)){j=0;break;}}if
(j){printf("%d\n",i);}}} /*Dmitri Gribenko <gribozavr@gmail.com>*/
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Dmitri Gribenko) #4

Well, choosing that word is very important :slight_smile: "strict" does not imply
"throwing" to me, FWIW, it is more like "lossless" than "throwing".

Dmitri

···

On Mon, Dec 7, 2015 at 7:49 AM, thorsten@portableinnovations.de <thorsten@portableinnovations.de> wrote:

The throwing ones might use "strict", or something similar, i.e.

init(strict value: Int64) throws

--
main(i,j){for(i=2;;i++){for(j=2;j<i;j++){if(!(i%j)){j=0;break;}}if
(j){printf("%d\n",i);}}} /*Dmitri Gribenko <gribozavr@gmail.com>*/


(Matthew Johnson) #5

I personally don't care what the argument label is (or even if this functionality is achieved through other means as Chris indicated a preference for). I just want the language to support failable numeric conversions in some way or another.

That said, if we do add such initializers and IEEE has a terminology for this it seems like a no-brainier to use it.

···

Sent from my iPad

On Dec 7, 2015, at 12:21 PM, Stephen Canon via swift-evolution <swift-evolution@swift.org> wrote:

If we’re going to bikeshed (:grinning:), I have a strong preference for “exact” for this usage. There would need to be a good reason to diverge from the widely-used IEEE 754 terminology.

On Dec 7, 2015, at 12:50 PM, Dmitri Gribenko via swift-evolution <swift-evolution@swift.org> wrote:

On Mon, Dec 7, 2015 at 7:49 AM, thorsten@portableinnovations.de >> <thorsten@portableinnovations.de> wrote:

The throwing ones might use "strict", or something similar, i.e.

init(strict value: Int64) throws

Well, choosing that word is very important :slight_smile: "strict" does not imply
"throwing" to me, FWIW, it is more like "lossless" than "throwing".

Dmitri

--
main(i,j){for(i=2;;i++){for(j=2;j<i;j++){if(!(i%j)){j=0;break;}}if
(j){printf("%d\n",i);}}} /*Dmitri Gribenko <gribozavr@gmail.com>*/
_______________________________________________
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


(Stephen Canon) #6

If we’re going to bikeshed (:grinning:), I have a strong preference for “exact” for this usage. There would need to be a good reason to diverge from the widely-used IEEE 754 terminology.

···

On Dec 7, 2015, at 12:50 PM, Dmitri Gribenko via swift-evolution <swift-evolution@swift.org> wrote:

On Mon, Dec 7, 2015 at 7:49 AM, thorsten@portableinnovations.de > <thorsten@portableinnovations.de> wrote:

The throwing ones might use "strict", or something similar, i.e.

init(strict value: Int64) throws

Well, choosing that word is very important :slight_smile: "strict" does not imply
"throwing" to me, FWIW, it is more like "lossless" than "throwing".

Dmitri

--
main(i,j){for(i=2;;i++){for(j=2;j<i;j++){if(!(i%j)){j=0;break;}}if
(j){printf("%d\n",i);}}} /*Dmitri Gribenko <gribozavr@gmail.com>*/
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution