[swift-evolution] [Idea] Typed Numerics

Charlie Monroe charlie at charliemonroe.net
Mon Aug 22 11:17:47 CDT 2016


I've personally come across something like Nur suggested. In particular, this is with NSTimeInterval, which is a typedef for Double.

What I wanted to do is to make

extension NSTimeInterval {
	static let minute: NSTimeInterval = 60.0
	static let hour: NSTimeInterval = 3600.0
	/// ... day, ...
}

and you can then use it NSTimeInterval.minute.

The issue now is that is polutes the Double type as well, so currently, it's valid to write Double.minute with the code above - which is not disirable behavior IMHO.

Sure, you can work around by introducing e.g. enum or an empty struct TimeIntervals with static members, but I personally think that making specialized subtypes of numeric types is a good way to introduce some constants while not poluting the entire namespace.

Or maybe it's me still thinking too much in C-style and the proper way would be to represent NSTimeInterval as a different type...

> On Aug 22, 2016, at 6:07 PM, Vladimir.S via swift-evolution <swift-evolution at swift.org> wrote:
> 
> The first question is what the meaning of , for example, a multiplication of Distance ? I.e.
> let x1 : Distance = 10.m
> let x2 : Distance = 10.km
> let x3 = x1 * x2  // ???
> 
> I.e. as soon as your Distance is Double, you allows all kind of floating point operations on instances of this type. And some such operations has no meaning for Distance. So, in your proposal, you need to somehow control allowed operations.
> 
> And I believe you can get what you want right now with enums(If I'm not missing something):
> 
> enum Distance  {
>    case km(Double)
>    case m(Double)
>    case mm(Double)
> 
>    func inKilometers() -> Double {
>        return inMeters() / 1000.0
>    }
> 
>    func inMeters() -> Double {
>        switch self {
>            case .km(let value) : return value * 1000.0
>            case .m(let value) : return value
>            case .mm(let value) : return value / 1000.0
>        }
>    }
> 
>    func inMillimeters() -> Double {
>        return inMeters() * 1000.0
>    }
> 
>    static func +(lhs: Distance, rhs: Distance) -> Distance {
>        return .m(lhs.inMeters() + rhs.inMeters())
>    }
> 
>    static func -(lhs: Distance, rhs: Distance) -> Distance {
>        return .m(lhs.inMeters() - rhs.inMeters())
>    }
> 
>    // implement needed operations here..
> }
> 
> extension Double {
>    var km : Distance { return Distance.km(self) }
>    var m : Distance { return Distance.m(self) }
>    var mm : Distance { return Distance.mm(self) }
> }
> 
> let dist : Distance = 10.km + 5.m + 5.mm
> 
> print(dist.inMeters()) // 10005.005
> print(dist.inKilometers()) // 10.005005
> 
> More verbose, but more control. From other point of view, currently you'll need a lot of code for each such type(like Distance).
> 
> But I do think that such ability to create custom domain-specific types based on standard value types is a useful feature which can improve a quality of code and can reduce the number of bugs.
> 
> The syntax to declare such type should be simple and clear, to be able to declare a number of such types without a lot of boilerplate code. Something like this:
> 
> typealias Distance : Double {
>    var km : Distance { return self * 1000.0 }
>    var m : Distance { return self  }
>    var mm : Distance { return self / 1000.0 }
>    inherit [+,-,/]
> }
> 
> IMO such type is more a type alias than new structure
> 
> On 22.08.2016 17:54, Nur Ismail via swift-evolution wrote:
>> Hi,
>> 
>> I'm new to the list, but have an idea for Typed Numerics.
>> Basically numeric values (such as Double, Int, etc.) that are strongly
>> typed to a specific use case, for example Distance, Weight, etc. and cannot
>> be intermixed with untyped values.
>> 
>> So if I have (the syntax is made up, but perhaps something like this):
>> =====
>> //Distance
>> struct fixedtype Distance : Double {
>> var km: …
>> var m: …
>> typealias meters: m
>> var feet: ...
>> ...
>> }
>> 
>> //Weight
>> struct fixedtype Weight : Double {
>> var kg: …
>> var g: …
>> typealias grams : g
>> var pound: ...
>> }
>> 
>>>> var weight : Weight = 5.kg <http://5.kg/> + 5.g + 7.m
>> ………………………………………..^ Compiler Error: Can’t add Distance to Weight...
>> var distance: Distance = 7.km <http://7.km/> + 12.5.m + 5.0 + 3
>> ………………………………………………...^ Compiler Error: can’t add untyped number to Distance...
>> ===
>> 
>> The main restriction this syntax should do is disallow intermixing of
>> numeric types (even if they all descend from Double, Int, etc.) and not
>> allow adding untyped numerics (i.e. those without a type suffix), unless
>> explicitly asked for in the code.
>> 
>> Any of these can be converted to it's raw untyped value, for example:
>> =====
>> let number : Double = distance.rawValue + 5.0   //This is allowed
>> distance += number.m   //number is converted to m (meters)
>> =====
>> 
>> From the Swift 3 Language guide, we are for example given the following
>> example:
>> =====
>> extension Double {
>>  var km: Double { return self * 1_000.0 }
>>  var m: Double { return self }
>>  var cm: Double { return self / 100.0 }
>>  var mm: Double { return self / 1_000.0 }
>>  var ft: Double { return self / 3.28084 }
>> }
>> 
>> let aMarathon = 42.km <http://42.km/> + 195.m
>> print("A marathon is \(aMarathon) meters long")
>> // Prints "A marathon is 42195.0 meters long"
>> =====
>> 
>> This is quite nice to suffix a conversion method after the value, but if I
>> had another extension that converts the values to pounds and kilograms,
>> then one can illegally do this:
>>     let aValue = 42.km <http://42.km/> + 195.m + 17.pounds + 5.0
>> and then the code would still compile and run, but not work as intended.
>> 
>> Extra reading, and inspiration for a feature like the above:
>> Mars Probe Lost Due to Simple Math Error
>> http://articles.latimes.com/1999/oct/01/news/mn-17288
>> <http://articles.latimes.com/1999/oct/01/news/mn-17288>
>> 
>> "NASA lost its $125-million Mars Climate Orbiter because spacecraft
>> engineers failed to convert from English to metric measurements when
>> exchanging vital data before the craft was launched, space agency officials
>> said Thursday.
>> 
>> A navigation team at the Jet Propulsion Laboratory used the metric system
>> of millimeters and meters in its calculations, while Lockheed Martin
>> Astronautics in Denver, which designed and built the spacecraft, provided
>> crucial acceleration data in the English system of inches, feet and pounds.
>> 
>> As a result, JPL engineers mistook acceleration readings measured in
>> English units of pound-seconds for a metric measure of force called
>> newton-seconds."
>> 
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>> 
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution



More information about the swift-evolution mailing list