[swift-evolution] [Idea] Typed Numerics

Vladimir.S svabox at gmail.com
Tue Aug 23 11:00:50 CDT 2016


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


More information about the swift-evolution mailing list