[Pitch] Percentage Type

Hi Evolution,

I was wondering if we would consider adding a percentage type to Swift. This would be a type with a value between 0 & 1.

I know we can and do use doubles or floats for this now, but there really is a semantic difference between most parameters that take a value between 0 & 1 and those that take any floating point value. It would be nice to have a type that semantically means that the value is from 0 to 1.

It could even just wrap a Double for speed (in my own code I wrap a UInt64 for decimal accuracy… and to avoid issues around comparisons).

Thanks,
Jon

2 Likes

Here is the code I use for percentage myself. (I incorrectly said I use a UInt64… I use a UInt32):

///Represents a percentage with the precision of millionths of 1 (i.e. 4 decimal places: XX.XXXX%). The value is always positive (or zero), but may be greater than 100%
struct Percentage {
    fileprivate(set) var millionths:UInt32
    
    fileprivate init(storage:UInt32){
        millionths = storage
    }
    
    static var quarter = Percentage(storage: 250_000)
    static var half = Percentage(storage: 500_000)
    static var threeQuarters = Percentage(storage: 750_000)
    static var full = Percentage(storage: 1_000_000)
    
    init(millionths:Int) {
        self.millionths = UInt32(millionths)
    }
    
    init(_ double:Double, range:ClosedRange<Double> = 0...1) {
        if range == 0...1 {
            self.millionths = UInt32(max(double * 1_000_000, 0))
        }else if range == 0...100{
            self.millionths = UInt32(max(double * 10_000, 0))
        }else{
            self.millionths = UInt32(max((double - range.lowerBound)/(range.upperBound - range.lowerBound),0))
        }
    }
    
    init(_ num:Num, range:ClosedRange<Num> = 0...1) {
        if range == 0...1 {
            self.millionths = UInt32(max(num * 1_000_000, 0).integerValue)
        }else if range == 0...100{
            self.millionths = UInt32(max(num * 10_000, 0).integerValue)
        }else{
            self.millionths = UInt32(max((num - range.lowerBound)/(range.upperBound - range.lowerBound),0).integerValue)
        }
    }
    
    init(_ decimal:Decimal, range:ClosedRange<Decimal> = 0...1) {
        if range == 0...1 {
            self.millionths = NSDecimalNumber(decimal: max(decimal * 1_000_000, 0)).uint32Value
        }else if range == 0...100{
            self.millionths = NSDecimalNumber(decimal: max(decimal * 10_000, 0)).uint32Value
        }else{
            let shifted = max((decimal - range.lowerBound)/(range.upperBound - range.lowerBound),0)
            self.millionths = NSDecimalNumber(decimal: shifted).uint32Value
        }
    }
    
    init(hundredths:Int) {
        self.millionths = UInt32(max(hundredths * 10_000, 0))
    }

    init(thousandths:Int) {
        self.millionths = UInt32(max(thousandths * 1_000, 0))
    }
    
    var isFull:Bool {
        return self.millionths >= 1_000_000
    }
    
    var doubleValue:Double{
        return Double(self.millionths)/1_000_000
    }
    
    var cgfloatValue:CGFloat{
        return CGFloat(self.millionths)/1_000_000
    }
    
    var decimalValue:Decimal {
        return Decimal(self.millionths)/1_000_000
    }
    
    var numValue:Num {
        return Num(numerator: Int32(self.millionths), denominator: 1_000_000)
    }
    
    var hundredths:Int {
        return Int(self.millionths/10_000)
    }
    
    var thousandths:Int {
        return Int(self.millionths/1_000)
    }
    
    var tenThousandths:Int {
        return Int(self.millionths/100)
    }
    
    func map(to range:ClosedRange<Num>) -> Num {
        return self.numValue * (range.upperBound - range.lowerBound) + range.lowerBound
    }
    
    mutating func clip() {
        if self.millionths > 1_000_000 {
            self.millionths = 1_000_000
        }
    }
    
    func clipped()->Percentage {
        if self.millionths > 1_000_000 {
            return Percentage.full
        }
        return self
    }
    
}

