[swift-evolution] [Idea] Typed Numerics

Nur Ismail nur at estalea.com
Wed Aug 24 09:53:49 CDT 2016


Hi Vladimir,

Your latest code is even nicer :) and works for most part, however found
the following issues below:


let dist : Distance = 10.km + 5.m + 5.mm

let dist2 : Distance = 100.km + 5.m

let weight : Weight = 10.pounds + 5.kg + 5.g  // Assume we have Weight type
defined also

//let num = 100.km + 10.pounds  // Not allowed, as different types.
Compiler error as expected!

let distSum = dist + dist2  // Okay


print(dist.m) // Okay


print(dist.km + 10.km) // Doesn't work, although it should. Compiler Error:
Binary operator '+' cannot be applied to operands of type 'Double' and
'Distance'

print(dist.km + 10)   // 20.005005 // This works although it shouldn't!

print(dist.km * 200)  // 2001.001  // This works although it shouldn't!


print(dist.km + weight.kg) // 18.9470128740157  // This also works although
it shouldn't!

Also just still issue of Double space polluted with both Distance and
Weight extensions, but don't think we can get around that, unless we get
some compiler support :)

Regards,

On Tue, Aug 23, 2016 at 6:00 PM, Vladimir.S <svabox at gmail.com> wrote:

> On 23.08.2016 12:42, Nur Ismail wrote:
>
>> Hi Vladimir,
>>
>> Thanks for your code, it's actually quite close to what I want :)
>>
>> Some comments:
>> 1) The main downside is that to read back the value, one can't re-use
>> ".m",
>> ".km", etc. and have to use ".inMeters()", etc. Would have been nice to be
>> able read and write using the same suffixes.
>>
>
> I agree with all of your points. And about this first.. Yes, I think this
> variant is better:
>
> struct Distance  {
>
>     var km : Double { return self.m / 1000.0 }
>     var m: Double
>     var mm : Double { return self.m * 1000.0 }
>
>     init(m: Double) {
>         self.m = m
>     }
>
>     init(km: Double) {
>         self.m = km * 1000.0
>     }
>
>     init(mm: Double) {
>         self.m = mm / 1000.0
>     }
>
>     static func +(lhs: Distance, rhs: Distance) -> Distance {
>         return Distance(m: lhs.m + rhs.m)
>     }
>
>     static func -(lhs: Distance, rhs: Distance) -> Distance {
>         return Distance(m: lhs.m - rhs.m)
>     }
> }
>
> 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.m) // 10005.005
> print(dist.km) // 10.005005
> print(MemoryLayout<Distance>.size) // 8
>
>
>> 2) Works quite well on assigning values, and works as expected with a lot
>> of control of allowed operations, etc. However can make things verbose as
>> you also mentioned when needing to define many different types, but that's
>> a limitation of Swift not supporting this "Typed Numerics" natively rather
>> than your code :)
>>
>> 3) Requires an extra byte to store the enumeration value internally, so
>> whereas Double is 8 bytes, Distance would be 9 bytes. Sometimes it might
>> be
>> desirable to store the original type the value was assigned with, for
>> example .km, and only convert to appropriate type when reading, whereas
>> other times it's best to convert immediately to the preferred type on
>> assignment which would be .m (or meters) in this case, perhaps use the
>> "default" keyword next to the type?. Also compiler support would be
>> advantageous here also, so that constant expressions can be evaluated at
>> compile time with no overhead, so that if I put... let dist : Distance =
>> 125.km <http://125.km>, dist is converted at compile time to 125000.m.
>>
>> 4) Agree that this "feature" should rather be a type alias, as we dealing
>> with types rather than a structure.
>>
>> 5) If Swift could support the shortened typed numeric syntax natively, and
>> with the efficiency that can only happen in the compiler itself so that it
>> works as fast and efficiently as normal untyped numbers that would be
>> awesome :)
>>
>> 6) I think a feature like this could make Swift even more useful in
>> scientific and other critical applications where they deal with many
>> different types of numbers, such as velocity, acceleration, gravity,
>> power,
>> watts, distance, etc. etc.
>>
>> Also normal everyday code, for example,
>>     func circle(radius: Double) ... //Is that radius in pixels, meters,
>> millimetres or something else? But if radius was defined as our fancy
>> Distance (assuming we add pixels (px) too :), then they could call it
>> with:
>>     circle(radius: 150.px) //or
>>     circle(radius: 100.mm <http://100.mm>) //or
>>     circle(radius: 10.cm <http://10.cm>)
>>     //etc.
>>
>> This would help make the code clearer, and lead to better quality code and
>> hopefully less bugs.
>>
>> Regards,
>> Nur
>>
>> On Mon, Aug 22, 2016 at 6:07 PM, Vladimir.S <svabox at gmail.com
>> <mailto:svabox at gmail.com>> 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 <http://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 <http://10.km> + 5.m + 5.mm <http://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 <tel:22.08.2016%2017>: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> <http://5.kg/> + 5.g +
>> 7.m
>>         ………………………………………..^ Compiler Error: Can’t add Distance to Weight...
>>         var distance: Distance = 7.km <http://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> <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> <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>
>>         <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 <mailto:swift-evolution at swift.org>
>>         https://lists.swift.org/mailman/listinfo/swift-evolution
>>         <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>
>>
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160824/1f7291d6/attachment.html>


More information about the swift-evolution mailing list