Support for newtype feature/typesafe calculations


(Thorsten Seitz) #1

I think the name should be changed to NumberBox or something similar. A box is something very generic and should not have number related associated types.

-Thorsten

···

Am 06. Januar 2016 um 18:15 schrieb James Campbell james@supmenow.com:

I’ve managed to implement this already in the language with a few ugly corners due to the lack of generic protocols.

I created a protocol based on Box (https://github.com/robrix/Box/) which works really well. I have extended this to handle certain special protocols like Equatable so you can do SpecialType == SpecialType, and even literalConversion.

There is however a lot of boilerplate:

  • You have to declare all of your Convertible protocols for converting from one type to another
  • You have to define an empty init so the protocol extensions have something to chain to.
  • You need to write the value property with type.

Due to the lack of protocol generics, you also need to have a protocol for every type you wish to box which sets the associated type. Of course I could have done this with classes but I wanted to keep this as a value type :).

With member-wise initializations and generic protocols this could be achievable just by adding a Box protocol to the standard library.

Here is my implementation of Box as a protocol:

protocol Box: CustomStringConvertible, CustomDebugStringConvertible {


** typealias FloatLiteralType = Double**

** typealias IntegerLiteralType = Int**

** typealias BoxType = Any**


** var value: BoxType { get set }**


** init()**

** init(_ value: BoxType)**

}

extension Box where BoxType: CustomStringConvertible {


** var description: String {**

** return self.value.description**

}


** var debugDescription: String {**

** return “(self.value.description)㎭”**

}

}

//MARK: FloatingPointBox

protocol FloatingPointBox: Box, FloatLiteralConvertible, IntegerLiteralConvertible {


** typealias BoxType = Double**

** typealias FloatLiteralConvertible = Double**

** typealias IntegerLiteralConvertible = Int**

}

extension Box where Self.BoxType == Double {


** init(_ value: Double) {**


** self.init()**

** self.value = value**

}


** init(_ value: Int) {**


** self.init()**

** self.value = Double(value)**

}

}

extension FloatLiteralType {


** init<T: Box where T.BoxType == Double >(_ box: T) {**

** self.init(box.value)**

}


** init<T: Box where T.BoxType == Int >(_ box: T) {**

** self.init(box.value)**

}

}

extension CGFloat {


** init<T: Box where T.BoxType == Double >(_ box: T) {**

** self.init(box.value)**

}


** init<T: Box where T.BoxType == Int >(_ box: T) {**

** self.init(box.value)**

}

}

//Adding FloatLiteralConvertible, IntegerLiteralConvertible

extension FloatingPointBox where Self.BoxType == Double, Self.FloatLiteralConvertible == Double {


** init(floatLiteral value: Double) {**

** self.init(value)**

}


** init(integerLiteral value: Int) {**

** self.init(value)**

}


** init<T: IntegerType>(_ value: T) {**

** self.init(value)**

}

}

Here is my example of using the Box protocol:

struct Degree: FloatingPointBox {


** var value: Double = 0**


** init()**

{

}

}

protocol DegreeConvertiable {


** init(degreeLiteral value: Degree)**

}

extension Degree: RadianConvertiable {


** init(radianLiteral value: Radian) {**

__ self.value = Double(value) * 180.0 / M_PI__

}


** init(_ value: Radian) {**

** self.init(radianLiteral: value)**

}

}

On Tue, Jan 5, 2016 at 5:24 PM, Matthew Johnson via swift-evolution swift-evolution@swift.org wrote:

On Jan 5, 2016, at 11:16 AM, Thorsten Seitz via swift-evolution swift-evolution@swift.org wrote:

Am 05.01.2016 um 17:11 schrieb Grzegorz Adam Hankiewicz via swift-evolution swift-evolution@swift.org:

The ideal would be for the compiler to pretend Euros or RefTablePk are different types, yet use their parent type at the binary level. This needs a specific syntax to teach the compiler which existing methods/operations are allowed on the new fake types and which aren’t. These new distinct types would borrow previous implementations.

What about citing the relevant protocols in the newtype definition? This should include the ability to use my own protocols to which I have made the underlying type conform to by an extension.

This is how my forwarding proposal works. The newtype syntax I suggested as a possible extension looks like this:

newtype Euro = Double forwarding Addable, Subtractable

The keyword could be different, but I think forwarding is not bad. When I complete the second draft I think it will make even more sense. The forwarding facility has features to handle non-trivial cases (Self and associated type requirements, etc).

Throwing some syntax into the discussion:

newtype Euro = Double : Addable, Subtractable

where I have defined the protocols Addable and Subtractable somewhere and made Double conform to them if all this is not provided by the standard library.
The implementation of Euro then borrows the implementation of Double for these protocols.

-Thorsten


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


 Wizard

james@supmenow.com

+44 7523 279 698


(Félix Cloutier) #2

I'd like to hijack this thread to talk about what the TI Voyage 200 could do as far as "type-safe calculations" go.