extension Percentage:CustomStringConvertible {
    var description: String {
        let num = self.numValue * 100
        if num.isInteger{
            return "\(num)%"
        }
        return "\(num.decimalValue)%"
    }
}

extension Percentage:ExpressibleByIntegerLiteral {
    init(integerLiteral value: IntegerLiteralType) {
        self.millionths = UInt32(max(value * 10_000, 0))
    }
}

extension Percentage:Hashable {
    var hashValue: Int {
        return self.millionths.hashValue
    }
    
    static func == (lhs:Percentage, rhs:Percentage)->Bool {
        return lhs.millionths == rhs.millionths
    }
}

extension Percentage:Comparable {
    static func < (lhs:Percentage, rhs:Percentage) -> Bool {
        return lhs.millionths < rhs.millionths
    }
}

extension Percentage {
    static func + (lhs:Percentage, rhs:Percentage)->Percentage {
        return Percentage(storage: lhs.millionths + rhs.millionths)
    }
    
    static func - (lhs:Percentage, rhs:Percentage)->Percentage {
        if rhs > lhs {return 0}
        return Percentage(storage: lhs.millionths - rhs.millionths)
    }
    
    static func * (lhs:Percentage, rhs:Double)->Double {
        return lhs.doubleValue * rhs
    }

    static func * (lhs:Double, rhs:Percentage)->Double {
        return lhs * rhs.doubleValue
    }
    
    static func * (lhs:Percentage, rhs:CGFloat)->CGFloat {
        return lhs.cgfloatValue * rhs
    }
    
    static func * (lhs:CGFloat, rhs:Percentage)->CGFloat {
        return lhs * rhs.cgfloatValue
    }
    
    static func * (lhs:Percentage, rhs:Num)->Num {
        return lhs.numValue * rhs
    }
    
    static func * (lhs:Num, rhs:Percentage)->Num {
        return lhs * rhs.numValue
    }

    static func * (lhs:Percentage, rhs:Percentage)->Percentage {
        return Percentage(lhs.decimalValue * rhs.decimalValue)
    }
    
}

···

On Jan 13, 2018, at 6:26 PM, Jonathan Hull via swift-evolution <swift-evolution@swift.org> wrote:

Hi Evolution,

I was wondering if we would consider adding a percentage type to Swift. This would be a type with a value between 0 & 1.

I know we can and do use doubles or floats for this now, but there really is a semantic difference between most parameters that take a value between 0 & 1 and those that take any floating point value. It would be nice to have a type that semantically means that the value is from 0 to 1.

It could even just wrap a Double for speed (in my own code I wrap a UInt64 for decimal accuracy… and to avoid issues around comparisons).

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

No to this pitch, because a percentage can be higher than 100%.

Use NumberFormatter to display a number as a percentage. https://developer.apple.com/documentation/foundation/numberformatter

Or you could make an NSNumber subclass if you want to enforce an arbitrary rule upon numbers.

Jonathan

···

On Jan 13, 2018, at 18:26, Jonathan Hull via swift-evolution <swift-evolution@swift.org> wrote:

Hi Evolution,

I was wondering if we would consider adding a percentage type to Swift. This would be a type with a value between 0 & 1.

I know we can and do use doubles or floats for this now, but there really is a semantic difference between most parameters that take a value between 0 & 1 and those that take any floating point value. It would be nice to have a type that semantically means that the value is from 0 to 1.

It could even just wrap a Double for speed (in my own code I wrap a UInt64 for decimal accuracy… and to avoid issues around comparisons).

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

As Erica mentioned about Angle, this seems to be a perfect fit for an
appropriately focused third-party library, but I'm not sure I appreciate
why this should be part of the standard library. In large part, you seem to
have reinvented a decimal type, which is already available in the form of
Foundation.Decimal on all supported platforms.

···

On Sat, Jan 13, 2018 at 9:07 PM, Jonathan Hull via swift-evolution < swift-evolution@swift.org> wrote:

Here is the code I use for percentage myself. (I incorrectly said I use a
UInt64… I use a UInt32):

///Represents a percentage with the precision of millionths of 1 (i.e. 4
decimal places: XX.XXXX%). The value is always positive (or zero), but may
be greater than 100%
struct Percentage {
    fileprivate(set) var millionths:UInt32

    fileprivate init(storage:UInt32){
        millionths = storage
    }

    static var quarter = Percentage(storage: 250_000)
    static var half = Percentage(storage: 500_000)
    static var threeQuarters = Percentage(storage: 750_000)
    static var full = Percentage(storage: 1_000_000)

    init(millionths:Int) {
        self.millionths = UInt32(millionths)
    }

    init(_ double:Double, range:ClosedRange<Double> = 0...1) {
        if range == 0...1 {
            self.millionths = UInt32(max(double * 1_000_000, 0))
        }else if range == 0...100{
            self.millionths = UInt32(max(double * 10_000, 0))
        }else{
            self.millionths = UInt32(max((double - range.lowerBound
)/(range.upperBound - range.lowerBound),0))
        }
    }

    init(_ num:Num, range:ClosedRange<Num> = 0...1) {
        if range == 0...1 {
            self.millionths = UInt32(max(num * 1_000_000, 0).integerValue)
        }else if range == 0...100{
            self.millionths = UInt32(max(num * 10_000, 0).integerValue)
        }else{
            self.millionths = UInt32(max((num - range.lowerBound)/(range.
upperBound - range.lowerBound),0).integerValue)
        }
    }

    init(_ decimal:Decimal, range:ClosedRange<Decimal> = 0...1) {
        if range == 0...1 {
            self.millionths = NSDecimalNumber(decimal: max(decimal *
1_000_000, 0)).uint32Value
        }else if range == 0...100{
            self.millionths = NSDecimalNumber(decimal: max(decimal *
10_000, 0)).uint32Value
        }else{
            let shifted = max((decimal - range.lowerBound)/(range.upper
Bound - range.lowerBound),0)
            self.millionths = NSDecimalNumber(decimal: shifted).
uint32Value
        }
    }

    init(hundredths:Int) {
        self.millionths = UInt32(max(hundredths * 10_000, 0))
    }

    init(thousandths:Int) {
        self.millionths = UInt32(max(thousandths * 1_000, 0))
    }

    var isFull:Bool {
        return self.millionths >= 1_000_000
    }

    var doubleValue:Double{
        return Double(self.millionths)/1_000_000
    }

    var cgfloatValue:CGFloat{
        return CGFloat(self.millionths)/1_000_000
    }

    var decimalValue:Decimal {
        return Decimal(self.millionths)/1_000_000
    }

    var numValue:Num {
        return Num(numerator: Int32(self.millionths), denominator:
1_000_000)
    }

    var hundredths:Int {
        return Int(self.millionths/10_000)
    }

    var thousandths:Int {
        return Int(self.millionths/1_000)
    }

    var tenThousandths:Int {
        return Int(self.millionths/100)
    }

    func map(to range:ClosedRange<Num>) -> Num {
        return self.numValue * (range.upperBound - range.lowerBound) +
range.lowerBound
    }

    mutating func clip() {
        if self.millionths > 1_000_000 {
            self.millionths = 1_000_000
        }
    }

    func clipped()->Percentage {
        if self.millionths > 1_000_000 {
            return Percentage.full
        }
        return self
    }

}

extension Percentage:CustomStringConvertible {
    var description: String {
        let num = self.numValue * 100
        if num.isInteger{
            return "\(num)%"
        }
        return "\(num.decimalValue)%"
    }
}

extension Percentage:ExpressibleByIntegerLiteral {
    init(integerLiteral value: IntegerLiteralType) {
        self.millionths = UInt32(max(value * 10_000, 0))
    }
}

extension Percentage:Hashable {
    var hashValue: Int {
        return self.millionths.hashValue
    }

    static func == (lhs:Percentage, rhs:Percentage)->Bool {
        return lhs.millionths == rhs.millionths
    }
}

extension Percentage:Comparable {
    static func < (lhs:Percentage, rhs:Percentage) -> Bool {
        return lhs.millionths < rhs.millionths
    }
}

extension Percentage {
    static func + (lhs:Percentage, rhs:Percentage)->Percentage {
        return Percentage(storage: lhs.millionths + rhs.millionths)
    }

    static func - (lhs:Percentage, rhs:Percentage)->Percentage {
        if rhs > lhs {return 0}
        return Percentage(storage: lhs.millionths - rhs.millionths)
    }

    static func * (lhs:Percentage, rhs:Double)->Double {
        return lhs.doubleValue * rhs
    }

    static func * (lhs:Double, rhs:Percentage)->Double {
        return lhs * rhs.doubleValue
    }

    static func * (lhs:Percentage, rhs:CGFloat)->CGFloat {
        return lhs.cgfloatValue * rhs
    }

    static func * (lhs:CGFloat, rhs:Percentage)->CGFloat {
        return lhs * rhs.cgfloatValue
    }

    static func * (lhs:Percentage, rhs:Num)->Num {
        return lhs.numValue * rhs
    }

    static func * (lhs:Num, rhs:Percentage)->Num {
        return lhs * rhs.numValue
    }

    static func * (lhs:Percentage, rhs:Percentage)->Percentage {
        return Percentage(lhs.decimalValue * rhs.decimalValue)
    }

}

On Jan 13, 2018, at 6:26 PM, Jonathan Hull via swift-evolution < > swift-evolution@swift.org> wrote:

Hi Evolution,

I was wondering if we would consider adding a percentage type to Swift.
This would be a type with a value between 0 & 1.

I know we can and do use doubles or floats for this now, but there really
is a semantic difference between most parameters that take a value between
0 & 1 and those that take any floating point value. It would be nice to
have a type that semantically means that the value is from 0 to 1.

It could even just wrap a Double for speed (in my own code I wrap a UInt64
for decimal accuracy… and to avoid issues around comparisons).

Thanks,
Jon
_______________________________________________
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

No to this pitch, because a percentage can be higher than 100%.

‚ėĚÔłŹ One need only look in Activity Monitor to see this in action.

Use NumberFormatter to display a number as a percentage. https://developer.apple.com/documentation/foundation/numberformatter

I agree 1000% ūüėČ

Or you could make an NSNumber subclass if you want to enforce an arbitrary rule upon numbers.

In theory you can; in practice you can’t. Subclassing things like NSNumber is fraught with undocumented peril.

Dave

···

On Jan 16, 2018, at 9:56 AM, Jon Gilbert via swift-evolution <swift-evolution@swift.org> wrote:

Mainly semantics.

We could technically use Int instead of having a Bool type (just using 1 and 0). We don’t do that since Int and Bool have intrinsically different meanings in code.

What I am saying is that parameters that take the range 0 to 1 typically have a fundamentally different meaning (or at least a different way of thinking about them) than Doubles. It would be nice to be able to see that distinction when using APIs.

With both this and the Angle type, I am pointing out areas where, due to historical reasons in C, we have conflated a bunch of types which have different behavior, and then just expect programmers to be conscientious enough to use them correctly in each case. These types/numbers all have a different forms of dimensionality.

I’d like to discuss that before we lock everything down.

Thanks,
Jon

···

On Jan 13, 2018, at 9:18 PM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

As Erica mentioned about Angle, this seems to be a perfect fit for an appropriately focused third-party library, but I'm not sure I appreciate why this should be part of the standard library. In large part, you seem to have reinvented a decimal type, which is already available in the form of Foundation.Decimal on all supported platforms.

On Sat, Jan 13, 2018 at 9:07 PM, Jonathan Hull via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
Here is the code I use for percentage myself. (I incorrectly said I use a UInt64… I use a UInt32):

///Represents a percentage with the precision of millionths of 1 (i.e. 4 decimal places: XX.XXXX%). The value is always positive (or zero), but may be greater than 100%
struct Percentage {
    fileprivate(set) var millionths:UInt32
    
    fileprivate init(storage:UInt32){
        millionths = storage
    }
    
    static var quarter = Percentage(storage: 250_000)
    static var half = Percentage(storage: 500_000)
    static var threeQuarters = Percentage(storage: 750_000)
    static var full = Percentage(storage: 1_000_000)
    
    init(millionths:Int) {
        self.millionths = UInt32(millionths)
    }
    
    init(_ double:Double, range:ClosedRange<Double> = 0...1) {
        if range == 0...1 {
            self.millionths = UInt32(max(double * 1_000_000, 0))
        }else if range == 0...100{
            self.millionths = UInt32(max(double * 10_000, 0))
        }else{
            self.millionths = UInt32(max((double - range.lowerBound)/(range.upperBound - range.lowerBound),0))
        }
    }
    
    init(_ num:Num, range:ClosedRange<Num> = 0...1) {
        if range == 0...1 {
            self.millionths = UInt32(max(num * 1_000_000, 0).integerValue)
        }else if range == 0...100{
            self.millionths = UInt32(max(num * 10_000, 0).integerValue)
        }else{
            self.millionths = UInt32(max((num - range.lowerBound)/(range.upperBound - range.lowerBound),0).integerValue)
        }
    }
    
    init(_ decimal:Decimal, range:ClosedRange<Decimal> = 0...1) {
        if range == 0...1 {
            self.millionths = NSDecimalNumber(decimal: max(decimal * 1_000_000, 0)).uint32Value
        }else if range == 0...100{
            self.millionths = NSDecimalNumber(decimal: max(decimal * 10_000, 0)).uint32Value
        }else{
            let shifted = max((decimal - range.lowerBound)/(range.upperBound - range.lowerBound),0)
            self.millionths = NSDecimalNumber(decimal: shifted).uint32Value
        }
    }
    
    init(hundredths:Int) {
        self.millionths = UInt32(max(hundredths * 10_000, 0))
    }

    init(thousandths:Int) {
        self.millionths = UInt32(max(thousandths * 1_000, 0))
    }
    
    var isFull:Bool {
        return self.millionths >= 1_000_000
    }
    
    var doubleValue:Double{
        return Double(self.millionths)/1_000_000
    }
    
    var cgfloatValue:CGFloat{
        return CGFloat(self.millionths)/1_000_000
    }
    
    var decimalValue:Decimal {
        return Decimal(self.millionths)/1_000_000
    }
    
    var numValue:Num {
        return Num(numerator: Int32(self.millionths), denominator: 1_000_000)
    }
    
    var hundredths:Int {
        return Int(self.millionths/10_000)
    }
    
    var thousandths:Int {
        return Int(self.millionths/1_000)
    }
    
    var tenThousandths:Int {
        return Int(self.millionths/100)
    }
    
    func map(to range:ClosedRange<Num>) -> Num {
        return self.numValue * (range.upperBound - range.lowerBound) + range.lowerBound
    }
    
    mutating func clip() {
        if self.millionths > 1_000_000 {
            self.millionths = 1_000_000
        }
    }
    
    func clipped()->Percentage {
        if self.millionths > 1_000_000 {
            return Percentage.full
        }
        return self
    }
    
}

extension Percentage:CustomStringConvertible {
    var description: String {
        let num = self.numValue * 100
        if num.isInteger{
            return "\(num)%"
        }
        return "\(num.decimalValue)%"
    }
}

extension Percentage:ExpressibleByIntegerLiteral {
    init(integerLiteral value: IntegerLiteralType) {
        self.millionths = UInt32(max(value * 10_000, 0))
    }
}

extension Percentage:Hashable {
    var hashValue: Int {
        return self.millionths.hashValue
    }
    
    static func == (lhs:Percentage, rhs:Percentage)->Bool {
        return lhs.millionths == rhs.millionths
    }
}

