# Integers and integer literals

We have the very handy `ExpressibleByIntegerLiteral` protocol, which lets custom types be represented by integer literals in source code.

Sometimes, however, we want to convert an existing integer value stored in a variable, to another type which is `ExpressibleByIntegerLiteral`. This is clearly a sensible thing to do, because there exists an integer literal which represents the integer value.

If that value were a compile-time constant we could just use the literal in source code to initialize the other type. But if the value is computed at runtimeâ€”perhaps the `count` of an arrayâ€”then it is not obvious how to perform the conversion. Thereâ€™s no way to turn an integer into an integer literal.

For example, suppose we have a `Field` protocol that extends `Numeric` to allow division, and we want to write a function which takes the average of a collection:

``````extension Collection where Element: Field {
func average() -> Element {
return reduce(0, +) / Element(count)
}
}
``````

This is a compiler error, because there is no initializer matching the call to `Element(count)`. Of course we should be able to convert an integer to a field typeâ€”after all there is a canonical homomorphism from the integers to any ring with identityâ€”but none of the protocols involved actually require such an initializer.

I propose we remedy the situation by making it possible to turn an integer into an `ExpressibleByIntegerLiteral` type. We can do this with a one-line initializer in a protocol extension. Here is a sample implementation:

``````extension ExpressibleByIntegerLiteral where IntegerLiteralType: BinaryInteger {
init<T: BinaryInteger>(_ n: T) {
self.init(integerLiteral: numericCast(n))
}
}
``````

Wouldnâ€™t that just be implicit conversion?

No, the conversion is explicit. You call an initializer just like any other.

This is a logical error. Simply because all values of type `T` can be represented in type `U` it does not follow that all members of type `U` can be represented in type `T`.

Consider this type:

``````struct WhatEvenAreNumbers: ExpressibleByIntegerLiteral {
private var val: UInt8?

init(integerLiteral: UInt8) {
self.val = val
}

init() {
self.val = nil
}
}
``````

It is clear that we cannot safely convert this to all types that are `ExpressibleByIntegerLiteral where IntegerLiteralType == UInt8`, or indeed to any type expressible by an integer literal.

Ah, apologies, on re-reading I see you were only proposing to allow `BinaryInteger` conformances to match. In that case I think the only time it'd be acceptable is if you were allowing it expressly for the case where `BinaryInteger == IntegerLiteralType`, and in that case you can naturally just call the initializer provided by `ExpressibleByIntegerLiteral`.

The point is to provide a type conversion from any `BinaryInteger` to the `Expressible` type.

We use `Int` as a currency type, and this is a deliberate design decision, but not every type uses `Int` as its literal type.

Plus, the `init(integerLiteral:)` initializer is documented with â€śDo not call this initializer directly.â€ť

I think that @lantua is right here: this is an implicit type conversion. You initially responded with

and that's true, but misleading. There are two type conversions occurring here the first from the `BinaryInteger` to the `IntegerLiteralType`, and then one from the `IntegerLiteralType` to the `ExpressibleByIntegerLiteralType`. As I understand it, you are proposing that this:

``````func t<Integer: BinaryInteger, Expressible: ExpressibleByIntegerLiteral>(_ I: Integer) -> Expressible {
return Expressible(integerLiteral: Expressible.IntegerLiteralType(I))
}
``````

should be able to be written

``````func t<Integer: BinaryInteger, Expressible: ExpressibleByIntegerLiteral>(_ I: Integer) -> Expressible {
return Expressible(integerLiteral: I)
}
``````

Constructed in this way it is clear that you are proposing an implicit type conversion. I think it is right and fair to reject that conversion.

No.

I am proposing that one could write `Expressible(n)`.

This is exactly analogous to writing `Float(n)`.

The `FloatingPoint` protocol already includes an identical initializer `init<T: BinaryInteger>(_ n: T)`.

I am saying this conversion is more generally applicable than just `FloatingPoint`. It should work for any `Numeric` type, and indeed for any `ExpressibleByIntegerLiteral` type whose `IntegerLiteralType` is itself a `BinaryInteger`.

The conversion is explicit, and it is precedented in the standard library.

What you wrote works just about fine:

``````extension Collection where Element: Field {
func average() -> Element {
guard let countAsElement = Element(exactly: count) else {
// You need to handle this case appropriately.
}
return reduce(0, +) / countAsElement
}
}
``````
1 Like

I don't think I agree with the generalisation. I'm open to saying it should work for `Numeric`, and indeed it does work for `BinaryInteger`, but many types conform to `ExpressibleByIntegerLiteral` that should not be created from arbitrary binary integers. Indeed, I wrote one.

Things that can be expressed by integer literals in the code are not necessarily integers.

Surely you are not proposing that we eliminate the existing `FloatingPoint` initializer with identical signature to the one I am proposing, and replace all calls like `Double(n)` with `guard let x = Double(exactly: n) else { â€¦ }`, right?

Iâ€™m sure you recognize the value of the simple call `Double(n)`.

I disagree with both your assertion and your example.

If a type can be constructed from an integer literal, then it can equally well be constructed from the same integer stored in a variable.

No, but also some generic code might be better off if we did. It's important to observe that there are some dangerous edge cases around the naked conversion that deserve attention. What should happen if you average 100,000 `Float16` values? 100,000 converts to infinity in `Float16`, so the average without this check would always be zero or NaN. Is that desirable behavior?

This isn't as bad for floating-point, because the conversion from integer values is fully defined and never traps, it only saturates to out-of-range values; hoisting the init up to Numeric would make it available on a wide range of types that either have never had to define what happens for out-of-range integer conversions, or trap all such conversions, which makes using it in a generic context pretty dangerous.

1 Like

It is expected behavior.

Though I will note that if the value exceeds the maximum of the `IntegerLiteralType` then the conversion will trap, just as using such a literal would cause a compile-time error.

Swift has been clear and consistent in maintaining that trapping is safe, not dangerous. Swift traps on integer overflow, on out-of-bounds array access, and on force-unwrapping `nil`.

If a type can be initialized from an integer literal, then it can handle all values of its `IntegerLiteralType`. A value which overflows that type will of course trap, as expected.