[Idea] Typed Numerics


(Nur Ismail) #1

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 + 5.g + 7.m
………………………………………..^ Compiler Error: Can’t add Distance to Weight...
var distance: Distance = 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 + 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 + 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

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


(Félix Cloutier) #2

Hello,

The idea has been explored a number of times before. In some languages that already implement it, it's called "newtype". You can see previous discussions by googling "site:lists.swift.org newtype". There have also been other ideas going around for type-safe unit calculations (there's one long thread called "Epic: typesafe calculations" that ran for a while).

I remember that the conclusion for type-safe unit calculations was that we didn't want to implement it as a compiler feature, but rather implement compiler features that would make it possible (as it is in C++ <https://github.com/zneak/units-cxx14>, for instance). I don't remember what happened with the newtype debate.

Félix

···

Le 22 août 2016 à 07:54:59, Nur Ismail via swift-evolution <swift-evolution@swift.org> a écrit :

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

"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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Daniel Duan) #3

Reposting comment from Dave A. in a previous thread:

"I'm not sure how useful this is, because it's not really a comment on
your efforts, but... some of us think we know how this problem domain
*should* be addressed, and we know that Swift doesn't yet have the
necessary language facilities (integer generic parameters) to do the job
right. The technique was described back in 1994 by Barton & Nackman in
https://www.amazon.com/Scientific-Engineering-Introduction-Advanced-Techniques/dp/0201533936

We would have the ability to implement dimensional analysis library like the one in Boost today: http://www.boost.org/doc/libs/1_61_0/doc/html/boost_units.html

···

--
Daniel Duan

On August 22, 2016 at 11:54:13 AM, David Sweeris via swift-evolution (swift-evolution@swift.org) wrote:

Wasn't there something about "units" announced at wwdc that does exactly this?

Sent from my iPhone

On Aug 22, 2016, at 09:54, Nur Ismail via swift-evolution <swift-evolution@swift.org> 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 + 5.g + 7.m
………………………………………..^ Compiler Error: Can’t add Distance to Weight...
var distance: Distance = 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 + 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 + 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

"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@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) #4

Wasn't there something about "units" announced at wwdc that does exactly this?

···

Sent from my iPhone

On Aug 22, 2016, at 09:54, Nur Ismail via swift-evolution <swift-evolution@swift.org> 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 + 5.g + 7.m
………………………………………..^ Compiler Error: Can’t add Distance to Weight...
var distance: Distance = 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 + 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 + 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

"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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Alejandro Martinez) #5

This has definitely come up in the list before, maybe somebody else can provide some links to the conversations.
What I would love to see is a generic way of doing newtype, basically just defining a “typealias” but with the difference that the type checker treats it differently.
I’m not sure if it’s the right time to discuss this tough, unless if affects the ABI in a deep way.

Cheers

···

On 22 Aug 2016, at 15:54, Nur Ismail via swift-evolution <swift-evolution@swift.org> 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

"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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Vladimir) #6

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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Rien) #7

I programmed in Ada in about 1990 and used typed numerics all the time.
Missed them ever since…

I would suggest to take a look at Ada and steal the best features to implement typed numerics.

struct Distance: Double(checkedBounds: (lower: 100.0, upper: 10_000.0), delta: 0.1, digits: 10) { … }

Link to the Ada RM on Scalar Types: http://ada-auth.org/standards/12rm/html/RM-3-5.html

The above type definition would use integer calculations, not double, thanks to the ‘delta’ specification.

Rien.

···

On 22 Aug 2016, at 22:13, Daniel Duan via swift-evolution <swift-evolution@swift.org> wrote:

Reposting comment from Dave A. in a previous thread:

"I'm not sure how useful this is, because it's not really a comment on
your efforts, but... some of us think we know how this problem domain
*should* be addressed, and we know that Swift doesn't yet have the
necessary language facilities (integer generic parameters) to do the job
right. The technique was described back in 1994 by Barton & Nackman in
https://www.amazon.com/Scientific-Engineering-Introduction-Advanced-Techniques/dp/0201533936

We would have the ability to implement dimensional analysis library like the one in Boost today: http://www.boost.org/doc/libs/1_61_0/doc/html/boost_units.html

--
Daniel Duan

On August 22, 2016 at 11:54:13 AM, David Sweeris via swift-evolution (swift-evolution@swift.org) wrote:

Wasn't there something about "units" announced at wwdc that does exactly this?

Sent from my iPhone

On Aug 22, 2016, at 09:54, Nur Ismail via swift-evolution <swift-evolution@swift.org> 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 + 5.g + 7.m
………………………………………..^ Compiler Error: Can’t add Distance to Weight...
var distance: Distance = 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 + 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 + 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

"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@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 Abrahams) #8

+1

···

on Mon Aug 22 2016, Félix Cloutier <swift-evolution@swift.org> wrote:

I remember that the conclusion for type-safe unit calculations was
that we didn't want to implement it as a compiler feature, but rather
implement compiler features that would make it possible (as it is in
C++ <https://github.com/zneak/units-cxx14>, for instance).

--
-Dave


(Charlie Monroe) #9

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


(Karl) #10

In that particular case I would say NSTimeInterval should be a struct with convenient initialisers. Similarly to DispatchTime(Interval).

Have you seen the new Foundation Measurement API? http://oleb.net/blog/2016/07/measurements-and-units/

Karl

···

On 22 Aug 2016, at 18:17, Charlie Monroe via swift-evolution <swift-evolution@swift.org> wrote:

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


(Nur Ismail) #11

Hi Vladimir,

Thanks for your code, it's actually quite close to what I want :slight_smile:

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.

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

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, 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 :slight_smile:

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) //or
    circle(radius: 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@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
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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Vladimir) #12

Hi Vladimir,

Thanks for your code, it's actually quite close to what I want :slight_smile:

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

···

On 23.08.2016 12:42, Nur Ismail wrote:

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

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

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@gmail.com > <mailto:svabox@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@swift.org <mailto:swift-evolution@swift.org>
        https://lists.swift.org/mailman/listinfo/swift-evolution
        <https://lists.swift.org/mailman/listinfo/swift-evolution>


(Nur Ismail) #13

Hi Vladimir,

Your latest code is even nicer :slight_smile: 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 :slight_smile:

Regards,

···

On Tue, Aug 23, 2016 at 6:00 PM, Vladimir.S <svabox@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 :slight_smile:

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

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

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@gmail.com >> <mailto:svabox@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@swift.org <mailto:swift-evolution@swift.org>
        https://lists.swift.org/mailman/listinfo/swift-evolution
        <https://lists.swift.org/mailman/listinfo/swift-evolution>