[swift-evolution] Support for newtype feature/typesafe calculations

James Campbell james at supmenow.com
Thu Jan 7 11:25:16 CST 2016


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 at 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 :-) )
>
> 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 at 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 at 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 at 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 at 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 at swift.org> wrote:
>>
>>>
>>> > On Jan 5, 2016, at 11:16 AM, Thorsten Seitz via swift-evolution <
>>> swift-evolution at swift.org> wrote:
>>> >
>>> >
>>> >> Am 05.01.2016 um 17:11 schrieb Grzegorz Adam Hankiewicz via
>>> swift-evolution <swift-evolution at 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 at swift.org
>>> > https://lists.swift.org/mailman/listinfo/swift-evolution
>>>
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution at swift.org
>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>
>>
>>
>>
>> --
>>  Wizard
>> james at supmenow.com
>> +44 7523 279 698
>>
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>
>>
>>
>
>
> --
>  Wizard
> james at supmenow.com
> +44 7523 279 698
>
>
>


-- 
 Wizard
james at supmenow.com
+44 7523 279 698
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160107/b46a8e4c/attachment.html>


More information about the swift-evolution mailing list