extension Percentage:Comparable {
    static func < (lhs:Percentage, rhs:Percentage) -> Bool {
        return lhs.millionths < rhs.millionths
    }
}

extension Percentage {
    static func + (lhs:Percentage, rhs:Percentage)->Percentage {
        return Percentage(storage: lhs.millionths + rhs.millionths)
    }
    
    static func - (lhs:Percentage, rhs:Percentage)->Percentage {
        if rhs > lhs {return 0}
        return Percentage(storage: lhs.millionths - rhs.millionths)
    }
    
    static func * (lhs:Percentage, rhs:Double)->Double {
        return lhs.doubleValue * rhs
    }

    static func * (lhs:Double, rhs:Percentage)->Double {
        return lhs * rhs.doubleValue
    }
    
    static func * (lhs:Percentage, rhs:CGFloat)->CGFloat {
        return lhs.cgfloatValue * rhs
    }
    
    static func * (lhs:CGFloat, rhs:Percentage)->CGFloat {
        return lhs * rhs.cgfloatValue
    }
    
    static func * (lhs:Percentage, rhs:Num)->Num {
        return lhs.numValue * rhs
    }
    
    static func * (lhs:Num, rhs:Percentage)->Num {
        return lhs * rhs.numValue
    }

    static func * (lhs:Percentage, rhs:Percentage)->Percentage {
        return Percentage(lhs.decimalValue * rhs.decimalValue)
    }
    
}

On Jan 13, 2018, at 6:26 PM, Jonathan Hull via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi Evolution,

I was wondering if we would consider adding a percentage type to Swift. This would be a type with a value between 0 & 1.

I know we can and do use doubles or floats for this now, but there really is a semantic difference between most parameters that take a value between 0 & 1 and those that take any floating point value. It would be nice to have a type that semantically means that the value is from 0 to 1.

It could even just wrap a Double for speed (in my own code I wrap a UInt64 for decimal accuracy… and to avoid issues around comparisons).

Thanks,
Jon
_______________________________________________
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

If you look at the implementation I provided, it allows for percentages higher than 100% (and has an easy way to clip them or map them to an arbitrary range).

···

On Jan 16, 2018, at 11:39 AM, Dave DeLong via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 16, 2018, at 9:56 AM, Jon Gilbert via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

No to this pitch, because a percentage can be higher than 100%.

:point_up:ÔłŹ One need only look in Activity Monitor to see this in action.

Use NumberFormatter to display a number as a percentage. https://developer.apple.com/documentation/foundation/numberformatter

I agree 1000% :wink:

Or you could make an NSNumber subclass if you want to enforce an arbitrary rule upon numbers.

In theory you can; in practice you can’t. Subclassing things like NSNumber is fraught with undocumented peril.

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

Mainly semantics.

We could technically use Int instead of having a Bool type (just using 1 and 0). We don’t do that since Int and Bool have intrinsically different meanings in code.

What I am saying is that parameters that take the range 0 to 1 typically have a fundamentally different meaning (or at least a different way of thinking about them) than Doubles. It would be nice to be able to see that distinction when using APIs.

With both this and the Angle type, I am pointing out areas where, due to historical reasons in C, we have conflated a bunch of types which have different behavior, and then just expect programmers to be conscientious enough to use them correctly in each case. These types/numbers all have a different forms of dimensionality.

I’d like to discuss that before we lock everything down.

+1 (although I think a ‚Äúnormalized to [0, 1]‚ÄĚ type would be more useful than a ‚Äúpercentage‚ÄĚ type)

- Dave Sweeris

···

Sent from my iPhone

On Jan 16, 2018, at 23:45, Jonathan Hull via swift-evolution <swift-evolution@swift.org> wrote:

Bool is not a good example; it permits precisely two logical values (0 and
1). By contrast, if you're going to support 1000%, then your type supports
the same values as the underlying storage. As I wrote in a different
thread, one way to look at a type is the set of values that a variable can
have.

