circumference should be around 6.3, but this is too large. - Oh, the angle should be in radians, not degrees. let arc = arcLength(radius: 1, angle: 45) // -> 45
The length of arc produced by the `radius` sweeping `angle` /// where `angle` is in radians. func arcLength(radius: Double, angle: Double) -> Double { return radius * angle } let arc = arcLength(radius: 1, angle: .pi / 4) // -> 0.785
to radians to be clearer. But it is still possible to pass in the wrong unit. /// The length of arc produced by the `radius` sweeping `radians`. func arcLength(radius: Double, radians: Double) -> Double { return radius * radians }
lie, it doesnʼt create a type, just a pseudonym for an existing type. You canʼt enforce a convention, there is nothing from stopping the consumer of your API from doing something wrong, even for something as simple as this. typealias Radians = Double /// The length of arc produced by the `radius` sweeping `angle`. func arcLength(radius: Double, angle: Radians) -> Double { return radius * angle }
will be passed and lead to a bug that will time to track down and fix. Donʼt even mention the agile process of raising a ticket, grooming, estimation, code review... let degrees = Double(45) let arc = arcLength(radius: 1, angle: degrees) // -> 45
just a number can mitigate against this. It restricts what can be provided in the parameter, and so the cases that have to be handled in a function, or method are also reduced. STRONG TYPING TO THE RESCUE
on the slide. The radius has a length dimension, the angle has an “angle” dimension. Within the function we make sure that we are using the radian representation of whatever angle weʼve provided, and it returns a length. A proper, typed length, not just a number. /// The length arc produced by `radius` sweeping `angle` func arcLength( radius: Measurement<UnitLength>, angle: Measurement<UnitAngle> ) -> Measurement<UnitLength> { let radians = angle.converted(to: .radians).value return radius * radians }
and still get the correct, typed, response. Of course, this isnʼt foolproof, you could still create the measurement with the UnitAngle.degrees, but it wonʼt let you make the mistake of reversing the parameters. The new Measurements API is really useful; invite me back sometime and Iʼll talk about it some more. let angle = Measurement(value: 45, unit: UnitAngle.degrees) let radius = Measurement(value: 1, unit: UnitLength.meters) let length = arcLength(radius: radius, angle: angle) // -> 0.785397882567309 m
do this right - SELF DOCUMENTING METHODS. /// The length arc produced by `radius` sweeping `angle` func arcLength( radius: Measurement<UnitLength>, angle: Measurement<UnitAngle> ) -> Measurement<UnitLength> { ... }
I donʼt have to use ugly indentation. I didnʼt say they were bad, just that they need to be used with care. I already have a well formed type, Iʼm just using a more convenient alias to refer to it. But look at the documentation - the summary is enough to tell you what the function does. The types provide information about what types come in and what types come out. SELF DOCUMENTING METHODS typealias Length = Measurement<UnitLength> typealias Angle = Measurement<UnitAngle> /// The length arc produced by `radius` sweeping `angle` func arcLength(radius: Length, angle: Angle ) -> Length { ... }
create a length and an angle that produces a known length. You donʼt need to worry about the whether itʼs in degrees, or radians. Inches or centimetres, because they are just interchangeable types. TESTING IS EASIER typealias Length = Measurement<UnitLength> typealias Angle = Measurement<UnitAngle> /// The length arc produced by `radius` sweeping `angle` func arcLength(radius: Length, angle: Angle ) -> Length { ... }
some of the benefits of type safety with a type that wraps a simple Double into a type. This is one way to do it on early Ones. WRAPPER TYPES struct Radian { let value: Double } struct Degree { let value: Double }
nothing. The advantage of this is that the base type is shared and you have the benefits of writing extensions that apply to all types of generic type PHANTOM TYPES struct Radian {} struct Degree {} struct Angle<T> { let value: Double } let radian = Angle<Radian>(value: .pi)
array of dictionaries. Map them to a proper Model object, and use that. I wonʼt make you look at my face again, but Iʼve actually seen this. USE PROPER TYPES.
easier to read, easier to debug and less prone to 3am errors. I wanted to remind you that the type system makes for safer programming. That's not all it does - It also lets you use different programming paradigms such as Protocol Oriented Programming, extensions, generics. Invite me back sometime and I'll talk to you about them. THE TYPE SYSTEM IS YOUR FRIEND.