Representation of natural numbers with a distinct type


(James F) #1

In the standard library, there are methods (notably array subscripts, CollectionType's .count and the dimensions of CGSize) which are designed to deal only with natural (non-negative) numbers. Functions either throw an error if a parameter is negative, or else ignore the possibility of a negative value finding its way into these functions.

By updating the standard library with a natural number type to represent these values (and potentially the Swift interfaces for Foundation and other frameworks), there is no way a negative number can be passed to these functions. This eliminates the need for many preconditions, allowing them to eliminate the possibility of a crash, and it would be clear that values such as count will never be negative.

If this change were accepted, the user would have to explicitly convert between the types at some points in their code. This could be seen as a burden, but requiring a cast encourages the programmer to check for negative values, keeping negatives out of natural number functions, and moving checks to the source of the data, similar to how Optionals eliminate nil values early on.

The parallel to Optionals is, in my opinion, the most appealing aspect of this idea. With Swift's focus on type safety, the lack of distinction between natural numbers and potentially negative ones seems like an omission, given how commonly both kinds of number are used. Enabling this level of integral safety would add additional clarity and reduce potential errors. Use cases include not just indexes, but in sizes such as collections' count and CG dimensions, for both clarity when getting values, and enforcing valid values at initialisation.

UInt is the obvious candidate to represent integer natural numbers, but is hazardous in edge cases: UInt allows an extra bit for magnitude, so has the possibility of overflow when converting back to Int. I assume this hazard is the primary reason UInt currently isn't used. If this a concern, a separate collection of types for natural integers (NInt?) could be created, which could be a strict subset of the correspondingly sized Int values (using n-1 bits).

This would mean adding a new collection of integer types in the standard library, which is undesirable. However, for high level applications which Swift is primarily used for, this would arguably be a more useful type than UInt, considering the near absence of UInt in current APIs, and its tiny increase (1 bit?) in precision compared to using an Int of a larger size. An unsigned Integer subset would allow safe conversion back to Int, and the conversion from Int is relatively simple, from the design perspective - ignore the sign bit, throw an error, or round up to 0, as appropriate.

Alternatives include:
• A generic wrapper for Int which constrains its possible values. This would allow inherent support for use with existing operators, but would be rather clunky in cases where only natural numbers are being dealt with.
• In a future version of Swift, the addition of type parameters constraining possible values may enable this to be added, but even if this does get added it may be too late to make such a major change.
• Leaving the APIs as they are and require every function dealing with natural numbers to either throw a runtime error when a negative is passed. If the user forgets to follow this rule (or wont put in the extra effort to add the check), their functions will have unspecified behaviour.

This ended up longer than I would like, sorry about that. I don't think this issue has been covered here.
I'd be interested to hear responses and opinions, and while I'm confident this change would be beneficial if done correctly, doing it correctly is easier said that done.

- James


(David Turnbull) #2

-1 CollectionType and it's ilk, contrary to what you believe, were
designed to handle negative indexes just fine. Or even indexes that aren't
numeric. And there's plenty of real use cases for this.

class GraphRow : MutableCollectionType {

    var values = [Int](count: 201, repeatedValue: 0)

    var startIndex: Int { return -100 }

    var endIndex: Int { return 100 }

    subscript(column: Int) -> Int {

        get {

            return values[column+100]

        }

        set {

            values[column+100] = newValue

        }

    }

}

var r = GraphRow()

r[-3] = 12

r[9] = 2

print(r[-3])

print(r.reduce(0, combine: +))

-david

···

On Wed, Feb 3, 2016 at 9:31 AM, James Froggatt via swift-evolution < swift-evolution@swift.org> wrote:

In the standard library, there are methods (notably array subscripts,
CollectionType's .count and the dimensions of CGSize) which are designed to
deal only with natural (non-negative) numbers. Functions either throw an
error if a parameter is negative, or else ignore the possibility of a
negative value finding its way into these functions.

By updating the standard library with a natural number type to represent
these values (and potentially the Swift interfaces for Foundation and other
frameworks), there is no way a negative number can be passed to these
functions. This eliminates the need for many preconditions, allowing them
to eliminate the possibility of a crash, and it would be clear that values
such as count will never be negative.

If this change were accepted, the user would have to explicitly convert
between the types at some points in their code. This could be seen as a
burden, but requiring a cast encourages the programmer to check for
negative values, keeping negatives out of natural number functions, and
moving checks to the source of the data, similar to how Optionals eliminate
nil values early on.

The parallel to Optionals is, in my opinion, the most appealing aspect of
this idea. With Swift's focus on type safety, the lack of distinction
between natural numbers and potentially negative ones seems like an
omission, given how commonly both kinds of number are used. Enabling this
level of integral safety would add additional clarity and reduce potential
errors. Use cases include not just indexes, but in sizes such as
collections' count and CG dimensions, for both clarity when getting values,
and enforcing valid values at initialisation.

UInt is the obvious candidate to represent integer natural numbers, but is
hazardous in edge cases: UInt allows an extra bit for magnitude, so has the
possibility of overflow when converting back to Int. I assume this hazard
is the primary reason UInt currently isn't used. If this a concern, a
separate collection of types for natural integers (NInt?) could be created,
which could be a strict subset of the correspondingly sized Int values
(using n-1 bits).

This would mean adding a new collection of integer types in the standard
library, which is undesirable. However, for high level applications which
Swift is primarily used for, this would arguably be a more useful type than
UInt, considering the near absence of UInt in current APIs, and its tiny
increase (1 bit?) in precision compared to using an Int of a larger size.
An unsigned Integer subset would allow safe conversion back to Int, and the
conversion from Int is relatively simple, from the design perspective -
ignore the sign bit, throw an error, or round up to 0, as appropriate.

Alternatives include:
• A generic wrapper for Int which constrains its possible values. This
would allow inherent support for use with existing operators, but would be
rather clunky in cases where only natural numbers are being dealt with.
• In a future version of Swift, the addition of type parameters
constraining possible values may enable this to be added, but even if this
does get added it may be too late to make such a major change.
• Leaving the APIs as they are and require every function dealing with
natural numbers to either throw a runtime error when a negative is passed.
If the user forgets to follow this rule (or wont put in the extra effort to
add the check), their functions will have unspecified behaviour.

This ended up longer than I would like, sorry about that. I don't think
this issue has been covered here.
I'd be interested to hear responses and opinions, and while I'm confident
this change would be beneficial if done correctly, doing it correctly is
easier said that done.

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


(David Sweeris) #3

What about .count? Is it possible for that to be negative? If we change how count is represented, I think it should be switched to size_t, rather that UInt. I’m aware that they’re currently the same thing, but that might not always be the case, and, at least the way I understand things, the max value of a platform’s “pointer” type is more directly tied to the maximum possible element count than the max value of its native uint type. I know of at least one platform in development which uses 64 bits for pointers, but the address space is only 61 bits because the CPU & memory system use 3 bits for flags.

- Dave Sweeris

···

On Feb 3, 2016, at 11:58, David Turnbull via swift-evolution <swift-evolution@swift.org> wrote:

-1 CollectionType and it's ilk, contrary to what you believe, were designed to handle negative indexes just fine. Or even indexes that aren't numeric. And there's plenty of real use cases for this.

class GraphRow : MutableCollectionType {
    var values = [Int](count: 201, repeatedValue: 0)
    var startIndex: Int { return -100 }
    var endIndex: Int { return 100 }
    subscript(column: Int) -> Int {
        get {
            return values[column+100]
        }
        set {
            values[column+100] = newValue
        }
    }
}
var r = GraphRow()
r[-3] = 12
r[9] = 2
print(r[-3])
print(r.reduce(0, combine: +))

-david

On Wed, Feb 3, 2016 at 9:31 AM, James Froggatt via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
In the standard library, there are methods (notably array subscripts, CollectionType's .count and the dimensions of CGSize) which are designed to deal only with natural (non-negative) numbers. Functions either throw an error if a parameter is negative, or else ignore the possibility of a negative value finding its way into these functions.

By updating the standard library with a natural number type to represent these values (and potentially the Swift interfaces for Foundation and other frameworks), there is no way a negative number can be passed to these functions. This eliminates the need for many preconditions, allowing them to eliminate the possibility of a crash, and it would be clear that values such as count will never be negative.

If this change were accepted, the user would have to explicitly convert between the types at some points in their code. This could be seen as a burden, but requiring a cast encourages the programmer to check for negative values, keeping negatives out of natural number functions, and moving checks to the source of the data, similar to how Optionals eliminate nil values early on.

The parallel to Optionals is, in my opinion, the most appealing aspect of this idea. With Swift's focus on type safety, the lack of distinction between natural numbers and potentially negative ones seems like an omission, given how commonly both kinds of number are used. Enabling this level of integral safety would add additional clarity and reduce potential errors. Use cases include not just indexes, but in sizes such as collections' count and CG dimensions, for both clarity when getting values, and enforcing valid values at initialisation.

UInt is the obvious candidate to represent integer natural numbers, but is hazardous in edge cases: UInt allows an extra bit for magnitude, so has the possibility of overflow when converting back to Int. I assume this hazard is the primary reason UInt currently isn't used. If this a concern, a separate collection of types for natural integers (NInt?) could be created, which could be a strict subset of the correspondingly sized Int values (using n-1 bits).

This would mean adding a new collection of integer types in the standard library, which is undesirable. However, for high level applications which Swift is primarily used for, this would arguably be a more useful type than UInt, considering the near absence of UInt in current APIs, and its tiny increase (1 bit?) in precision compared to using an Int of a larger size. An unsigned Integer subset would allow safe conversion back to Int, and the conversion from Int is relatively simple, from the design perspective - ignore the sign bit, throw an error, or round up to 0, as appropriate.

Alternatives include:
• A generic wrapper for Int which constrains its possible values. This would allow inherent support for use with existing operators, but would be rather clunky in cases where only natural numbers are being dealt with.
• In a future version of Swift, the addition of type parameters constraining possible values may enable this to be added, but even if this does get added it may be too late to make such a major change.
• Leaving the APIs as they are and require every function dealing with natural numbers to either throw a runtime error when a negative is passed. If the user forgets to follow this rule (or wont put in the extra effort to add the check), their functions will have unspecified behaviour.

This ended up longer than I would like, sorry about that. I don't think this issue has been covered here.
I'd be interested to hear responses and opinions, and while I'm confident this change would be beneficial if done correctly, doing it correctly is easier said that done.

- James
_______________________________________________
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 Turnbull) #4

You can have a collection with a negative count. There's uses for this too.
For example, importing data. You can import whatever garbage in one pass,
and validate in a second. That way you don't have to mix concerns in your
code.

-david

···

On Wed, Feb 3, 2016 at 12:35 PM, <davesweeris@mac.com> wrote:

What about .count? Is it possible for that to be negative? *If* we change
how count is represented, I think it should be switched to size_t, rather
that UInt. I’m aware that they’re currently the same thing, but that might
not always be the case, and, at least the way I understand things, the max
value of a platform’s “pointer” type is more directly tied to the maximum
possible element count than the max value of its native uint type. I know
of at least one platform in development which uses 64 bits for pointers,
but the address space is only 61 bits because the CPU & memory system use 3
bits for flags.

- Dave Sweeris

On Feb 3, 2016, at 11:58, David Turnbull via swift-evolution < > swift-evolution@swift.org> wrote:

-1 CollectionType and it's ilk, contrary to what you believe, were
designed to handle negative indexes just fine. Or even indexes that aren't
numeric. And there's plenty of real use cases for this.

class GraphRow : MutableCollectionType {
    var values = [Int](count: 201, repeatedValue: 0)
    var startIndex: Int { return -100 }
    var endIndex: Int { return 100 }
    subscript(column: Int) -> Int {
        get {
            return values[column+100]
        }
        set {
            values[column+100] = newValue
        }
    }
}
var r = GraphRow()
r[-3] = 12
r[9] = 2
print(r[-3])
print(r.reduce(0, combine: +))

-david

On Wed, Feb 3, 2016 at 9:31 AM, James Froggatt via swift-evolution < > swift-evolution@swift.org> wrote:

In the standard library, there are methods (notably array subscripts,
CollectionType's .count and the dimensions of CGSize) which are designed to
deal only with natural (non-negative) numbers. Functions either throw an
error if a parameter is negative, or else ignore the possibility of a
negative value finding its way into these functions.

By updating the standard library with a natural number type to represent
these values (and potentially the Swift interfaces for Foundation and other
frameworks), there is no way a negative number can be passed to these
functions. This eliminates the need for many preconditions, allowing them
to eliminate the possibility of a crash, and it would be clear that values
such as count will never be negative.

If this change were accepted, the user would have to explicitly convert
between the types at some points in their code. This could be seen as a
burden, but requiring a cast encourages the programmer to check for
negative values, keeping negatives out of natural number functions, and
moving checks to the source of the data, similar to how Optionals eliminate
nil values early on.

The parallel to Optionals is, in my opinion, the most appealing aspect of
this idea. With Swift's focus on type safety, the lack of distinction
between natural numbers and potentially negative ones seems like an
omission, given how commonly both kinds of number are used. Enabling this
level of integral safety would add additional clarity and reduce potential
errors. Use cases include not just indexes, but in sizes such as
collections' count and CG dimensions, for both clarity when getting values,
and enforcing valid values at initialisation.

UInt is the obvious candidate to represent integer natural numbers, but
is hazardous in edge cases: UInt allows an extra bit for magnitude, so has
the possibility of overflow when converting back to Int. I assume this
hazard is the primary reason UInt currently isn't used. If this a concern,
a separate collection of types for natural integers (NInt?) could be
created, which could be a strict subset of the correspondingly sized Int
values (using n-1 bits).

This would mean adding a new collection of integer types in the standard
library, which is undesirable. However, for high level applications which
Swift is primarily used for, this would arguably be a more useful type than
UInt, considering the near absence of UInt in current APIs, and its tiny
increase (1 bit?) in precision compared to using an Int of a larger size.
An unsigned Integer subset would allow safe conversion back to Int, and the
conversion from Int is relatively simple, from the design perspective -
ignore the sign bit, throw an error, or round up to 0, as appropriate.

Alternatives include:
• A generic wrapper for Int which constrains its possible values. This
would allow inherent support for use with existing operators, but would be
rather clunky in cases where only natural numbers are being dealt with.
• In a future version of Swift, the addition of type parameters
constraining possible values may enable this to be added, but even if this
does get added it may be too late to make such a major change.
• Leaving the APIs as they are and require every function dealing with
natural numbers to either throw a runtime error when a negative is passed.
If the user forgets to follow this rule (or wont put in the extra effort to
add the check), their functions will have unspecified behaviour.

This ended up longer than I would like, sorry about that. I don't think
this issue has been covered here.
I'd be interested to hear responses and opinions, and while I'm confident
this change would be beneficial if done correctly, doing it correctly is
easier said that done.

- James
_______________________________________________
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


(David Sweeris) #5

Oooh… clever…

I’m not sure it really invalidates my point, though… In your example, the public “count” might return a negative number, but internally at some level your collection would still need a “count” for the purpose of knowing how many elements are in its buffer, right? I mean it’s not like that invalid data takes up a negative amount of memory…

- Dave Sweeris

···

On Feb 3, 2016, at 12:50, David Turnbull <dturnbull@gmail.com> wrote:

You can have a collection with a negative count. There's uses for this too. For example, importing data. You can import whatever garbage in one pass, and validate in a second. That way you don't have to mix concerns in your code.

-david

On Wed, Feb 3, 2016 at 12:35 PM, <davesweeris@mac.com <mailto:davesweeris@mac.com>> wrote:
What about .count? Is it possible for that to be negative? If we change how count is represented, I think it should be switched to size_t, rather that UInt. I’m aware that they’re currently the same thing, but that might not always be the case, and, at least the way I understand things, the max value of a platform’s “pointer” type is more directly tied to the maximum possible element count than the max value of its native uint type. I know of at least one platform in development which uses 64 bits for pointers, but the address space is only 61 bits because the CPU & memory system use 3 bits for flags.

- Dave Sweeris

On Feb 3, 2016, at 11:58, David Turnbull via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

-1 CollectionType and it's ilk, contrary to what you believe, were designed to handle negative indexes just fine. Or even indexes that aren't numeric. And there's plenty of real use cases for this.

class GraphRow : MutableCollectionType {
    var values = [Int](count: 201, repeatedValue: 0)
    var startIndex: Int { return -100 }
    var endIndex: Int { return 100 }
    subscript(column: Int) -> Int {
        get {
            return values[column+100]
        }
        set {
            values[column+100] = newValue
        }
    }
}
var r = GraphRow()
r[-3] = 12
r[9] = 2
print(r[-3])
print(r.reduce(0, combine: +))

-david

On Wed, Feb 3, 2016 at 9:31 AM, James Froggatt via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
In the standard library, there are methods (notably array subscripts, CollectionType's .count and the dimensions of CGSize) which are designed to deal only with natural (non-negative) numbers. Functions either throw an error if a parameter is negative, or else ignore the possibility of a negative value finding its way into these functions.

By updating the standard library with a natural number type to represent these values (and potentially the Swift interfaces for Foundation and other frameworks), there is no way a negative number can be passed to these functions. This eliminates the need for many preconditions, allowing them to eliminate the possibility of a crash, and it would be clear that values such as count will never be negative.

If this change were accepted, the user would have to explicitly convert between the types at some points in their code. This could be seen as a burden, but requiring a cast encourages the programmer to check for negative values, keeping negatives out of natural number functions, and moving checks to the source of the data, similar to how Optionals eliminate nil values early on.

The parallel to Optionals is, in my opinion, the most appealing aspect of this idea. With Swift's focus on type safety, the lack of distinction between natural numbers and potentially negative ones seems like an omission, given how commonly both kinds of number are used. Enabling this level of integral safety would add additional clarity and reduce potential errors. Use cases include not just indexes, but in sizes such as collections' count and CG dimensions, for both clarity when getting values, and enforcing valid values at initialisation.

UInt is the obvious candidate to represent integer natural numbers, but is hazardous in edge cases: UInt allows an extra bit for magnitude, so has the possibility of overflow when converting back to Int. I assume this hazard is the primary reason UInt currently isn't used. If this a concern, a separate collection of types for natural integers (NInt?) could be created, which could be a strict subset of the correspondingly sized Int values (using n-1 bits).

This would mean adding a new collection of integer types in the standard library, which is undesirable. However, for high level applications which Swift is primarily used for, this would arguably be a more useful type than UInt, considering the near absence of UInt in current APIs, and its tiny increase (1 bit?) in precision compared to using an Int of a larger size. An unsigned Integer subset would allow safe conversion back to Int, and the conversion from Int is relatively simple, from the design perspective - ignore the sign bit, throw an error, or round up to 0, as appropriate.

Alternatives include:
• A generic wrapper for Int which constrains its possible values. This would allow inherent support for use with existing operators, but would be rather clunky in cases where only natural numbers are being dealt with.
• In a future version of Swift, the addition of type parameters constraining possible values may enable this to be added, but even if this does get added it may be too late to make such a major change.
• Leaving the APIs as they are and require every function dealing with natural numbers to either throw a runtime error when a negative is passed. If the user forgets to follow this rule (or wont put in the extra effort to add the check), their functions will have unspecified behaviour.

This ended up longer than I would like, sorry about that. I don't think this issue has been covered here.
I'd be interested to hear responses and opinions, and while I'm confident this change would be beneficial if done correctly, doing it correctly is easier said that done.

- James
_______________________________________________
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


(Jonathan Tang) #6

In the standard library, there are methods (notably array subscripts,
CollectionType's .count and the dimensions of CGSize) which are designed to
deal only with natural (non-negative) numbers. Functions either throw an
error if a parameter is negative, or else ignore the possibility of a
negative value finding its way into these functions.

By updating the standard library with a natural number type to represent
these values (and potentially the Swift interfaces for Foundation and other
frameworks), there is no way a negative number can be passed to these
functions. This eliminates the need for many preconditions, allowing them
to eliminate the possibility of a crash, and it would be clear that values
such as count will never be negative.

How would you handle the types of arithmetic operations? Natural numbers
are not closed under subtraction or division; a natural number minus
another natural number may be an integer. There's no provision for
arithmetic operations to throw an error in Swift, so you might have to
silently eat the error in the subtraction operation, which defeats much of
the typesafety of having a separate type.

If this change were accepted, the user would have to explicitly convert
between the types at some points in their code. This could be seen as a
burden, but requiring a cast encourages the programmer to check for
negative values, keeping negatives out of natural number functions, and
moving checks to the source of the data, similar to how Optionals eliminate
nil values early on.

You could alleviate this somewhat by having natural numbers conform to
IntLiteralConvertible.

···

On Wed, Feb 3, 2016 at 9:31 AM, James Froggatt via swift-evolution < swift-evolution@swift.org> wrote:

The parallel to Optionals is, in my opinion, the most appealing aspect of
this idea. With Swift's focus on type safety, the lack of distinction
between natural numbers and potentially negative ones seems like an
omission, given how commonly both kinds of number are used. Enabling this
level of integral safety would add additional clarity and reduce potential
errors. Use cases include not just indexes, but in sizes such as
collections' count and CG dimensions, for both clarity when getting values,
and enforcing valid values at initialisation.

UInt is the obvious candidate to represent integer natural numbers, but is
hazardous in edge cases: UInt allows an extra bit for magnitude, so has the
possibility of overflow when converting back to Int. I assume this hazard
is the primary reason UInt currently isn't used. If this a concern, a
separate collection of types for natural integers (NInt?) could be created,
which could be a strict subset of the correspondingly sized Int values
(using n-1 bits).

This would mean adding a new collection of integer types in the standard
library, which is undesirable. However, for high level applications which
Swift is primarily used for, this would arguably be a more useful type than
UInt, considering the near absence of UInt in current APIs, and its tiny
increase (1 bit?) in precision compared to using an Int of a larger size.
An unsigned Integer subset would allow safe conversion back to Int, and the
conversion from Int is relatively simple, from the design perspective -
ignore the sign bit, throw an error, or round up to 0, as appropriate.

Alternatives include:
• A generic wrapper for Int which constrains its possible values. This
would allow inherent support for use with existing operators, but would be
rather clunky in cases where only natural numbers are being dealt with.
• In a future version of Swift, the addition of type parameters
constraining possible values may enable this to be added, but even if this
does get added it may be too late to make such a major change.
• Leaving the APIs as they are and require every function dealing with
natural numbers to either throw a runtime error when a negative is passed.
If the user forgets to follow this rule (or wont put in the extra effort to
add the check), their functions will have unspecified behaviour.

This ended up longer than I would like, sorry about that. I don't think
this issue has been covered here.
I'd be interested to hear responses and opinions, and while I'm confident
this change would be beneficial if done correctly, doing it correctly is
easier said that done.

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


(Dave Abrahams) #7

You can have a collection with a negative count. There's uses for this too.
For example, importing data. You can import whatever garbage in one pass,
and validate in a second. That way you don't have to mix concerns in your
code.

I think the semantic requirements on CollectionType's count prevent it
from ever being negative. If they don't, they should.

···

on Wed Feb 03 2016, David Turnbull <swift-evolution@swift.org> wrote:

-david

On Wed, Feb 3, 2016 at 12:35 PM, <davesweeris@mac.com> wrote:

What about .count? Is it possible for that to be negative? *If* we change
how count is represented, I think it should be switched to size_t, rather
that UInt. I’m aware that they’re currently the same thing, but that might
not always be the case, and, at least the way I understand things, the max
value of a platform’s “pointer” type is more directly tied to the maximum
possible element count than the max value of its native uint type. I know
of at least one platform in development which uses 64 bits for pointers,
but the address space is only 61 bits because the CPU & memory system use 3
bits for flags.

- Dave Sweeris

On Feb 3, 2016, at 11:58, David Turnbull via swift-evolution < >> swift-evolution@swift.org> wrote:

-1 CollectionType and it's ilk, contrary to what you believe, were
designed to handle negative indexes just fine. Or even indexes that aren't
numeric. And there's plenty of real use cases for this.

class GraphRow : MutableCollectionType {
    var values = [Int](count: 201, repeatedValue: 0)
    var startIndex: Int { return -100 }
    var endIndex: Int { return 100 }
    subscript(column: Int) -> Int {
        get {
            return values[column+100]
        }
        set {
            values[column+100] = newValue
        }
    }
}
var r = GraphRow()
r[-3] = 12
r[9] = 2
print(r[-3])
print(r.reduce(0, combine: +))

-david

On Wed, Feb 3, 2016 at 9:31 AM, James Froggatt via swift-evolution < >> swift-evolution@swift.org> wrote:

In the standard library, there are methods (notably array subscripts,
CollectionType's .count and the dimensions of CGSize) which are designed to
deal only with natural (non-negative) numbers. Functions either throw an
error if a parameter is negative, or else ignore the possibility of a
negative value finding its way into these functions.

By updating the standard library with a natural number type to represent
these values (and potentially the Swift interfaces for Foundation and other
frameworks), there is no way a negative number can be passed to these
functions. This eliminates the need for many preconditions, allowing them
to eliminate the possibility of a crash, and it would be clear that values
such as count will never be negative.

If this change were accepted, the user would have to explicitly convert
between the types at some points in their code. This could be seen as a
burden, but requiring a cast encourages the programmer to check for
negative values, keeping negatives out of natural number functions, and
moving checks to the source of the data, similar to how Optionals eliminate
nil values early on.

The parallel to Optionals is, in my opinion, the most appealing aspect of
this idea. With Swift's focus on type safety, the lack of distinction
between natural numbers and potentially negative ones seems like an
omission, given how commonly both kinds of number are used. Enabling this
level of integral safety would add additional clarity and reduce potential
errors. Use cases include not just indexes, but in sizes such as
collections' count and CG dimensions, for both clarity when getting values,
and enforcing valid values at initialisation.

UInt is the obvious candidate to represent integer natural numbers, but
is hazardous in edge cases: UInt allows an extra bit for magnitude, so has
the possibility of overflow when converting back to Int. I assume this
hazard is the primary reason UInt currently isn't used. If this a concern,
a separate collection of types for natural integers (NInt?) could be
created, which could be a strict subset of the correspondingly sized Int
values (using n-1 bits).

This would mean adding a new collection of integer types in the standard
library, which is undesirable. However, for high level applications which
Swift is primarily used for, this would arguably be a more useful type than
UInt, considering the near absence of UInt in current APIs, and its tiny
increase (1 bit?) in precision compared to using an Int of a larger size.
An unsigned Integer subset would allow safe conversion back to Int, and the
conversion from Int is relatively simple, from the design perspective -
ignore the sign bit, throw an error, or round up to 0, as appropriate.

Alternatives include:
• A generic wrapper for Int which constrains its possible values. This
would allow inherent support for use with existing operators, but would be
rather clunky in cases where only natural numbers are being dealt with.
• In a future version of Swift, the addition of type parameters
constraining possible values may enable this to be added, but even if this
does get added it may be too late to make such a major change.
• Leaving the APIs as they are and require every function dealing with
natural numbers to either throw a runtime error when a negative is passed.
If the user forgets to follow this rule (or wont put in the extra effort to
add the check), their functions will have unspecified behaviour.

This ended up longer than I would like, sorry about that. I don't think
this issue has been covered here.
I'd be interested to hear responses and opinions, and while I'm confident
this change would be beneficial if done correctly, doing it correctly is
easier said that done.

- James
_______________________________________________
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

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

--
-Dave


(David Turnbull) #8

Only when you actually try to iterate will it fail. It's useful to have
this invalid state where start>end. You might have one thread finding
minima and another finding maxima. That's how it is today anyways. Which I
think is nice.

-david

···

On Wed, Feb 3, 2016 at 4:35 PM, Dave Abrahams via swift-evolution < swift-evolution@swift.org> wrote:

I think the semantic requirements on CollectionType's count prevent it
from ever being negative. If they don't, they should.


(James F) #9

I specifically referred to the subscript of Array, not all CollectionTypes. The fact is Array's specific implementation of CollectionType implements a zero-based index, yet exposes its subscript as a signed integer.

As for .count, there is no way for it to be negative in CollectionType's default implementation, and don't see any realistic situation in which an implementing type would override it to allow negative values. If one did, I'm not sure it should conform to CollectionType to begin with. I expect there are many occurrences of 0 ..< collection.count being used in functions operating on collections without first checking count is not negative, because why would it be?

The problem is, currently any type implementing CollectionType could do this, so when creating generic functions, any cautious programmer would have to account for it. The only sensible thing to do in that situation, that I can see, is throw an error, which suggests count is in fact unsafe.

I am unfamiliar with size_t, but a type that could act as an equivalent of the CGFloat typealias for unsigned integers seems a good idea. However, given size_t goes against Swift type naming conventions (I assume it exists for C interop?), it seems like it would be preferable to create a separate typealias for this purpose.

From James F

···

On 3 Feb 2016, at 20:35, davesweeris@mac.com wrote:

What about .count? Is it possible for that to be negative? If we change how count is represented, I think it should be switched to size_t, rather that UInt. I’m aware that they’re currently the same thing, but that might not always be the case, and, at least the way I understand things, the max value of a platform’s “pointer” type is more directly tied to the maximum possible element count than the max value of its native uint type. I know of at least one platform in development which uses 64 bits for pointers, but the address space is only 61 bits because the CPU & memory system use 3 bits for flags.

- Dave Sweeris

On Feb 3, 2016, at 11:58, David Turnbull via swift-evolution <swift-evolution@swift.org> wrote:

-1 CollectionType and it's ilk, contrary to what you believe, were designed to handle negative indexes just fine. Or even indexes that aren't numeric. And there's plenty of real use cases for this.

class GraphRow : MutableCollectionType {
    var values = [Int](count: 201, repeatedValue: 0)
    var startIndex: Int { return -100 }
    var endIndex: Int { return 100 }
    subscript(column: Int) -> Int {
        get {
            return values[column+100]
        }
        set {
            values[column+100] = newValue
        }
    }
}
var r = GraphRow()
r[-3] = 12
r[9] = 2
print(r[-3])
print(r.reduce(0, combine: +))

-david

On Wed, Feb 3, 2016 at 9:31 AM, James Froggatt via swift-evolution <swift-evolution@swift.org> wrote:
In the standard library, there are methods (notably array subscripts, CollectionType's .count and the dimensions of CGSize) which are designed to deal only with natural (non-negative) numbers. Functions either throw an error if a parameter is negative, or else ignore the possibility of a negative value finding its way into these functions.

By updating the standard library with a natural number type to represent these values (and potentially the Swift interfaces for Foundation and other frameworks), there is no way a negative number can be passed to these functions. This eliminates the need for many preconditions, allowing them to eliminate the possibility of a crash, and it would be clear that values such as count will never be negative.

If this change were accepted, the user would have to explicitly convert between the types at some points in their code. This could be seen as a burden, but requiring a cast encourages the programmer to check for negative values, keeping negatives out of natural number functions, and moving checks to the source of the data, similar to how Optionals eliminate nil values early on.

The parallel to Optionals is, in my opinion, the most appealing aspect of this idea. With Swift's focus on type safety, the lack of distinction between natural numbers and potentially negative ones seems like an omission, given how commonly both kinds of number are used. Enabling this level of integral safety would add additional clarity and reduce potential errors. Use cases include not just indexes, but in sizes such as collections' count and CG dimensions, for both clarity when getting values, and enforcing valid values at initialisation.

UInt is the obvious candidate to represent integer natural numbers, but is hazardous in edge cases: UInt allows an extra bit for magnitude, so has the possibility of overflow when converting back to Int. I assume this hazard is the primary reason UInt currently isn't used. If this a concern, a separate collection of types for natural integers (NInt?) could be created, which could be a strict subset of the correspondingly sized Int values (using n-1 bits).

This would mean adding a new collection of integer types in the standard library, which is undesirable. However, for high level applications which Swift is primarily used for, this would arguably be a more useful type than UInt, considering the near absence of UInt in current APIs, and its tiny increase (1 bit?) in precision compared to using an Int of a larger size. An unsigned Integer subset would allow safe conversion back to Int, and the conversion from Int is relatively simple, from the design perspective - ignore the sign bit, throw an error, or round up to 0, as appropriate.

Alternatives include:
• A generic wrapper for Int which constrains its possible values. This would allow inherent support for use with existing operators, but would be rather clunky in cases where only natural numbers are being dealt with.
• In a future version of Swift, the addition of type parameters constraining possible values may enable this to be added, but even if this does get added it may be too late to make such a major change.
• Leaving the APIs as they are and require every function dealing with natural numbers to either throw a runtime error when a negative is passed. If the user forgets to follow this rule (or wont put in the extra effort to add the check), their functions will have unspecified behaviour.

This ended up longer than I would like, sorry about that. I don't think this issue has been covered here.
I'd be interested to hear responses and opinions, and while I'm confident this change would be beneficial if done correctly, doing it correctly is easier said that done.

- James
_______________________________________________
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


(James F) #10

I'm not sure I follow. Could you elaborate on this?

If such a type were made, I'm fairly sure generic functions designed to accept a CollectionType would throw an error if the count were negative (if trying to iterate with 0 ..< .count, for example), indicating such a type doesn't implement the protocol as it was intended to be used.

From James F

···

On 3 Feb 2016, at 20:50, David Turnbull <dturnbull@gmail.com> wrote:

You can have a collection with a negative count. There's uses for this too. For example, importing data. You can import whatever garbage in one pass, and validate in a second. That way you don't have to mix concerns in your code.

-david

On Wed, Feb 3, 2016 at 12:35 PM, <davesweeris@mac.com> wrote:
What about .count? Is it possible for that to be negative? If we change how count is represented, I think it should be switched to size_t, rather that UInt. I’m aware that they’re currently the same thing, but that might not always be the case, and, at least the way I understand things, the max value of a platform’s “pointer” type is more directly tied to the maximum possible element count than the max value of its native uint type. I know of at least one platform in development which uses 64 bits for pointers, but the address space is only 61 bits because the CPU & memory system use 3 bits for flags.

- Dave Sweeris

On Feb 3, 2016, at 11:58, David Turnbull via swift-evolution <swift-evolution@swift.org> wrote:

-1 CollectionType and it's ilk, contrary to what you believe, were designed to handle negative indexes just fine. Or even indexes that aren't numeric. And there's plenty of real use cases for this.

class GraphRow : MutableCollectionType {
    var values = [Int](count: 201, repeatedValue: 0)
    var startIndex: Int { return -100 }
    var endIndex: Int { return 100 }
    subscript(column: Int) -> Int {
        get {
            return values[column+100]
        }
        set {
            values[column+100] = newValue
        }
    }
}
var r = GraphRow()
r[-3] = 12
r[9] = 2
print(r[-3])
print(r.reduce(0, combine: +))

-david

On Wed, Feb 3, 2016 at 9:31 AM, James Froggatt via swift-evolution <swift-evolution@swift.org> wrote:
In the standard library, there are methods (notably array subscripts, CollectionType's .count and the dimensions of CGSize) which are designed to deal only with natural (non-negative) numbers. Functions either throw an error if a parameter is negative, or else ignore the possibility of a negative value finding its way into these functions.

By updating the standard library with a natural number type to represent these values (and potentially the Swift interfaces for Foundation and other frameworks), there is no way a negative number can be passed to these functions. This eliminates the need for many preconditions, allowing them to eliminate the possibility of a crash, and it would be clear that values such as count will never be negative.

If this change were accepted, the user would have to explicitly convert between the types at some points in their code. This could be seen as a burden, but requiring a cast encourages the programmer to check for negative values, keeping negatives out of natural number functions, and moving checks to the source of the data, similar to how Optionals eliminate nil values early on.

The parallel to Optionals is, in my opinion, the most appealing aspect of this idea. With Swift's focus on type safety, the lack of distinction between natural numbers and potentially negative ones seems like an omission, given how commonly both kinds of number are used. Enabling this level of integral safety would add additional clarity and reduce potential errors. Use cases include not just indexes, but in sizes such as collections' count and CG dimensions, for both clarity when getting values, and enforcing valid values at initialisation.

UInt is the obvious candidate to represent integer natural numbers, but is hazardous in edge cases: UInt allows an extra bit for magnitude, so has the possibility of overflow when converting back to Int. I assume this hazard is the primary reason UInt currently isn't used. If this a concern, a separate collection of types for natural integers (NInt?) could be created, which could be a strict subset of the correspondingly sized Int values (using n-1 bits).

This would mean adding a new collection of integer types in the standard library, which is undesirable. However, for high level applications which Swift is primarily used for, this would arguably be a more useful type than UInt, considering the near absence of UInt in current APIs, and its tiny increase (1 bit?) in precision compared to using an Int of a larger size. An unsigned Integer subset would allow safe conversion back to Int, and the conversion from Int is relatively simple, from the design perspective - ignore the sign bit, throw an error, or round up to 0, as appropriate.

Alternatives include:
• A generic wrapper for Int which constrains its possible values. This would allow inherent support for use with existing operators, but would be rather clunky in cases where only natural numbers are being dealt with.
• In a future version of Swift, the addition of type parameters constraining possible values may enable this to be added, but even if this does get added it may be too late to make such a major change.
• Leaving the APIs as they are and require every function dealing with natural numbers to either throw a runtime error when a negative is passed. If the user forgets to follow this rule (or wont put in the extra effort to add the check), their functions will have unspecified behaviour.

This ended up longer than I would like, sorry about that. I don't think this issue has been covered here.
I'd be interested to hear responses and opinions, and while I'm confident this change would be beneficial if done correctly, doing it correctly is easier said that done.

- James
_______________________________________________
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


(James F) #11

That is an interesting point. Based on how the operators are defined for Int:

‘Unlike the arithmetic operators in C and Objective-C, the Swift arithmetic operators do not allow values to overflow by default. You can opt in to value overflow behavior by using Swift’s overflow operators (such as a &+ b)’

https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/BasicOperators.html

The former statement, I believe, refers to the behaviour of throwing a runtime error. The solution would be the same as for checking before using an Array subscript (as currently implemented): explicit comparisons before performing the potentially crashing operation.

A regular signed integer already has the hazard of a ÷0 error. I think it would make sense to throw errors readily for subtraction - if the user would have to perform a check beforehand or face the consequences, just as they do for index out of bounds errors with Arrays. This is is currently how UInt’s subtract implementation works, and I think the best option is to match this behaviour.

···

On 3 Feb 2016, at 21:51, Jonathan Tang jonathan.d.tang@gmail.com wrote:

On Wed, Feb 3, 2016 at 9:31 AM, James Froggatt via swift-evolution swift-evolution@swift.org wrote:

In the standard library, there are methods (notably array subscripts, CollectionType’s .count and the dimensions of CGSize) which are designed to deal only with natural (non-negative) numbers. Functions either throw an error if a parameter is negative, or else ignore the possibility of a negative value finding its way into these functions.

By updating the standard library with a natural number type to represent these values (and potentially the Swift interfaces for Foundation and other frameworks), there is no way a negative number can be passed to these functions. This eliminates the need for many preconditions, allowing them to eliminate the possibility of a crash, and it would be clear that values such as count will never be negative.

How would you handle the types of arithmetic operations? Natural numbers are not closed under subtraction or division; a natural number minus another natural number may be an integer. There’s no provision for arithmetic operations to throw an error in Swift, so you might have to silently eat the error in the subtraction operation, which defeats much of the typesafety of having a separate type.


(Dave Abrahams) #12

I think the semantic requirements on CollectionType's count prevent it
from ever being negative. If they don't, they should.

Only when you actually try to iterate will it fail.

That's not what I'm saying.

  /// Returns the number of elements.
  /// ...
  var count: Index.Distance { get }

Awkward wording aside, this says that count is the number of elements in
the collection. Once you have that, it's automatically non-negative.
There's no way for a collection to hold less than zero elements.

It's useful to have this invalid state where start>end. You might have
one thread finding minima and another finding maxima. That's how it is
today anyways. Which I think is nice.

I have no idea what you mean here, sorry.

···

on Wed Feb 03 2016, David Turnbull <dturnbull-AT-gmail.com> wrote:

On Wed, Feb 3, 2016 at 4:35 PM, Dave Abrahams via swift-evolution < > swift-evolution@swift.org> wrote:

--
-Dave


(David Sweeris) #13

Dunno, but unless you’re still specifically talking about arrays, why assume that it starts at 0?

I expect there are many occurrences of 0 ..< collection.count being used in functions operating on collections without first checking count is not negative, because why would it be?

IIRC, size_t is C’s pointer type, and as such, it’s maximum value is also the maximum # of bytes the system can address. Since the smallest data type in C is a byte, it’s always big enough to hold the number of elements in an array (which is why I used it). I think on most platforms it’s just typedefed to uint_64t (or uint_32t on 32-bit platforms). I don’t know if Swift is using it for anything behind the scenes, but I believe you are correct in that you’d only come across it in production code for interoperating with C code. Last time I checked, in Swift it was typealiased to UInt.

- Dave Sweeris

···

On Feb 3, 2016, at 13:03, James Froggatt <conductator@ntlworld.com> wrote:

I am unfamiliar with size_t, but a type that could act as an equivalent of the CGFloat typealias for unsigned integers seems a good idea. However, given size_t goes against Swift type naming conventions (I assume it exists for C interop?), it seems like it would be preferable to create a separate typealias for this purpose.

From James F

On 3 Feb 2016, at 20:35, davesweeris@mac.com <mailto:davesweeris@mac.com> wrote:

What about .count? Is it possible for that to be negative? If we change how count is represented, I think it should be switched to size_t, rather that UInt. I’m aware that they’re currently the same thing, but that might not always be the case, and, at least the way I understand things, the max value of a platform’s “pointer” type is more directly tied to the maximum possible element count than the max value of its native uint type. I know of at least one platform in development which uses 64 bits for pointers, but the address space is only 61 bits because the CPU & memory system use 3 bits for flags.

- Dave Sweeris

On Feb 3, 2016, at 11:58, David Turnbull via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

-1 CollectionType and it's ilk, contrary to what you believe, were designed to handle negative indexes just fine. Or even indexes that aren't numeric. And there's plenty of real use cases for this.

class GraphRow : MutableCollectionType {
    var values = [Int](count: 201, repeatedValue: 0)
    var startIndex: Int { return -100 }
    var endIndex: Int { return 100 }
    subscript(column: Int) -> Int {
        get {
            return values[column+100]
        }
        set {
            values[column+100] = newValue
        }
    }
}
var r = GraphRow()
r[-3] = 12
r[9] = 2
print(r[-3])
print(r.reduce(0, combine: +))

-david

On Wed, Feb 3, 2016 at 9:31 AM, James Froggatt via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
In the standard library, there are methods (notably array subscripts, CollectionType's .count and the dimensions of CGSize) which are designed to deal only with natural (non-negative) numbers. Functions either throw an error if a parameter is negative, or else ignore the possibility of a negative value finding its way into these functions.

By updating the standard library with a natural number type to represent these values (and potentially the Swift interfaces for Foundation and other frameworks), there is no way a negative number can be passed to these functions. This eliminates the need for many preconditions, allowing them to eliminate the possibility of a crash, and it would be clear that values such as count will never be negative.

If this change were accepted, the user would have to explicitly convert between the types at some points in their code. This could be seen as a burden, but requiring a cast encourages the programmer to check for negative values, keeping negatives out of natural number functions, and moving checks to the source of the data, similar to how Optionals eliminate nil values early on.

The parallel to Optionals is, in my opinion, the most appealing aspect of this idea. With Swift's focus on type safety, the lack of distinction between natural numbers and potentially negative ones seems like an omission, given how commonly both kinds of number are used. Enabling this level of integral safety would add additional clarity and reduce potential errors. Use cases include not just indexes, but in sizes such as collections' count and CG dimensions, for both clarity when getting values, and enforcing valid values at initialisation.

UInt is the obvious candidate to represent integer natural numbers, but is hazardous in edge cases: UInt allows an extra bit for magnitude, so has the possibility of overflow when converting back to Int. I assume this hazard is the primary reason UInt currently isn't used. If this a concern, a separate collection of types for natural integers (NInt?) could be created, which could be a strict subset of the correspondingly sized Int values (using n-1 bits).

This would mean adding a new collection of integer types in the standard library, which is undesirable. However, for high level applications which Swift is primarily used for, this would arguably be a more useful type than UInt, considering the near absence of UInt in current APIs, and its tiny increase (1 bit?) in precision compared to using an Int of a larger size. An unsigned Integer subset would allow safe conversion back to Int, and the conversion from Int is relatively simple, from the design perspective - ignore the sign bit, throw an error, or round up to 0, as appropriate.

Alternatives include:
• A generic wrapper for Int which constrains its possible values. This would allow inherent support for use with existing operators, but would be rather clunky in cases where only natural numbers are being dealt with.
• In a future version of Swift, the addition of type parameters constraining possible values may enable this to be added, but even if this does get added it may be too late to make such a major change.
• Leaving the APIs as they are and require every function dealing with natural numbers to either throw a runtime error when a negative is passed. If the user forgets to follow this rule (or wont put in the extra effort to add the check), their functions will have unspecified behaviour.

This ended up longer than I would like, sorry about that. I don't think this issue has been covered here.
I'd be interested to hear responses and opinions, and while I'm confident this change would be beneficial if done correctly, doing it correctly is easier said that done.

- James
_______________________________________________
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


(Jonathan Tang) #14

That is an interesting point. Based on how the operators are defined for
Int:

‘Unlike the arithmetic operators in C and Objective-C, the Swift
arithmetic operators do not allow values to overflow by default. You can
opt in to value overflow behavior by using Swift’s overflow operators (such
as a &+ b)’

https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/BasicOperators.html

The former statement, I believe, refers to the behaviour of throwing a
runtime error. The solution would be the same as for checking before using
an Array subscript (as currently implemented): explicit comparisons before
performing the potentially crashing operation.

A regular signed integer already has the hazard of a ÷0 error. I think it
would make sense to throw errors readily for subtraction - if the user
would have to perform a check beforehand or face the consequences, just as
they do for index out of bounds errors with Arrays. This is is currently
how UInt's subtract implementation works, and I think the best option is to
match this behaviour.

We'd be largely in the same position we are in now w.r.t. array accesses.
You still need a runtime check and you can still cause a crash, but the
check has been moved from the array subscript to any subtraction on array
indices. As with divide-by-zero, it's the responsibility of the calling
algorithm to make sure this never happens.

I think I'm -1 on this. Swift has for-each loops already that eliminate
the vast majority of explicit array accesses. For ones with a computed
index, you'd still need a check for <0 on the computation. Literal indices
aren't much of a risk anyway - it's pretty obvious when you're subscripting
with a literal negative number - but as others have pointed out, there are
sometimes legit reasons why you might want a negative subscript or count
property.

···

On Wed, Feb 3, 2016 at 2:17 PM, James Samuel Froggatt < conductator@ntlworld.com> wrote:

On 3 Feb 2016, at 21:51, Jonathan Tang <jonathan.d.tang@gmail.com> wrote:

On Wed, Feb 3, 2016 at 9:31 AM, James Froggatt via swift-evolution < > swift-evolution@swift.org> wrote:

In the standard library, there are methods (notably array subscripts,
CollectionType's .count and the dimensions of CGSize) which are designed to
deal only with natural (non-negative) numbers. Functions either throw an
error if a parameter is negative, or else ignore the possibility of a
negative value finding its way into these functions.

By updating the standard library with a natural number type to represent
these values (and potentially the Swift interfaces for Foundation and other
frameworks), there is no way a negative number can be passed to these
functions. This eliminates the need for many preconditions, allowing them
to eliminate the possibility of a crash, and it would be clear that values
such as count will never be negative.

How would you handle the types of arithmetic operations? Natural numbers
are not closed under subtraction or division; a natural number minus
another natural number may be an integer. There's no provision for
arithmetic operations to throw an error in Swift, so you might have to
silently eat the error in the subtraction operation, which defeats much of
the typesafety of having a separate type.


(David Turnbull) #15

Here's how you could use a collection as a predicate. The OnSwitchesIndex is
opaque to anything outside the file. There's no way to iterate OnSwitches with
0 ..<count. Thinking that arrays and sequences are counting up from 0 by 1
is missing the entire point of these protocols.

class Switches : CollectionType{

    private let data:[Bool]

    init(_ d:[Bool]) {

        data = d

    }

    var startIndex: Int {

        return data.startIndex

    }

    var endIndex: Int {

        return data.endIndex

    }

    subscript(i:Int) -> Bool {

        return data[i]

    }

}

struct OnSwitchesIndex : ForwardIndexType {

    private let data:Switches

    private let index:Int

    init(_ d:Switches, _ i:Int) {

        data = d

        for i in i..<data.endIndex {

            if data[i] {

                index = i

                return

            }

        }

        index = data.endIndex

    }

    func successor() -> OnSwitchesIndex {

        return OnSwitchesIndex(data, index+1)

    }

}

func ==(lhs: OnSwitchesIndex, rhs: OnSwitchesIndex) -> Bool {

    return lhs.index == rhs.index

}

class OnSwitches : CollectionType {

    let data:Switches

    init(_ d:Switches) {

        data = d

    }

    var startIndex: OnSwitchesIndex {

        return OnSwitchesIndex(data, data.startIndex)

    }

    var endIndex: OnSwitchesIndex {

        return OnSwitchesIndex(data, data.endIndex)

    }

    subscript(sw: OnSwitchesIndex) -> Int {

        assert(sw.data.data == data.data)

        return sw.index

    }

}

let x:Switches = Switches([false, true, true, false, true])

x.count // == 5

let y = OnSwitches(x)

y.count // == 3

for z in y { print(z) } // == prints 1 2 4

-david

···

On Wed, Feb 3, 2016 at 1:09 PM, James Froggatt <conductator@ntlworld.com> wrote:

I'm not sure I follow. Could you elaborate on this?

If such a type were made, I'm fairly sure generic functions designed to
accept a CollectionType would throw an error if the count were negative (if
trying to iterate with 0 ..< .count, for example), indicating such a type
doesn't implement the protocol as it was intended to be used.

From James F

On 3 Feb 2016, at 20:50, David Turnbull <dturnbull@gmail.com> wrote:

You can have a collection with a negative count. There's uses for this
too. For example, importing data. You can import whatever garbage in one
pass, and validate in a second. That way you don't have to mix concerns in
your code.

-david

On Wed, Feb 3, 2016 at 12:35 PM, <davesweeris@mac.com> wrote:

What about .count? Is it possible for that to be negative? *If* we
change how count is represented, I think it should be switched to size_t,
rather that UInt. I’m aware that they’re currently the same thing, but that
might not always be the case, and, at least the way I understand things,
the max value of a platform’s “pointer” type is more directly tied to the
maximum possible element count than the max value of its native uint type.
I know of at least one platform in development which uses 64 bits for
pointers, but the address space is only 61 bits because the CPU & memory
system use 3 bits for flags.

- Dave Sweeris

On Feb 3, 2016, at 11:58, David Turnbull via swift-evolution < >> swift-evolution@swift.org> wrote:

-1 CollectionType and it's ilk, contrary to what you believe, were
designed to handle negative indexes just fine. Or even indexes that aren't
numeric. And there's plenty of real use cases for this.

class GraphRow : MutableCollectionType {
    var values = [Int](count: 201, repeatedValue: 0)
    var startIndex: Int { return -100 }
    var endIndex: Int { return 100 }
    subscript(column: Int) -> Int {
        get {
            return values[column+100]
        }
        set {
            values[column+100] = newValue
        }
    }
}
var r = GraphRow()
r[-3] = 12
r[9] = 2
print(r[-3])
print(r.reduce(0, combine: +))

-david

On Wed, Feb 3, 2016 at 9:31 AM, James Froggatt via swift-evolution < >> swift-evolution@swift.org> wrote:

In the standard library, there are methods (notably array subscripts,
CollectionType's .count and the dimensions of CGSize) which are designed to
deal only with natural (non-negative) numbers. Functions either throw an
error if a parameter is negative, or else ignore the possibility of a
negative value finding its way into these functions.

By updating the standard library with a natural number type to represent
these values (and potentially the Swift interfaces for Foundation and other
frameworks), there is no way a negative number can be passed to these
functions. This eliminates the need for many preconditions, allowing them
to eliminate the possibility of a crash, and it would be clear that values
such as count will never be negative.

If this change were accepted, the user would have to explicitly convert
between the types at some points in their code. This could be seen as a
burden, but requiring a cast encourages the programmer to check for
negative values, keeping negatives out of natural number functions, and
moving checks to the source of the data, similar to how Optionals eliminate
nil values early on.

The parallel to Optionals is, in my opinion, the most appealing aspect
of this idea. With Swift's focus on type safety, the lack of distinction
between natural numbers and potentially negative ones seems like an
omission, given how commonly both kinds of number are used. Enabling this
level of integral safety would add additional clarity and reduce potential
errors. Use cases include not just indexes, but in sizes such as
collections' count and CG dimensions, for both clarity when getting values,
and enforcing valid values at initialisation.

UInt is the obvious candidate to represent integer natural numbers, but
is hazardous in edge cases: UInt allows an extra bit for magnitude, so has
the possibility of overflow when converting back to Int. I assume this
hazard is the primary reason UInt currently isn't used. If this a concern,
a separate collection of types for natural integers (NInt?) could be
created, which could be a strict subset of the correspondingly sized Int
values (using n-1 bits).

This would mean adding a new collection of integer types in the standard
library, which is undesirable. However, for high level applications which
Swift is primarily used for, this would arguably be a more useful type than
UInt, considering the near absence of UInt in current APIs, and its tiny
increase (1 bit?) in precision compared to using an Int of a larger size.
An unsigned Integer subset would allow safe conversion back to Int, and the
conversion from Int is relatively simple, from the design perspective -
ignore the sign bit, throw an error, or round up to 0, as appropriate.

Alternatives include:
• A generic wrapper for Int which constrains its possible values. This
would allow inherent support for use with existing operators, but would be
rather clunky in cases where only natural numbers are being dealt with.
• In a future version of Swift, the addition of type parameters
constraining possible values may enable this to be added, but even if this
does get added it may be too late to make such a major change.
• Leaving the APIs as they are and require every function dealing with
natural numbers to either throw a runtime error when a negative is passed.
If the user forgets to follow this rule (or wont put in the extra effort to
add the check), their functions will have unspecified behaviour.

This ended up longer than I would like, sorry about that. I don't think
this issue has been covered here.
I'd be interested to hear responses and opinions, and while I'm
confident this change would be beneficial if done correctly, doing it
correctly is easier said that done.

- James
_______________________________________________
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


(James F) #16

I would assume count represents the number of elements in the collection, in which case the lowest possible value would be 0.

Admittedly, the loop would only be useful for CollectionTypes with zero indexed arrays, however that just leads back to the issue that while:
T: CollectionType where T.Index == Int
is valid, there is no guarantee that the collection is zero-indexed, which is useful information to have to constrain to Array-like collections.

From James F

···

On 3 Feb 2016, at 21:26, davesweeris@mac.com wrote:

Dunno, but unless you’re still specifically talking about arrays, why assume that it starts at 0?

On Feb 3, 2016, at 13:03, James Froggatt <conductator@ntlworld.com> wrote:

I expect there are many occurrences of 0 ..< collection.count being used in functions operating on collections without first checking count is not negative, because why would it be?

IIRC, size_t is C’s pointer type, and as such, it’s maximum value is also the maximum # of bytes the system can address. Since the smallest data type in C is a byte, it’s always big enough to hold the number of elements in an array (which is why I used it). I think on most platforms it’s just typedefed to uint_64t (or uint_32t on 32-bit platforms). I don’t know if Swift is using it for anything behind the scenes, but I believe you are correct in that you’d only come across it in production code for interoperating with C code. Last time I checked, in Swift it was typealiased to UInt.

- Dave Sweeris

I am unfamiliar with size_t, but a type that could act as an equivalent of the CGFloat typealias for unsigned integers seems a good idea. However, given size_t goes against Swift type naming conventions (I assume it exists for C interop?), it seems like it would be preferable to create a separate typealias for this purpose.

From James F

On 3 Feb 2016, at 20:35, davesweeris@mac.com wrote:

What about .count? Is it possible for that to be negative? If we change how count is represented, I think it should be switched to size_t, rather that UInt. I’m aware that they’re currently the same thing, but that might not always be the case, and, at least the way I understand things, the max value of a platform’s “pointer” type is more directly tied to the maximum possible element count than the max value of its native uint type. I know of at least one platform in development which uses 64 bits for pointers, but the address space is only 61 bits because the CPU & memory system use 3 bits for flags.

- Dave Sweeris

On Feb 3, 2016, at 11:58, David Turnbull via swift-evolution <swift-evolution@swift.org> wrote:

-1 CollectionType and it's ilk, contrary to what you believe, were designed to handle negative indexes just fine. Or even indexes that aren't numeric. And there's plenty of real use cases for this.

class GraphRow : MutableCollectionType {
    var values = [Int](count: 201, repeatedValue: 0)
    var startIndex: Int { return -100 }
    var endIndex: Int { return 100 }
    subscript(column: Int) -> Int {
        get {
            return values[column+100]
        }
        set {
            values[column+100] = newValue
        }
    }
}
var r = GraphRow()
r[-3] = 12
r[9] = 2
print(r[-3])
print(r.reduce(0, combine: +))

-david

On Wed, Feb 3, 2016 at 9:31 AM, James Froggatt via swift-evolution <swift-evolution@swift.org> wrote:
In the standard library, there are methods (notably array subscripts, CollectionType's .count and the dimensions of CGSize) which are designed to deal only with natural (non-negative) numbers. Functions either throw an error if a parameter is negative, or else ignore the possibility of a negative value finding its way into these functions.

By updating the standard library with a natural number type to represent these values (and potentially the Swift interfaces for Foundation and other frameworks), there is no way a negative number can be passed to these functions. This eliminates the need for many preconditions, allowing them to eliminate the possibility of a crash, and it would be clear that values such as count will never be negative.

If this change were accepted, the user would have to explicitly convert between the types at some points in their code. This could be seen as a burden, but requiring a cast encourages the programmer to check for negative values, keeping negatives out of natural number functions, and moving checks to the source of the data, similar to how Optionals eliminate nil values early on.

The parallel to Optionals is, in my opinion, the most appealing aspect of this idea. With Swift's focus on type safety, the lack of distinction between natural numbers and potentially negative ones seems like an omission, given how commonly both kinds of number are used. Enabling this level of integral safety would add additional clarity and reduce potential errors. Use cases include not just indexes, but in sizes such as collections' count and CG dimensions, for both clarity when getting values, and enforcing valid values at initialisation.

UInt is the obvious candidate to represent integer natural numbers, but is hazardous in edge cases: UInt allows an extra bit for magnitude, so has the possibility of overflow when converting back to Int. I assume this hazard is the primary reason UInt currently isn't used. If this a concern, a separate collection of types for natural integers (NInt?) could be created, which could be a strict subset of the correspondingly sized Int values (using n-1 bits).

This would mean adding a new collection of integer types in the standard library, which is undesirable. However, for high level applications which Swift is primarily used for, this would arguably be a more useful type than UInt, considering the near absence of UInt in current APIs, and its tiny increase (1 bit?) in precision compared to using an Int of a larger size. An unsigned Integer subset would allow safe conversion back to Int, and the conversion from Int is relatively simple, from the design perspective - ignore the sign bit, throw an error, or round up to 0, as appropriate.

Alternatives include:
• A generic wrapper for Int which constrains its possible values. This would allow inherent support for use with existing operators, but would be rather clunky in cases where only natural numbers are being dealt with.
• In a future version of Swift, the addition of type parameters constraining possible values may enable this to be added, but even if this does get added it may be too late to make such a major change.
• Leaving the APIs as they are and require every function dealing with natural numbers to either throw a runtime error when a negative is passed. If the user forgets to follow this rule (or wont put in the extra effort to add the check), their functions will have unspecified behaviour.

This ended up longer than I would like, sorry about that. I don't think this issue has been covered here.
I'd be interested to hear responses and opinions, and while I'm confident this change would be beneficial if done correctly, doing it correctly is easier said that done.

- James
_______________________________________________
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


(James F) #17

Fair point, though I'll point out that if the subtraction of Array indices ever did result in a negative, using the result as an index will result in a runtime error already.

If the negative result is useful, an explicit cast would be necessary for both operands. The main issue with this idea does seem to be the explicit casting, and this would have to be either accepted, or else overcome to make working with naturals more friendly, for this proposal to be accepted.

One solution would be an operator like subtract which returns an Int, to allow for easy conversion in this case, however this smells of a workaround.

Even if the idea isn't accepted, any solution we find for the problem of working with naturals would be equally applicable to UInt, so the discussion seems worth having either way.

From James F

···

On 3 Feb 2016, at 22:26, Jonathan Tang <jonathan.d.tang@gmail.com> wrote:

On Wed, Feb 3, 2016 at 2:17 PM, James Samuel Froggatt <conductator@ntlworld.com> wrote:
That is an interesting point. Based on how the operators are defined for Int:

‘Unlike the arithmetic operators in C and Objective-C, the Swift arithmetic operators do not allow values to overflow by default. You can opt in to value overflow behavior by using Swift’s overflow operators (such as a &+ b)’
https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/BasicOperators.html

The former statement, I believe, refers to the behaviour of throwing a runtime error. The solution would be the same as for checking before using an Array subscript (as currently implemented): explicit comparisons before performing the potentially crashing operation.

A regular signed integer already has the hazard of a ÷0 error. I think it would make sense to throw errors readily for subtraction - if the user would have to perform a check beforehand or face the consequences, just as they do for index out of bounds errors with Arrays. This is is currently how UInt's subtract implementation works, and I think the best option is to match this behaviour.

We'd be largely in the same position we are in now w.r.t. array accesses. You still need a runtime check and you can still cause a crash, but the check has been moved from the array subscript to any subtraction on array indices. As with divide-by-zero, it's the responsibility of the calling algorithm to make sure this never happens.

I think I'm -1 on this. Swift has for-each loops already that eliminate the vast majority of explicit array accesses. For ones with a computed index, you'd still need a check for <0 on the computation. Literal indices aren't much of a risk anyway - it's pretty obvious when you're subscripting with a literal negative number - but as others have pointed out, there are sometimes legit reasons why you might want a negative subscript or count property.

On 3 Feb 2016, at 21:51, Jonathan Tang <jonathan.d.tang@gmail.com> wrote:

On Wed, Feb 3, 2016 at 9:31 AM, James Froggatt via swift-evolution <swift-evolution@swift.org> wrote:
In the standard library, there are methods (notably array subscripts, CollectionType's .count and the dimensions of CGSize) which are designed to deal only with natural (non-negative) numbers. Functions either throw an error if a parameter is negative, or else ignore the possibility of a negative value finding its way into these functions.

By updating the standard library with a natural number type to represent these values (and potentially the Swift interfaces for Foundation and other frameworks), there is no way a negative number can be passed to these functions. This eliminates the need for many preconditions, allowing them to eliminate the possibility of a crash, and it would be clear that values such as count will never be negative.

How would you handle the types of arithmetic operations? Natural numbers are not closed under subtraction or division; a natural number minus another natural number may be an integer. There's no provision for arithmetic operations to throw an error in Swift, so you might have to silently eat the error in the subtraction operation, which defeats much of the typesafety of having a separate type.


(James F) #18

As a side note to the natural numbers discussion, the idea of constraining properties at compile time seems like it would allow compile-time safety for all existing types.

Based on the previous ‘Partially Constrained Protocols’ discussion, a natural extension would be:
protocol<Self == TypeName where Self.instance.property == [literal]>

This would require a new keyword, ‘instance’ is an obvious candidate, though isn't great, but would allow function parameters and return types to make compile-time requirements on the value being passed, and make promises about values being returned, eliminating the need for failure conditions hidden inside the function itself.

Thus allowing:
protocol<Self == Int where Self.instance >= 0>

I'm not sure how feasible this would be, as it's presumably asking a lot in terms of compile-time code checking, but it would certainly cover constraining to natural numbers and eliminate many runtime failure conditions.

Honestly, I'm not happy with verbosity of the ‘where’ syntax in general; any other suggestions for syntax are welcome.

Usage:
typealias NaturalInt = protocol<Self == Int where Self.instance >= 0>
struct SafeSize {
    var width: NaturalInt
    var height: NaturalInt
}
extension Array {
    subscript(safer index: NaturalInt) -> _Element {
        return self[index]
    }
}

I also feel I've seen this issue raised here before, so I may be reviving an old discussion.

From James F


(James F) #19

No matter the index, since .count represents the number of elements in the collection, it should never be negative.

An EveryOtherElementOfReverseOfArray implementation could have a startIndex of 4 and endIndex of 0, and wouldn't be iterable with a simple range, yet I'd still expect the count to be positive. Types without integer indexes are no exception.

For cases such as CGSize: from memory, I have no idea how CGSize would handle negatives. Would it ignore the sign, crash on creation, or simply allow them?

According to the CGGeometry reference:

A CGSize structure is sometimes used to represent a distance vector, rather than a physical size. As a vector, its values can be negative. To normalize a CGRect structure so that its size is represented by positive values, call the CGRectStandardize function.

CGSize represents vectors and sizes? And an explicit ‘normalisation’ (a non-negative safety check) must be performed to work with CGSize as a size? This sounds awfully like a lack of type safety.

So based on CGSize's use as a vector, it would have to remain unchanged. Using naturals elsewhere in the frameworks would serve to make it much more obvious negative checks should be performed working with this and similar types. This example just serves to emphasise how important making this distinction is.

From James F

···

On 4 Feb 2016, at 01:08, David Turnbull <dturnbull@gmail.com> wrote:

Here's how you could use a collection as a predicate. The OnSwitchesIndex is opaque to anything outside the file. There's no way to iterate OnSwitches with 0 ..<count. Thinking that arrays and sequences are counting up from 0 by 1 is missing the entire point of these protocols.


(David Turnbull) #20

That's an invariant. Enforcing y>=x with a type system is just as hard as
the halting problem.

-david

···

On Thu, Feb 4, 2016 at 3:48 AM, James Froggatt <conductator@ntlworld.com> wrote:

CGSize represents vectors *and* sizes? And an explicit ‘normalisation’ (a
non-negative safety check) must be performed to work with CGSize as a size?
This sounds awfully like a lack of type safety.