What is your limiting principle here if you think that a range that's not
enforced makes a value become of a different type? Often, a 1-5 rating
system is used. Sometimes, it's 1-4 or 1-10. And of course, a "3" on a 1-5
scale means something very different from a "3" on a 1-10 scale. Should
ScaleFrom1To5 be its own type? And also ScaleFrom1To4 and ScaleFrom1To10?

Besides, even supposing a percentage type would be in high demand, there's
no need for its inclusion in the standard library. It's very easy to
implement on your own in a third-party library. Moreover, custom operators
will allow you to define a postfix `%`, and then you could write: `let x =
100%`. Throw in some heterogeneous arithmetic operators and you could do
almost any math you want.

···

On Wed, Jan 17, 2018 at 2:04 AM, David Sweeris <davesweeris@mac.com> wrote:

Sent from my iPhone

> On Jan 16, 2018, at 23:45, Jonathan Hull via swift-evolution < > swift-evolution@swift.org> wrote:
>
> Mainly semantics.
>
> We could technically use Int instead of having a Bool type (just using 1
and 0). We don’t do that since Int and Bool have intrinsically different
meanings in code.
>
> What I am saying is that parameters that take the range 0 to 1 typically
have a fundamentally different meaning (or at least a different way of
thinking about them) than Doubles. It would be nice to be able to see that
distinction when using APIs.
>
> With both this and the Angle type, I am pointing out areas where, due to
historical reasons in C, we have conflated a bunch of types which have
different behavior, and then just expect programmers to be conscientious
enough to use them correctly in each case. These types/numbers all have a
different forms of dimensionality.
>
> I’d like to discuss that before we lock everything down.

+1 (although I think a ‚Äúnormalized to [0, 1]‚ÄĚ type would be more useful
than a ‚Äúpercentage‚ÄĚ type)

Saagar Jha

Sent from my iPhone

>
> Mainly semantics.
>
> We could technically use Int instead of having a Bool type (just using 1 and 0). We don’t do that since Int and Bool have intrinsically different meanings in code.
>
> What I am saying is that parameters that take the range 0 to 1 typically have a fundamentally different meaning (or at least a different way of thinking about them) than Doubles. It would be nice to be able to see that distinction when using APIs.
>
> With both this and the Angle type, I am pointing out areas where, due to historical reasons in C, we have conflated a bunch of types which have different behavior, and then just expect programmers to be conscientious enough to use them correctly in each case. These types/numbers all have a different forms of dimensionality.
>
> I’d like to discuss that before we lock everything down.

+1 (although I think a ‚Äúnormalized to [0, 1]‚ÄĚ type would be more useful than a ‚Äúpercentage‚ÄĚ type)

Bool is not a good example; it permits precisely two logical values (0 and 1). By contrast, if you're going to support 1000%, then your type supports the same values as the underlying storage. As I wrote in a different thread, one way to look at a type is the set of values that a variable can have.

What is your limiting principle here if you think that a range that's not enforced makes a value become of a different type? Often, a 1-5 rating system is used. Sometimes, it's 1-4 or 1-10. And of course, a "3" on a 1-5 scale means something very different from a "3" on a 1-10 scale. Should ScaleFrom1To5 be its own type? And also ScaleFrom1To4 and ScaleFrom1To10?

Just a thought: if Swift ever allows integer literals in generics (e.g. for a fixed size array equivalent to C++’s std::array), this seems like how a type like ScaleFrom (and a percent type, for that matter) could be implemented.

···

On Jan 17, 2018, at 16:56, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:
On Wed, Jan 17, 2018 at 2:04 AM, David Sweeris <davesweeris@mac.com <mailto:davesweeris@mac.com>> wrote:
> On Jan 16, 2018, at 23:45, Jonathan Hull via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Besides, even supposing a percentage type would be in high demand, there's no need for its inclusion in the standard library. It's very easy to implement on your own in a third-party library. Moreover, custom operators will allow you to define a postfix `%`, and then you could write: `let x = 100%`. Throw in some heterogeneous arithmetic operators and you could do almost any math you want.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution