[swift-evolution] Epic: Typesafe calculations

Félix Cloutier felixcca at yahoo.ca
Fri Jan 8 15:48:57 CST 2016


I said earlier (maybe in a different thread?) that I didn't want to implement units in C++ even though I was pretty sure you could do it because I'd just pull my hair for hours. Well, there's no better way to learn than to pull your hair for hours, so I did it anyway.

http://coliru.stacked-crooked.com/a/b1dde8e8320b55b7 <http://coliru.stacked-crooked.com/a/b1dde8e8320b55b7>

The bottom part is the interesting part and shows how to use units with that system:

> #pragma mark - (actually using units)
> enum class Units
> {
> 	Meter,
> 	Second,
> 	Kilogram,
> };
> 
> template<Units U>
> using SimpleUnit = Quantity<double, UnitQuotient<UnitProduct<Units, U>, UnitProduct<Units>>>;
> 
> SimpleUnit<Units::Meter> _m(1);
> SimpleUnit<Units::Meter> _ft(0.3048);
> SimpleUnit<Units::Second> _s(1);
> SimpleUnit<Units::Kilogram> _Kg(1);
> auto _N = (_Kg * _m) / (_s * _s);
> 
> int main()
> {
> 	decltype(_m) distance = 12.5 * _m;
> 	decltype(_m * _m) area = 2 * _ft * distance;
> 	decltype(_Kg) mass = 18 * _Kg;
> 	decltype(_N) force = mass * distance / (1.2 * _s * 1.8 * _s);
> 	
> 	// uncomment for an error
> 	// decltype(_N) notForce = mass / distance;
> }

Of course, `auto` would work too, but it doesn't convey type/unit safety as well.

I'd like to be able to write that in Swift one day (with less incomprehensible template boilerplate) :-) I personally really like that you can "create" units just by declaring a variable, that you can get their type by using `decltype(unit)` and that they are very easy to compose.

Félix

> Le 8 janv. 2016 à 08:54:52, Thorsten Seitz <tseitz42 at icloud.com> a écrit :
> 
> 
>> Am 07.01.2016 um 20:26 schrieb Félix Cloutier <felixcca at yahoo.ca <mailto:felixcca at yahoo.ca>>:
>> 
>> Given that it relies on variadic templates and non-type template arguments, this wouldn't be feasible in Swift at the moment
> 
> Having non-type template arguments (and the ability to operate on them at compile time) would make it possible to drop all that stuff modeling this in the type system with peano numbers like I tried. Much more simple! 
> 
> This would reduce my example to the following (no need for variadic templates because I give each SI unit it fixed place in the parameter list — your idea of using variadic templates would probably allow to extend the system by new base units, though, and allow to cut down the long template parameter lists in the arithmetic functions which will grow longer with each base unit added in my example...):
> 
> // TODO: extend by more type parameters for the remaining SI units
> // missing language feature: non-type generic arguments, here: Int
> public struct Quantity<Length = Int, Time = Int, Mass = Int> : Equatable, Comparable {
>     
>     let value: Double
>     
>     init(_ value: Double) {
>         self.value = value
>     }
> }
> 
> public func ==<L,T,M>(lhs: Quantity<L,T,M>, rhs: Quantity<L,T,M>) -> Bool {
>     return lhs.value == rhs.value
> }
> 
> public func <<L,T,M>(lhs: Quantity<L,T,M>, rhs: Quantity<L,T,M>) -> Bool {
>     return lhs.value < rhs.value
> }
> 
> public func +<L,T,M>(lhs: Quantity<L,T,M>, rhs: Quantity<L,T,M>) -> Quantity<L,T,M> {
>     return Quantity<L,T,M>(lhs.value + rhs.value)
> }
> 
> public func *
>     <L1,T1,M1,L2,T2,M2,L,T,M where L == L1+L2, T == T1+T2, M == M1+M2>
>     (lhs: Quantity<L1,T1,M1>, rhs: Quantity<L2,T2,M2>) -> Quantity<L,T,M>
> {
>     return Quantity<L,T,M>(lhs.value * rhs.value)
> }
> 
> public func /
>     <L1,T1,M1,L2,T2,M2,L,T,M where L == L1-L2, T == T1-T2, M == M1-M2>
>     (lhs: Quantity<L1,T1,M1>, rhs: Quantity<L2,T2,M2>) -> Quantity<L,T,M>
> {
>     return Quantity<L,T,M>(lhs.value / rhs.value)
> }
> 
> 
> // Defining some nice typealiases with which to work
> 
> public typealias Scalar   = Quantity<0, 0, 0> // dimensionless
> public typealias Length   = Quantity<1, 0, 0> // meter
> public typealias Time     = Quantity<0, 1, 0> // seconds
> public typealias Mass     = Quantity<0, 0, 1> // kilogram
> public typealias Velocity = Quantity<1, -1, 0> // m/s
> public typealias Newton   = Quantity<1, -2, 1> // m*kg/s^2
> 
> 
> public extension Double {
>     
>     public var m: Length {
>         return Length(self)
>     }
>     public var s: Time {
>         return Time(self)
>     }
>     public var kg: Mass {
>         return Mass(self)
>     }
> }
> 
> let dist = 10.m + 5.m
> let time = 20.s
> let mass = 5.kg
> let velocity: Velocity = dist / time
> let force: Newton = mass * dist / (time * time)
> 
> 
> -Thorsten

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160108/04fbce99/attachment.html>


More information about the swift-evolution mailing list