The calculator supports symbolic equations (for instance, `a * 1000 / 2` results in `500a` when a isn't known). The interesting part is that you could append units to numbers. For instance, you can write `500_m`, and this means 500 meters. Numbers with a type can only be added/subtracted with numbers of the same type, but they can be multiplied or divided by pretty much anything. For instance, `500_m - 2_s` (500 meters minus 2 seconds) is an error, but `500_m / 2_s` is `250 (_m/_s)` (or _m * _s^-1).

I found this *extremely* useful for engineering calculations. For instance, when you multiply quantities that should end up in Teslas, you know that you've done something wrong if the unit displayed after the number doesn't look like `V * s * m^-2`. It was also a lifesaver that you could do something like `6_ft` and end up with 1.8288_m (because _ft is defined as 0.3048_m).

I have no idea how you'd implement that with Swift though. I'm not a very powerful template wizard, but I have no idea how you'd do it with C++ either.

Of course, it might be a few years before you're allowed to use the Swift compiler during your physics exams, and I don't think that real-world programs often need that much unit safety with numbers. But when I read "epic typesafe calculations", that's what I think about.

Félix

···

Le 7 janv. 2016 à 01:42:07, Thorsten Seitz via swift-evolution <swift-evolution@swift.org> a écrit :

I think the name should be changed to NumberBox or something similar. A box is something very generic and should not have number related associated types.

-Thorsten

Am 06. Januar 2016 um 18:15 schrieb James Campbell <james@supmenow.com>:

I've managed to implement this already in the language with a few ugly corners due to the lack of generic protocols.

I created a protocol based on Box (https://github.com/robrix/Box/) which works really well. I have extended this to handle certain special protocols like Equatable so you can do SpecialType == SpecialType, and even literalConversion.

There is however a lot of boilerplate:

- You have to declare all of your Convertible protocols for converting from one type to another
- You have to define an empty init so the protocol extensions have something to chain to.
- You need to write the value property with type.

Due to the lack of protocol generics, you also need to have a protocol for every type you wish to box which sets the associated type. Of course I could have done this with classes but I wanted to keep this as a value type :).

With member-wise initializations and generic protocols this could be achievable just by adding a Box protocol to the standard library.

Here is my implementation of Box as a protocol:

protocol Box: CustomStringConvertible, CustomDebugStringConvertible {

    typealias FloatLiteralType = Double

    typealias IntegerLiteralType = Int

    typealias BoxType = Any

    var value: BoxType { get set }

    init()

    init(_ value: BoxType)

}

extension Box where BoxType: CustomStringConvertible {

    var description: String {

        return self.value.description

    }

    var debugDescription: String {

        return "\(self.value.description)㎭"

    }

}

//MARK: FloatingPointBox

protocol FloatingPointBox: Box, FloatLiteralConvertible, IntegerLiteralConvertible {

    typealias BoxType = Double

    typealias FloatLiteralConvertible = Double

    typealias IntegerLiteralConvertible = Int

}

extension Box where Self.BoxType == Double {

    init(_ value: Double) {

        self.init()

        self.value = value

    }

    init(_ value: Int) {

        self.init()

        self.value = Double(value)

    }

}

extension FloatLiteralType {

    init<T: Box where T.BoxType == Double >(_ box: T) {

        self.init(box.value)

    }

    init<T: Box where T.BoxType == Int >(_ box: T) {

        self.init(box.value)

    }

}

extension CGFloat {

    init<T: Box where T.BoxType == Double >(_ box: T) {

        self.init(box.value)

    }

    init<T: Box where T.BoxType == Int >(_ box: T) {

        self.init(box.value)

    }

}

//Adding FloatLiteralConvertible, IntegerLiteralConvertible

extension FloatingPointBox where Self.BoxType == Double, Self.FloatLiteralConvertible == Double {

    init(floatLiteral value: Double) {

        self.init(value)

    }

    init(integerLiteral value: Int) {

        self.init(value)

    }

    init<T: IntegerType>(_ value: T) {

        self.init(value)

    }

}

Here is my example of using the Box protocol:
struct Degree: FloatingPointBox {

    var value: Double = 0

    init()

    {

    }

}

protocol DegreeConvertiable {

    init(degreeLiteral value: Degree)

}

extension Degree: RadianConvertiable {

    init(radianLiteral value: Radian) {

        self.value = Double(value) * 180.0 / M_PI

    }

    init(_ value: Radian) {

        self.init(radianLiteral: value)

    }

}

On Tue, Jan 5, 2016 at 5:24 PM, Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

> On Jan 5, 2016, at 11:16 AM, Thorsten Seitz via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>
>
>> Am 05.01.2016 um 17:11 schrieb Grzegorz Adam Hankiewicz via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:
>>
>> The ideal would be for the compiler to pretend Euros or RefTablePk are different types, yet use their parent type at the binary level. This needs a specific syntax to teach the compiler which existing methods/operations are allowed on the new fake types and which aren’t. These new distinct types would *borrow* previous implementations.
>
> What about citing the relevant protocols in the newtype definition? This should include the ability to use my own protocols to which I have made the underlying type conform to by an extension.

This is how my forwarding proposal works. The newtype syntax I suggested as a possible extension looks like this:

newtype Euro = Double forwarding Addable, Subtractable

The keyword could be different, but I think `forwarding` is not bad. When I complete the second draft I think it will make even more sense. The forwarding facility has features to handle non-trivial cases (Self and associated type requirements, etc).

>
> Throwing some syntax into the discussion:
>
> newtype Euro = Double : Addable, Subtractable
>
> where I have defined the protocols Addable and Subtractable somewhere and made Double conform to them if all this is not provided by the standard library.
> The implementation of Euro then borrows the implementation of Double for these protocols.
>
> -Thorsten
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

--
 Wizard
james@supmenow.com <mailto:james@supmenow.com>
+44 7523 279 698

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


(James Campbell) #3

So this is how this feature could be achieved right now with minimal
changes to the language.

- We expose some kind of protocol that allows you to Box up types, we could
call this `Box` or something else like `Unit`. (I have a working
implementation in the current language with a bit of boilerplate). This
protocol handles the default implementation of converting from literals and
to floats etc.
- For each type-safe unit for a calculation - you define a protocol
extending this `Box` type which defines the associated type of the value
that unit holds. For example for Degree and Radian I declared a `AngleType`
which set the associated type to be a double.
- For each unit type, you declare a struct that inherits from that protocol
you defined. So I have two structs `Degree` and `Radian` which implement
the `AngleType` protocol.
- You implement the functions for figuring out if your units are equal and
all other operators they may need i.e `Degree(360) - 30`.

Future improvements with language updates:

- `AngleType` protocol may not be needed if swift introduces generic
protocols.
- Boilerplate in your type safe unit types may be reduced if swift
introduces memberwise initialization.
- The current implementation of this system in Swift will be greatly
simplified once the refactoring of Swift's Number types has been completed.
We currently use a bunch of work arounds with the compiler.
- We could introduce custom user literals to create these types if swift
supports this in the future (As Felix states).

···

On Thu, Jan 7, 2016 at 7:06 AM, Félix Cloutier <felixcca@yahoo.ca> wrote:

I'd like to hijack this thread to talk about what the TI Voyage 200 could
do as far as "type-safe calculations" go.

The calculator supports symbolic equations (for instance, `a * 1000 / 2`
results in `500a` when a isn't known). The interesting part is that you
could append units to numbers. For instance, you can write `500_m`, and
this means 500 meters. Numbers with a type can only be added/subtracted
with numbers of the same type, but they can be multiplied or divided by
pretty much anything. For instance, `500_m - 2_s` (500 meters minus 2
seconds) is an error, but `500_m / 2_s` is `250 (_m/_s)` (or _m * _s^-1).

I found this *extremely* useful for engineering calculations. For
instance, when you multiply quantities that should end up in Teslas, you
know that you've done something wrong if the unit displayed after the
number doesn't look like `V * s * m^-2`. It was also a lifesaver that you
could do something like `6_ft` and end up with 1.8288_m (because _ft is
defined as 0.3048_m).

I have no idea how you'd implement that with Swift though. I'm not a very
powerful template wizard, but I have no idea how you'd do it with C++
either.

Of course, it might be a few years before you're allowed to use the Swift
compiler during your physics exams, and I don't think that real-world
programs often need that much unit safety with numbers. But when I read
"epic typesafe calculations", that's what I think about.

Félix

Le 7 janv. 2016 à 01:42:07, Thorsten Seitz via swift-evolution < > swift-evolution@swift.org> a écrit :

I think the name should be changed to NumberBox or something similar. A
box is something very generic and should not have number related associated
types.

-Thorsten

Am 06. Januar 2016 um 18:15 schrieb James Campbell <james@supmenow.com>:

I've managed to implement this already in the language with a few ugly
corners due to the lack of generic protocols.

I created a protocol based on Box (https://github.com/robrix/Box/) which
works really well. I have extended this to handle certain special protocols
like Equatable so you can do SpecialType == SpecialType, and even
literalConversion.

There is however a lot of boilerplate:

- You have to declare all of your Convertible protocols for converting
from one type to another
- You have to define an empty init so the protocol extensions have
something to chain to.
- You need to write the value property with type.

Due to the lack of protocol generics, you also need to have a protocol for
every type you wish to box which sets the associated type. Of course I
could have done this with classes but I wanted to keep this as a value type
:).

With member-wise initializations and generic protocols this could be
achievable just by adding a Box protocol to the standard library.

Here is my implementation of Box as a protocol:

*protocol Box: CustomStringConvertible, CustomDebugStringConvertible {*

* typealias FloatLiteralType = Double*

* typealias IntegerLiteralType = Int*

* typealias BoxType = Any*

* var value: BoxType { get set }*

* init()*

* init(_ value: BoxType)*

*}*

*extension Box where BoxType: CustomStringConvertible {*

* var description: String {*

* return self.value.description*

* }*

* var debugDescription: String {*

* return "\(self.value.description)㎭"*

* }*

*}*

*//MARK: FloatingPointBox*

*protocol FloatingPointBox: Box, FloatLiteralConvertible,
IntegerLiteralConvertible {*

* typealias BoxType = Double*

* typealias FloatLiteralConvertible = Double*

* typealias IntegerLiteralConvertible = Int*

*}*

*extension Box where Self.BoxType == Double {*

* init(_ value: Double) {*

* self.init()*

* self.value = value*

* }*

* init(_ value: Int) {*

* self.init()*

* self.value = Double(value)*

* }*

*}*

*extension FloatLiteralType {*

* init<T: Box where T.BoxType == Double >(_ box: T) {*

* self.init(box.value)*

* }*

* init<T: Box where T.BoxType == Int >(_ box: T) {*

* self.init(box.value)*

* }*

*}*

*extension CGFloat {*

* init<T: Box where T.BoxType == Double >(_ box: T) {*

* self.init(box.value)*

* }*

* init<T: Box where T.BoxType == Int >(_ box: T) {*

* self.init(box.value)*

* }*

*}*

*//Adding FloatLiteralConvertible, IntegerLiteralConvertible*

*extension FloatingPointBox where Self.BoxType == Double,
Self.FloatLiteralConvertible == Double {*

* init(floatLiteral value: Double) {*

* self.init(value)*

* }*

* init(integerLiteral value: Int) {*

* self.init(value)*

* }*

* init<T: IntegerType>(_ value: T) {*

* self.init(value)*

* }*

*}*

Here is my example of using the Box protocol:

*struct Degree: FloatingPointBox {*

* var value: Double = 0*

* init()*

* {*

* }*

*}*

*protocol DegreeConvertiable {*

* init(degreeLiteral value: Degree)*

*}*

*extension Degree: RadianConvertiable {*

* init(radianLiteral value: Radian) {*

* self.value = Double(value) * 180.0 / M_PI*

* }*

* init(_ value: Radian) {*

* self.init(radianLiteral: value)*

* }*

*}*

On Tue, Jan 5, 2016 at 5:24 PM, Matthew Johnson via swift-evolution < > swift-evolution@swift.org> wrote:

> On Jan 5, 2016, at 11:16 AM, Thorsten Seitz via swift-evolution < >> swift-evolution@swift.org> wrote:
>
>
>> Am 05.01.2016 um 17:11 schrieb Grzegorz Adam Hankiewicz via >> swift-evolution <swift-evolution@swift.org>:
>>
>> The ideal would be for the compiler to pretend Euros or RefTablePk are
different types, yet use their parent type at the binary level. This needs
a specific syntax to teach the compiler which existing methods/operations
are allowed on the new fake types and which aren’t. These new distinct
types would *borrow* previous implementations.
>
> What about citing the relevant protocols in the newtype definition?
This should include the ability to use my own protocols to which I have
made the underlying type conform to by an extension.

This is how my forwarding proposal works. The newtype syntax I suggested
as a possible extension looks like this:

newtype Euro = Double forwarding Addable, Subtractable

The keyword could be different, but I think `forwarding` is not bad.
When I complete the second draft I think it will make even more sense. The
forwarding facility has features to handle non-trivial cases (Self and
associated type requirements, etc).

>
> Throwing some syntax into the discussion:
>
> newtype Euro = Double : Addable, Subtractable
>
> where I have defined the protocols Addable and Subtractable somewhere
and made Double conform to them if all this is not provided by the standard
library.
> The implementation of Euro then borrows the implementation of Double
for these protocols.
>
> -Thorsten
> _______________________________________________
> 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

--
 Wizard
james@supmenow.com
+44 7523 279 698

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

--
 Wizard
james@supmenow.com
+44 7523 279 698


(Grzegorz Adam Hankiewicz) #4

I’d like to hijack this thread to talk about what the TI Voyage 200 could do as far as "type-safe calculations" go.

I don’t think you are hijacking it, I share your vision, but it seems drowned among all the protocol-wielding users.

I’m not a very powerful template wizard, but I have no idea how you'd do it with C++ either.

See http://stackoverflow.com/questions/23726038/how-can-i-create-a-new-primitive-type-using-c11-style-strong-typedefs for that.

···

El 7/1/2016, a las 8:06, Félix Cloutier <felixcca@yahoo.ca> escribió:


(Tino) #5

that much unit safety with numbers. But when I read "epic typesafe calculations", that's what I think about.

me to :wink: — and I want this to work with force vectors, matrices and tensors as well.
But I'm quite sure that composed units are the hardest part, and I don't think it will be possible in Swift 3.

I would like to start with tiny steps, and afaics, we miss a protocol that denotes that something can be added and substracted, don't we?

So, I think a first tiny proposal would be to add protocols for groups, rings & bodys, and add make the numeric types conform to them.

Meanwhile, I pushed a tiny playground to github - it is quite chaotic, but it allows you to do stuff like
let f0 = Force.Newton(1)
let f1 = Force.Kilopound(1)
print(10.0*f0 + f1/444.8221615255)

https://github.com/SwiftTypesafeCalculations/Units

I did also start an "organization" at github and to see how this infrastructure can be leveraged to channel the discussion without loosing track.

Tino


(John Randolph) #6

I took a different approach to this, which was to typealias Angle to Double, keep all Angles as radians (since the stdlib trig functions take radians), and add some computed setters and getters to let me access an Angle in radians, degrees, or grads.

I would like to see Angle, along with several other basic geometric concepts, promoted to the Swift standard library.

-jcr

···

On Jan 7, 2016, at 4:06 AM, James Campbell via swift-evolution <swift-evolution@swift.org> wrote:

So this is how this feature could be achieved right now with minimal changes to the language.

- We expose some kind of protocol that allows you to Box up types, we could call this `Box` or something else like `Unit`. (I have a working implementation in the current language with a bit of boilerplate). This protocol handles the default implementation of converting from literals and to floats etc.
- For each type-safe unit for a calculation - you define a protocol extending this `Box` type which defines the associated type of the value that unit holds. For example for Degree and Radian I declared a `AngleType` which set the associated type to be a double.
- For each unit type, you declare a struct that inherits from that protocol you defined. So I have two structs `Degree` and `Radian` which implement the `AngleType` protocol.
- You implement the functions for figuring out if your units are equal and all other operators they may need i.e `Degree(360) - 30`.


(James Campbell) #7

This is my current implementation of this pattern.

TypeSafeUnits.zip (33.2 KB)

···

On Thu, Jan 7, 2016 at 12:06 PM, James Campbell <james@supmenow.com> wrote:

So this is how this feature could be achieved right now with minimal
changes to the language.

- We expose some kind of protocol that allows you to Box up types, we
could call this `Box` or something else like `Unit`. (I have a working
implementation in the current language with a bit of boilerplate). This
protocol handles the default implementation of converting from literals and
to floats etc.
- For each type-safe unit for a calculation - you define a protocol
extending this `Box` type which defines the associated type of the value
that unit holds. For example for Degree and Radian I declared a `AngleType`
which set the associated type to be a double.
- For each unit type, you declare a struct that inherits from that
protocol you defined. So I have two structs `Degree` and `Radian` which
implement the `AngleType` protocol.
- You implement the functions for figuring out if your units are equal and
all other operators they may need i.e `Degree(360) - 30`.

Future improvements with language updates:

- `AngleType` protocol may not be needed if swift introduces generic
protocols.
- Boilerplate in your type safe unit types may be reduced if swift
introduces memberwise initialization.
- The current implementation of this system in Swift will be greatly
simplified once the refactoring of Swift's Number types has been completed.
We currently use a bunch of work arounds with the compiler.
- We could introduce custom user literals to create these types if swift
supports this in the future (As Felix states).

On Thu, Jan 7, 2016 at 7:06 AM, Félix Cloutier <felixcca@yahoo.ca> wrote:

I'd like to hijack this thread to talk about what the TI Voyage 200 could
do as far as "type-safe calculations" go.

The calculator supports symbolic equations (for instance, `a * 1000 / 2`
results in `500a` when a isn't known). The interesting part is that you
could append units to numbers. For instance, you can write `500_m`, and
this means 500 meters. Numbers with a type can only be added/subtracted
with numbers of the same type, but they can be multiplied or divided by
pretty much anything. For instance, `500_m - 2_s` (500 meters minus 2
seconds) is an error, but `500_m / 2_s` is `250 (_m/_s)` (or _m * _s^-1).

I found this *extremely* useful for engineering calculations. For
instance, when you multiply quantities that should end up in Teslas, you
know that you've done something wrong if the unit displayed after the
number doesn't look like `V * s * m^-2`. It was also a lifesaver that you
could do something like `6_ft` and end up with 1.8288_m (because _ft is
defined as 0.3048_m).

I have no idea how you'd implement that with Swift though. I'm not a very
powerful template wizard, but I have no idea how you'd do it with C++
either.

Of course, it might be a few years before you're allowed to use the Swift
compiler during your physics exams, and I don't think that real-world
programs often need that much unit safety with numbers. But when I read
"epic typesafe calculations", that's what I think about.

Félix

Le 7 janv. 2016 à 01:42:07, Thorsten Seitz via swift-evolution < >> swift-evolution@swift.org> a écrit :

I think the name should be changed to NumberBox or something similar. A
box is something very generic and should not have number related associated
types.

-Thorsten

Am 06. Januar 2016 um 18:15 schrieb James Campbell <james@supmenow.com>:

I've managed to implement this already in the language with a few ugly
corners due to the lack of generic protocols.

I created a protocol based on Box (https://github.com/robrix/Box/) which
works really well. I have extended this to handle certain special protocols
like Equatable so you can do SpecialType == SpecialType, and even
literalConversion.

There is however a lot of boilerplate:

- You have to declare all of your Convertible protocols for converting
from one type to another
- You have to define an empty init so the protocol extensions have
something to chain to.
- You need to write the value property with type.

Due to the lack of protocol generics, you also need to have a protocol
for every type you wish to box which sets the associated type. Of course I
could have done this with classes but I wanted to keep this as a value type
:).

With member-wise initializations and generic protocols this could be
achievable just by adding a Box protocol to the standard library.

Here is my implementation of Box as a protocol:

*protocol Box: CustomStringConvertible, CustomDebugStringConvertible {*

* typealias FloatLiteralType = Double*

* typealias IntegerLiteralType = Int*

* typealias BoxType = Any*

* var value: BoxType { get set }*

* init()*

* init(_ value: BoxType)*

*}*

*extension Box where BoxType: CustomStringConvertible {*

* var description: String {*

* return self.value.description*

* }*

* var debugDescription: String {*

* return "\(self.value.description)㎭"*

* }*

*}*

*//MARK: FloatingPointBox*

*protocol FloatingPointBox: Box, FloatLiteralConvertible,
IntegerLiteralConvertible {*

* typealias BoxType = Double*

* typealias FloatLiteralConvertible = Double*

* typealias IntegerLiteralConvertible = Int*

*}*

*extension Box where Self.BoxType == Double {*

* init(_ value: Double) {*

* self.init()*

* self.value = value*

* }*

* init(_ value: Int) {*

* self.init()*

* self.value = Double(value)*

* }*

*}*

*extension FloatLiteralType {*

* init<T: Box where T.BoxType == Double >(_ box: T) {*

* self.init(box.value)*

* }*

* init<T: Box where T.BoxType == Int >(_ box: T) {*

* self.init(box.value)*

* }*

*}*

*extension CGFloat {*

* init<T: Box where T.BoxType == Double >(_ box: T) {*

* self.init(box.value)*

* }*

* init<T: Box where T.BoxType == Int >(_ box: T) {*

* self.init(box.value)*

* }*

*}*

*//Adding FloatLiteralConvertible, IntegerLiteralConvertible*

*extension FloatingPointBox where Self.BoxType == Double,
Self.FloatLiteralConvertible == Double {*

* init(floatLiteral value: Double) {*

* self.init(value)*

* }*

* init(integerLiteral value: Int) {*

* self.init(value)*

* }*

* init<T: IntegerType>(_ value: T) {*

* self.init(value)*

* }*

*}*

Here is my example of using the Box protocol:

*struct Degree: FloatingPointBox {*

* var value: Double = 0*

* init()*

* {*

* }*

*}*

*protocol DegreeConvertiable {*

* init(degreeLiteral value: Degree)*

*}*

*extension Degree: RadianConvertiable {*

* init(radianLiteral value: Radian) {*

* self.value = Double(value) * 180.0 / M_PI*

* }*

* init(_ value: Radian) {*

* self.init(radianLiteral: value)*

* }*

*}*

On Tue, Jan 5, 2016 at 5:24 PM, Matthew Johnson via swift-evolution < >> swift-evolution@swift.org> wrote:

> On Jan 5, 2016, at 11:16 AM, Thorsten Seitz via swift-evolution < >>> swift-evolution@swift.org> wrote:
>
>
>> Am 05.01.2016 um 17:11 schrieb Grzegorz Adam Hankiewicz via >>> swift-evolution <swift-evolution@swift.org>:
>>
>> The ideal would be for the compiler to pretend Euros or RefTablePk
are different types, yet use their parent type at the binary level. This
needs a specific syntax to teach the compiler which existing
methods/operations are allowed on the new fake types and which aren’t.
These new distinct types would *borrow* previous implementations.
>
> What about citing the relevant protocols in the newtype definition?
This should include the ability to use my own protocols to which I have
made the underlying type conform to by an extension.

This is how my forwarding proposal works. The newtype syntax I
suggested as a possible extension looks like this:

newtype Euro = Double forwarding Addable, Subtractable

The keyword could be different, but I think `forwarding` is not bad.
When I complete the second draft I think it will make even more sense. The
forwarding facility has features to handle non-trivial cases (Self and
associated type requirements, etc).

>
> Throwing some syntax into the discussion:
>
> newtype Euro = Double : Addable, Subtractable
>
> where I have defined the protocols Addable and Subtractable somewhere
and made Double conform to them if all this is not provided by the standard
library.
> The implementation of Euro then borrows the implementation of Double
for these protocols.
>
> -Thorsten
> _______________________________________________
> 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

--
 Wizard
james@supmenow.com
+44 7523 279 698

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

--
 Wizard
james@supmenow.com
+44 7523 279 698

--
 Wizard
james@supmenow.com
+44 7523 279 698


(Félix Cloutier) #8

James, that works for degrees to radians, but I'm afraid that one protocol per unit simply can't scale if you want to multiply/divide quantities with different units. Just look at the different things can make up a tesla <https://upload.wikimedia.org/math/d/c/1/dc1af6ed32aa3e906230cc15ea08a2a4.png>. The combination of units is factorial and defining like 24 protocols each with multiplication and division operation to every other defined protocol doesn't make sense. I'd rather use "untyped" quantities if I had to do the same thing in Swift right now.

(By the way: Degree(360) - 30 should probably be Degree(360) - Degree(30), no? I'm not sure whether 30 is expressed in degrees or radians :slight_smile: )

The important parts in the outlined vision are that:

the unit information is available as type information to the compiler; it checks that additions and subtractions use the same units and uses the correct derived type when you multiply/divide quantities with units;
you can easily add new "fundamental" units that would be very hard or impossible to derive from other units (meters, seconds, kilograms, radians, etc);
you can easily derive units from fundamental units (N = kg * m^1 * s^-2).

Right now, we can do #1. If this is what we're going for, then newtype isn't very important because it doesn't contribute that much to anything here (though if we decide that we don't need that much power, it would be a decent crutch).

Félix

···

Le 7 janv. 2016 à 07:06:01, James Campbell <james@supmenow.com> a écrit :

So this is how this feature could be achieved right now with minimal changes to the language.

- We expose some kind of protocol that allows you to Box up types, we could call this `Box` or something else like `Unit`. (I have a working implementation in the current language with a bit of boilerplate). This protocol handles the default implementation of converting from literals and to floats etc.
- For each type-safe unit for a calculation - you define a protocol extending this `Box` type which defines the associated type of the value that unit holds. For example for Degree and Radian I declared a `AngleType` which set the associated type to be a double.
- For each unit type, you declare a struct that inherits from that protocol you defined. So I have two structs `Degree` and `Radian` which implement the `AngleType` protocol.
- You implement the functions for figuring out if your units are equal and all other operators they may need i.e `Degree(360) - 30`.

Future improvements with language updates:

- `AngleType` protocol may not be needed if swift introduces generic protocols.
- Boilerplate in your type safe unit types may be reduced if swift introduces memberwise initialization.
- The current implementation of this system in Swift will be greatly simplified once the refactoring of Swift's Number types has been completed. We currently use a bunch of work arounds with the compiler.
- We could introduce custom user literals to create these types if swift supports this in the future (As Felix states).

On Thu, Jan 7, 2016 at 7:06 AM, Félix Cloutier <felixcca@yahoo.ca <mailto:felixcca@yahoo.ca>> wrote:
I'd like to hijack this thread to talk about what the TI Voyage 200 could do as far as "type-safe calculations" go.

The calculator supports symbolic equations (for instance, `a * 1000 / 2` results in `500a` when a isn't known). The interesting part is that you could append units to numbers. For instance, you can write `500_m`, and this means 500 meters. Numbers with a type can only be added/subtracted with numbers of the same type, but they can be multiplied or divided by pretty much anything. For instance, `500_m - 2_s` (500 meters minus 2 seconds) is an error, but `500_m / 2_s` is `250 (_m/_s)` (or _m * _s^-1).

I found this *extremely* useful for engineering calculations. For instance, when you multiply quantities that should end up in Teslas, you know that you've done something wrong if the unit displayed after the number doesn't look like `V * s * m^-2`. It was also a lifesaver that you could do something like `6_ft` and end up with 1.8288_m (because _ft is defined as 0.3048_m).

I have no idea how you'd implement that with Swift though. I'm not a very powerful template wizard, but I have no idea how you'd do it with C++ either.

Of course, it might be a few years before you're allowed to use the Swift compiler during your physics exams, and I don't think that real-world programs often need that much unit safety with numbers. But when I read "epic typesafe calculations", that's what I think about.

Félix

Le 7 janv. 2016 à 01:42:07, Thorsten Seitz via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> a écrit :

I think the name should be changed to NumberBox or something similar. A box is something very generic and should not have number related associated types.

-Thorsten

Am 06. Januar 2016 um 18:15 schrieb James Campbell <james@supmenow.com <mailto:james@supmenow.com>>:

I've managed to implement this already in the language with a few ugly corners due to the lack of generic protocols.

I created a protocol based on Box (https://github.com/robrix/Box/) which works really well. I have extended this to handle certain special protocols like Equatable so you can do SpecialType == SpecialType, and even literalConversion.

There is however a lot of boilerplate:

- You have to declare all of your Convertible protocols for converting from one type to another
- You have to define an empty init so the protocol extensions have something to chain to.
- You need to write the value property with type.

Due to the lack of protocol generics, you also need to have a protocol for every type you wish to box which sets the associated type. Of course I could have done this with classes but I wanted to keep this as a value type :).

With member-wise initializations and generic protocols this could be achievable just by adding a Box protocol to the standard library.

Here is my implementation of Box as a protocol:

protocol Box: CustomStringConvertible, CustomDebugStringConvertible {

    typealias FloatLiteralType = Double

    typealias IntegerLiteralType = Int

    typealias BoxType = Any

    var value: BoxType { get set }

    init()

    init(_ value: BoxType)

}

extension Box where BoxType: CustomStringConvertible {

    var description: String {

        return self.value.description

    }

    var debugDescription: String {

        return "\(self.value.description)㎭"

    }

}

//MARK: FloatingPointBox

protocol FloatingPointBox: Box, FloatLiteralConvertible, IntegerLiteralConvertible {

    typealias BoxType = Double

    typealias FloatLiteralConvertible = Double

    typealias IntegerLiteralConvertible = Int

}

extension Box where Self.BoxType == Double {

    init(_ value: Double) {

        self.init()

        self.value = value

    }

    init(_ value: Int) {

        self.init()

        self.value = Double(value)

    }

}

extension FloatLiteralType {

    init<T: Box where T.BoxType == Double >(_ box: T) {

        self.init(box.value)

    }

    init<T: Box where T.BoxType == Int >(_ box: T) {

        self.init(box.value)

    }

}

extension CGFloat {

    init<T: Box where T.BoxType == Double >(_ box: T) {

        self.init(box.value)

    }

    init<T: Box where T.BoxType == Int >(_ box: T) {

        self.init(box.value)

    }

}

//Adding FloatLiteralConvertible, IntegerLiteralConvertible

extension FloatingPointBox where Self.BoxType == Double, Self.FloatLiteralConvertible == Double {

    init(floatLiteral value: Double) {

        self.init(value)

    }

    init(integerLiteral value: Int) {

        self.init(value)

    }

    init<T: IntegerType>(_ value: T) {

        self.init(value)

    }

}

Here is my example of using the Box protocol:
struct Degree: FloatingPointBox {

    var value: Double = 0

    init()

    {

    }

}

protocol DegreeConvertiable {

    init(degreeLiteral value: Degree)

}

extension Degree: RadianConvertiable {

    init(radianLiteral value: Radian) {

        self.value = Double(value) * 180.0 / M_PI

    }

    init(_ value: Radian) {

        self.init(radianLiteral: value)

    }

}

On Tue, Jan 5, 2016 at 5:24 PM, Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

> On Jan 5, 2016, at 11:16 AM, Thorsten Seitz via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>
>
>> Am 05.01.2016 um 17:11 schrieb Grzegorz Adam Hankiewicz via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:
>>
>> The ideal would be for the compiler to pretend Euros or RefTablePk are different types, yet use their parent type at the binary level. This needs a specific syntax to teach the compiler which existing methods/operations are allowed on the new fake types and which aren’t. These new distinct types would *borrow* previous implementations.
>
> What about citing the relevant protocols in the newtype definition? This should include the ability to use my own protocols to which I have made the underlying type conform to by an extension.

This is how my forwarding proposal works. The newtype syntax I suggested as a possible extension looks like this:

newtype Euro = Double forwarding Addable, Subtractable

The keyword could be different, but I think `forwarding` is not bad. When I complete the second draft I think it will make even more sense. The forwarding facility has features to handle non-trivial cases (Self and associated type requirements, etc).

>
> Throwing some syntax into the discussion:
>
> newtype Euro = Double : Addable, Subtractable
>
> where I have defined the protocols Addable and Subtractable somewhere and made Double conform to them if all this is not provided by the standard library.
> The implementation of Euro then borrows the implementation of Double for these protocols.
>
> -Thorsten
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

--
 Wizard
james@supmenow.com <mailto:james@supmenow.com>
+44 7523 279 698 <tel:%2B44%207523%20279%20698>

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

--
 Wizard
james@supmenow.com <mailto:james@supmenow.com>
+44 7523 279 698


(John Randolph) #9

I took a different approach to this, which was to just typealias Angle to Double, keep all Angle values as radians (since the stdlib trig functions take radians), and add some computed setters and getters to let me access an Angle in radians, degrees, or grads.

I would like to see Angle, along with several other basic geometric concepts, promoted to the Swift standard library.

-jcr

···

On Jan 7, 2016, at 4:06 AM, James Campbell via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

So this is how this feature could be achieved right now with minimal changes to the language.

- We expose some kind of protocol that allows you to Box up types, we could call this `Box` or something else like `Unit`. (I have a working implementation in the current language with a bit of boilerplate). This protocol handles the default implementation of converting from literals and to floats etc.
- For each type-safe unit for a calculation - you define a protocol extending this `Box` type which defines the associated type of the value that unit holds. For example for Degree and Radian I declared a `AngleType` which set the associated type to be a double.
- For each unit type, you declare a struct that inherits from that protocol you defined. So I have two structs `Degree` and `Radian` which implement the `AngleType` protocol.
- You implement the functions for figuring out if your units are equal and all other operators they may need i.e `Degree(360) - 30`.


(James Campbell) #10

I understand, I want to keep prototyping this with other units to see what
insight I can achieve.

Do we have a list of units we would like to model to serve as a suite of
tests and also to test any implementation we may need ?

···

On Thu, Jan 7, 2016 at 5:10 PM, Félix Cloutier <felixcca@yahoo.ca> wrote:

James, that works for degrees to radians, but I'm afraid that one protocol
per unit simply can't scale if you want to multiply/divide quantities with
different units. Just look at the different things can make up a tesla
<https://upload.wikimedia.org/math/d/c/1/dc1af6ed32aa3e906230cc15ea08a2a4.png>.
The combination of units is factorial and defining like 24 protocols each
with multiplication and division operation to every other defined protocol
doesn't make sense. I'd rather use "untyped" quantities if I had to do the
same thing in Swift right now.

(By the way: Degree(360) - 30 should probably be Degree(360) - Degree(30),
no? I'm not sure whether 30 is expressed in degrees or radians :slight_smile: )

The important parts in the outlined vision are that:

   1. the unit information is available as type information to the
   compiler; it checks that additions and subtractions use the same units and
   uses the correct derived type when you multiply/divide quantities with
   units;
   2. you can easily add new "fundamental" units that would be very hard
   or impossible to derive from other units (meters, seconds, kilograms,
   radians, etc);
   3. you can easily derive units from fundamental units (N = kg * m^1 *
   s^-2).

Right now, we can do #1. If this is what we're going for, then newtype
isn't very important because it doesn't contribute that much to anything
here (though if we decide that we don't need that much power, it would be a
decent crutch).

Félix

Le 7 janv. 2016 à 07:06:01, James Campbell <james@supmenow.com> a écrit :

So this is how this feature could be achieved right now with minimal
changes to the language.

- We expose some kind of protocol that allows you to Box up types, we
could call this `Box` or something else like `Unit`. (I have a working
implementation in the current language with a bit of boilerplate). This
protocol handles the default implementation of converting from literals and
to floats etc.
- For each type-safe unit for a calculation - you define a protocol
extending this `Box` type which defines the associated type of the value
that unit holds. For example for Degree and Radian I declared a `AngleType`
which set the associated type to be a double.
- For each unit type, you declare a struct that inherits from that
protocol you defined. So I have two structs `Degree` and `Radian` which
implement the `AngleType` protocol.
- You implement the functions for figuring out if your units are equal and
all other operators they may need i.e `Degree(360) - 30`.

Future improvements with language updates:

- `AngleType` protocol may not be needed if swift introduces generic
protocols.
- Boilerplate in your type safe unit types may be reduced if swift
introduces memberwise initialization.
- The current implementation of this system in Swift will be greatly
simplified once the refactoring of Swift's Number types has been completed.
We currently use a bunch of work arounds with the compiler.
- We could introduce custom user literals to create these types if swift
supports this in the future (As Felix states).

On Thu, Jan 7, 2016 at 7:06 AM, Félix Cloutier <felixcca@yahoo.ca> wrote:

I'd like to hijack this thread to talk about what the TI Voyage 200 could
do as far as "type-safe calculations" go.

The calculator supports symbolic equations (for instance, `a * 1000 / 2`
results in `500a` when a isn't known). The interesting part is that you
could append units to numbers. For instance, you can write `500_m`, and
this means 500 meters. Numbers with a type can only be added/subtracted
with numbers of the same type, but they can be multiplied or divided by
pretty much anything. For instance, `500_m - 2_s` (500 meters minus 2
seconds) is an error, but `500_m / 2_s` is `250 (_m/_s)` (or _m * _s^-1).

I found this *extremely* useful for engineering calculations. For
instance, when you multiply quantities that should end up in Teslas, you
know that you've done something wrong if the unit displayed after the
number doesn't look like `V * s * m^-2`. It was also a lifesaver that you
could do something like `6_ft` and end up with 1.8288_m (because _ft is
defined as 0.3048_m).

I have no idea how you'd implement that with Swift though. I'm not a very
powerful template wizard, but I have no idea how you'd do it with C++
either.

Of course, it might be a few years before you're allowed to use the Swift
compiler during your physics exams, and I don't think that real-world
programs often need that much unit safety with numbers. But when I read
"epic typesafe calculations", that's what I think about.

Félix

Le 7 janv. 2016 à 01:42:07, Thorsten Seitz via swift-evolution < >> swift-evolution@swift.org> a écrit :

I think the name should be changed to NumberBox or something similar. A
box is something very generic and should not have number related associated
types.

-Thorsten

Am 06. Januar 2016 um 18:15 schrieb James Campbell <james@supmenow.com>:

I've managed to implement this already in the language with a few ugly
corners due to the lack of generic protocols.

I created a protocol based on Box (https://github.com/robrix/Box/) which
works really well. I have extended this to handle certain special protocols
like Equatable so you can do SpecialType == SpecialType, and even
literalConversion.

There is however a lot of boilerplate:

- You have to declare all of your Convertible protocols for converting
from one type to another
- You have to define an empty init so the protocol extensions have
something to chain to.
- You need to write the value property with type.

Due to the lack of protocol generics, you also need to have a protocol
for every type you wish to box which sets the associated type. Of course I
could have done this with classes but I wanted to keep this as a value type
:).

With member-wise initializations and generic protocols this could be
achievable just by adding a Box protocol to the standard library.

Here is my implementation of Box as a protocol:

*protocol Box: CustomStringConvertible, CustomDebugStringConvertible {*

* typealias FloatLiteralType = Double*

* typealias IntegerLiteralType = Int*

* typealias BoxType = Any*

* var value: BoxType { get set }*

* init()*

* init(_ value: BoxType)*

*}*

*extension Box where BoxType: CustomStringConvertible {*

* var description: String {*

* return self.value.description*

* }*

* var debugDescription: String {*

* return "\(self.value.description)㎭"*

* }*

*}*

*//MARK: FloatingPointBox*

*protocol FloatingPointBox: Box, FloatLiteralConvertible,
IntegerLiteralConvertible {*

* typealias BoxType = Double*

* typealias FloatLiteralConvertible = Double*

* typealias IntegerLiteralConvertible = Int*

*}*

*extension Box where Self.BoxType == Double {*

* init(_ value: Double) {*

* self.init()*

* self.value = value*

* }*

* init(_ value: Int) {*

* self.init()*

* self.value = Double(value)*

* }*

*}*

*extension FloatLiteralType {*

* init<T: Box where T.BoxType == Double >(_ box: T) {*

* self.init(box.value)*

* }*

* init<T: Box where T.BoxType == Int >(_ box: T) {*

* self.init(box.value)*

* }*

*}*

*extension CGFloat {*

* init<T: Box where T.BoxType == Double >(_ box: T) {*

* self.init(box.value)*

* }*

* init<T: Box where T.BoxType == Int >(_ box: T) {*

* self.init(box.value)*

* }*

*}*

*//Adding FloatLiteralConvertible, IntegerLiteralConvertible*

*extension FloatingPointBox where Self.BoxType == Double,
Self.FloatLiteralConvertible == Double {*

* init(floatLiteral value: Double) {*

* self.init(value)*

* }*

* init(integerLiteral value: Int) {*

* self.init(value)*

* }*

* init<T: IntegerType>(_ value: T) {*

* self.init(value)*

* }*

*}*

Here is my example of using the Box protocol:

*struct Degree: FloatingPointBox {*

* var value: Double = 0*

* init()*

* {*

* }*

*}*

*protocol DegreeConvertiable {*

* init(degreeLiteral value: Degree)*

*}*

*extension Degree: RadianConvertiable {*

* init(radianLiteral value: Radian) {*

* self.value = Double(value) * 180.0 / M_PI*

* }*

* init(_ value: Radian) {*

* self.init(radianLiteral: value)*

* }*

*}*

On Tue, Jan 5, 2016 at 5:24 PM, Matthew Johnson via swift-evolution < >> swift-evolution@swift.org> wrote:

> On Jan 5, 2016, at 11:16 AM, Thorsten Seitz via swift-evolution < >>> swift-evolution@swift.org> wrote:
>
>
>> Am 05.01.2016 um 17:11 schrieb Grzegorz Adam Hankiewicz via >>> swift-evolution <swift-evolution@swift.org>:
>>
>> The ideal would be for the compiler to pretend Euros or RefTablePk
are different types, yet use their parent type at the binary level. This
needs a specific syntax to teach the compiler which existing
methods/operations are allowed on the new fake types and which aren’t.
These new distinct types would *borrow* previous implementations.
>
> What about citing the relevant protocols in the newtype definition?
This should include the ability to use my own protocols to which I have
made the underlying type conform to by an extension.

This is how my forwarding proposal works. The newtype syntax I
suggested as a possible extension looks like this:

newtype Euro = Double forwarding Addable, Subtractable

The keyword could be different, but I think `forwarding` is not bad.
When I complete the second draft I think it will make even more sense. The
forwarding facility has features to handle non-trivial cases (Self and
associated type requirements, etc).

>
> Throwing some syntax into the discussion:
>
> newtype Euro = Double : Addable, Subtractable
>
> where I have defined the protocols Addable and Subtractable somewhere
and made Double conform to them if all this is not provided by the standard
library.
> The implementation of Euro then borrows the implementation of Double
for these protocols.
>
> -Thorsten
> _______________________________________________
> 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

--
 Wizard
james@supmenow.com
+44 7523 279 698

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

--
 Wizard
james@supmenow.com
+44 7523 279 698

--
 Wizard
james@supmenow.com
+44 7523 279 698


(James Campbell) #11

Interesting could you upload :slight_smile: would be good to consolidate all the
approaches we have right now so we can come up with the best one:

So far we have:

- Box/Unit Type Protocol (My Solution)
- Math Group Protocol (Tino Heth)
- Computed typealias (John Randolph)

···

On Thu, Jan 7, 2016 at 2:55 PM, John Randolph via swift-evolution < swift-evolution@swift.org> wrote:

On Jan 7, 2016, at 4:06 AM, James Campbell via swift-evolution < > swift-evolution@swift.org> wrote:

So this is how this feature could be achieved right now with minimal
changes to the language.

- We expose some kind of protocol that allows you to Box up types, we
could call this `Box` or something else like `Unit`. (I have a working
implementation in the current language with a bit of boilerplate). This
protocol handles the default implementation of converting from literals and
to floats etc.
- For each type-safe unit for a calculation - you define a protocol
extending this `Box` type which defines the associated type of the value
that unit holds. For example for Degree and Radian I declared a `AngleType`
which set the associated type to be a double.
- For each unit type, you declare a struct that inherits from that
protocol you defined. So I have two structs `Degree` and `Radian` which
implement the `AngleType` protocol.
- You implement the functions for figuring out if your units are equal and
all other operators they may need i.e `Degree(360) - 30`.

I took a different approach to this, which was to just typealias Angle to
Double, keep all Angle values as radians (since the stdlib trig functions
take radians), and add some computed setters and getters to let me access
an Angle in radians, degrees, or grads.

I would like to see Angle, along with several other basic geometric
concepts, promoted to the Swift standard library.

-jcr

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

--
 Wizard
james@supmenow.com
+44 7523 279 698


(David Sweeris) #12

The list of SI Units (https://en.wikipedia.org/wiki/International_System_of_Units#Units_and_prefixes) is probably a pretty good target. Might be a bit much to bite off all at once, though.

···

On Jan 7, 2016, at 09:25, James Campbell via swift-evolution <swift-evolution@swift.org> wrote:

I understand, I want to keep prototyping this with other units to see what insight I can achieve.

Do we have a list of units we would like to model to serve as a suite of tests and also to test any implementation we may need ?


(Matthew Johnson) #13

Interesting could you upload :slight_smile: would be good to consolidate all the approaches we have right now so we can come up with the best one:

So far we have:

- Box/Unit Type Protocol (My Solution)
- Math Group Protocol (Tino Heth)
- Computed typealias (John Randolph)

Is the goal of looking at approaches that are possible in the existing language to identify how the language could be improved to better facilitate type safe calculations? That seems relevant for this list. But if you get too far into collaborating on solutions you want to use today, that might be a conversation for the swift-users list.

···

On Jan 7, 2016, at 9:01 AM, James Campbell via swift-evolution <swift-evolution@swift.org> wrote:

On Thu, Jan 7, 2016 at 2:55 PM, John Randolph via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 7, 2016, at 4:06 AM, James Campbell via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

So this is how this feature could be achieved right now with minimal changes to the language.

- We expose some kind of protocol that allows you to Box up types, we could call this `Box` or something else like `Unit`. (I have a working implementation in the current language with a bit of boilerplate). This protocol handles the default implementation of converting from literals and to floats etc.
- For each type-safe unit for a calculation - you define a protocol extending this `Box` type which defines the associated type of the value that unit holds. For example for Degree and Radian I declared a `AngleType` which set the associated type to be a double.
- For each unit type, you declare a struct that inherits from that protocol you defined. So I have two structs `Degree` and `Radian` which implement the `AngleType` protocol.
- You implement the functions for figuring out if your units are equal and all other operators they may need i.e `Degree(360) - 30`.

I took a different approach to this, which was to just typealias Angle to Double, keep all Angle values as radians (since the stdlib trig functions take radians), and add some computed setters and getters to let me access an Angle in radians, degrees, or grads.

I would like to see Angle, along with several other basic geometric concepts, promoted to the Swift standard library.

-jcr

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

--
 Wizard
james@supmenow.com <mailto:james@supmenow.com>
+44 7523 279 698
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(James Campbell) #14

If it helps I would be happy to start a library for this, which could be
later added to the standard library :slight_smile:

···

On Thu, Jan 7, 2016 at 3:01 PM, James Campbell <james@supmenow.com> wrote:

Interesting could you upload :slight_smile: would be good to consolidate all the
approaches we have right now so we can come up with the best one:

So far we have:

- Box/Unit Type Protocol (My Solution)
- Math Group Protocol (Tino Heth)
- Computed typealias (John Randolph)

On Thu, Jan 7, 2016 at 2:55 PM, John Randolph via swift-evolution < > swift-evolution@swift.org> wrote:

On Jan 7, 2016, at 4:06 AM, James Campbell via swift-evolution < >> swift-evolution@swift.org> wrote:

So this is how this feature could be achieved right now with minimal
changes to the language.

- We expose some kind of protocol that allows you to Box up types, we
could call this `Box` or something else like `Unit`. (I have a working
implementation in the current language with a bit of boilerplate). This
protocol handles the default implementation of converting from literals and
to floats etc.
- For each type-safe unit for a calculation - you define a protocol
extending this `Box` type which defines the associated type of the value
that unit holds. For example for Degree and Radian I declared a `AngleType`
which set the associated type to be a double.
- For each unit type, you declare a struct that inherits from that
protocol you defined. So I have two structs `Degree` and `Radian` which
implement the `AngleType` protocol.
- You implement the functions for figuring out if your units are equal
and all other operators they may need i.e `Degree(360) - 30`.

I took a different approach to this, which was to just typealias Angle to
Double, keep all Angle values as radians (since the stdlib trig functions
take radians), and add some computed setters and getters to let me access
an Angle in radians, degrees, or grads.

I would like to see Angle, along with several other basic geometric
concepts, promoted to the Swift standard library.

-jcr

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

--
 Wizard
james@supmenow.com
+44 7523 279 698

--
 Wizard
james@supmenow.com
+44 7523 279 698


(James Campbell) #15

Yes that is the main purpose. At least 2 of the solutions define protocols
which at the very least may be useful to have in the standard library.

···

On Thu, Jan 7, 2016 at 3:29 PM, Matthew Johnson <matthew@anandabits.com> wrote:

On Jan 7, 2016, at 9:01 AM, James Campbell via swift-evolution < > swift-evolution@swift.org> wrote:

Interesting could you upload :slight_smile: would be good to consolidate all the
approaches we have right now so we can come up with the best one:

So far we have:

- Box/Unit Type Protocol (My Solution)
- Math Group Protocol (Tino Heth)
- Computed typealias (John Randolph)

Is the goal of looking at approaches that are possible in the existing
language to identify how the language could be improved to better
facilitate type safe calculations? That seems relevant for this list. But
if you get too far into collaborating on solutions you want to use today,
that might be a conversation for the swift-users list.

On Thu, Jan 7, 2016 at 2:55 PM, John Randolph via swift-evolution < > swift-evolution@swift.org> wrote:

On Jan 7, 2016, at 4:06 AM, James Campbell via swift-evolution < >> swift-evolution@swift.org> wrote:

So this is how this feature could be achieved right now with minimal
changes to the language.

- We expose some kind of protocol that allows you to Box up types, we
could call this `Box` or something else like `Unit`. (I have a working
implementation in the current language with a bit of boilerplate). This
protocol handles the default implementation of converting from literals and
to floats etc.
- For each type-safe unit for a calculation - you define a protocol
extending this `Box` type which defines the associated type of the value
that unit holds. For example for Degree and Radian I declared a `AngleType`
which set the associated type to be a double.
- For each unit type, you declare a struct that inherits from that
protocol you defined. So I have two structs `Degree` and `Radian` which
implement the `AngleType` protocol.
- You implement the functions for figuring out if your units are equal
and all other operators they may need i.e `Degree(360) - 30`.

I took a different approach to this, which was to just typealias Angle to
Double, keep all Angle values as radians (since the stdlib trig functions
take radians), and add some computed setters and getters to let me access
an Angle in radians, degrees, or grads.

I would like to see Angle, along with several other basic geometric
concepts, promoted to the Swift standard library.

-jcr

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

--
 Wizard
james@supmenow.com
+44 7523 279 698
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
 Wizard
james@supmenow.com
+44 7523 279 698


(John Randolph) #16

I would, but I kind of have it in pieces all over the floor at the moment, because I was experimenting with an alternate implementation using an enum.

-jcr

···

On Jan 7, 2016, at 7:02 AM, James Campbell <james@supmenow.com> wrote:

If it helps I would be happy to start a library for this, which could be later added to the standard library :slight_smile:

On Thu, Jan 7, 2016 at 3:01 PM, James Campbell <james@supmenow.com <mailto:james@supmenow.com>> wrote:
Interesting could you upload :slight_smile: would be good to consolidate all the approaches we have right now so we can come up with the best one:

So far we have:

- Box/Unit Type Protocol (My Solution)
- Math Group Protocol (Tino Heth)
- Computed typealias (John Randolph)

On Thu, Jan 7, 2016 at 2:55 PM, John Randolph via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 7, 2016, at 4:06 AM, James Campbell via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

So this is how this feature could be achieved right now with minimal changes to the language.

- We expose some kind of protocol that allows you to Box up types, we could call this `Box` or something else like `Unit`. (I have a working implementation in the current language with a bit of boilerplate). This protocol handles the default implementation of converting from literals and to floats etc.
- For each type-safe unit for a calculation - you define a protocol extending this `Box` type which defines the associated type of the value that unit holds. For example for Degree and Radian I declared a `AngleType` which set the associated type to be a double.
- For each unit type, you declare a struct that inherits from that protocol you defined. So I have two structs `Degree` and `Radian` which implement the `AngleType` protocol.
- You implement the functions for figuring out if your units are equal and all other operators they may need i.e `Degree(360) - 30`.

I took a different approach to this, which was to just typealias Angle to Double, keep all Angle values as radians (since the stdlib trig functions take radians), and add some computed setters and getters to let me access an Angle in radians, degrees, or grads.

I would like to see Angle, along with several other basic geometric concepts, promoted to the Swift standard library.

-jcr

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

--
 Wizard
james@supmenow.com <mailto:james@supmenow.com>
+44 7523 279 698 <tel:%2B44%207523%20279%20698>

--
 Wizard
james@supmenow.com <mailto:james@supmenow.com>
+44 7523 279 698


(Mark Glossop) #17

[snip]

Hi all,

I too have been mulling these ideas over for some time (well before this thread or list started!), but busy yak shaving stuff right now :-/ (hence the list-lurking behaviour...)

In the meantime, I'd suggest looking at the way Mathematica approaches this issue - it's supported units, quantities, multiple standard systems and conversions, and a lot more - and has done so for 20+ years now.

When I free up some time, I plan to see what I can do with Swift lib on this front, but I'd suggest taking a look at Wolfram's "prior work" if this area is of interest to you: https://reference.wolfram.com/language/ref/Quantity.html

There's also this by Martin Fowler on "quantity" types: http://martinfowler.com/eaaDev/quantity.html

Sidebar on decimal numerics since they're relevant to support for currency units - would be nice to know if the "new" IEEE 754 (2008) Decimal floating-point types are in the Swift roadmap?
My suspicion is that any support for them will only come from upstream in LLVM as a first-class type [quad precision/binary128 is supported as the fp128 type; but no decimal floating-point types at present] - so looks like any fixed-point decimal support in the interim would need to be provided by hacking int types, but if anyone wants to chime in... :slight_smile:

Cheers,
M.

···

On 7 Jan 2016, at 23:51, James Campbell via swift-evolution <swift-evolution@swift.org> wrote:

Yes that is the main purpose. At least 2 of the solutions define protocols which at the very least may be useful to have in the standard library.

On Thu, Jan 7, 2016 at 3:29 PM, Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> wrote:

On Jan 7, 2016, at 9:01 AM, James Campbell via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Interesting could you upload :slight_smile: would be good to consolidate all the approaches we have right now so we can come up with the best one:

So far we have:

- Box/Unit Type Protocol (My Solution)
- Math Group Protocol (Tino Heth)
- Computed typealias (John Randolph)

Is the goal of looking at approaches that are possible in the existing language to identify how the language could be improved to better facilitate type safe calculations? That seems relevant for this list. But if you get too far into collaborating on solutions you want to use today, that might be a conversation for the swift-users list.

--
Mark Glossop
E: lists <mailto:lists@cueballcentral.com>@cueballcentral.com <mailto:tech@cueballcentral.com>
TW: http://twitter.com/Cueball_AU


(Stephen Canon) #18

Decimal FP is somewhere in the hazy far-future hypothetical roadmap. It’s actually far more useful than (software) Binary128; implemented in software the performance delta between the two is fairly narrow on modern CPU architectures (except possibly for division), and having a decimal FP type available would be quite nice.

There’s a lot of lower-hanging fruit to chip away on first, however.
– Steve

···

On Jan 7, 2016, at 12:40 PM, Mark Glossop via swift-evolution <swift-evolution@swift.org> wrote:

Sidebar on decimal numerics since they're relevant to support for currency units - would be nice to know if the "new" IEEE 754 (2008) Decimal floating-point types are in the Swift roadmap?
My suspicion is that any support for them will only come from upstream in LLVM as a first-class type [quad precision/binary128 is supported as the fp128 type; but no decimal floating-point types at present] - so looks like any fixed-point decimal support in the interim would need to be provided by hacking int types, but if anyone wants to chime in... :slight_smile: