To me Angle is a unit with two common representations: radians and degrees.

It's not an enum because it doesn't have two values, it has one value that

you can view in two ways.

Therefore I would make an Angle struct, something like:

//: Angle struct instead of angle enum

import Foundation

struct Angle {

static let d2R = Double.pi / 180

static let r2D = 180 / Double.pi

private var degs: Double

var degrees: Double {

return degs

}

var radians: Double {

return degs * Angle.d2R

}

init(degrees: Double = 0) {

degs = degrees.truncatingRemainder(dividingBy: 180)

}

init(radians: Double) {

self.init(degrees: radians * Angle.r2D)

}

}

extension Angle: Hashable {

var hashValue: Int {

return degs.hashValue

}

static func ==(lhs: Angle, rhs: Angle) -> Bool {

return lhs.degs == rhs.degs

}

}

extension Angle/*: FloatingPoint*/ {

static func +(lhs: Angle, rhs: Angle) -> Angle {

return Angle(degrees: lhs.degs + rhs.degs)

}

static func +=(lhs: inout Angle, rhs: Angle) {

lhs.degs += rhs.degs

lhs.degs = lhs.degs.truncatingRemainder(dividingBy: 180)

}

// Rest of FloatingPoint ...

}

// Trig

extension Angle {

var sin: Double {

return Foundation.sin(radians) // Need to qualify name to stop name

clash

}

// Other trig

}

let a = Angle(degrees: 90) // 90

a.radians // pi / 2

a.sin // 1

let b = Angle(radians: 3) // Almost pi

b.degrees // 171.9

let c = a + b // Wraps over 180 degrees

c.degrees // 81.9

c == b // false

c.hashValue // 463...

let d = Angle(degrees: -90) // -90

d.radians // -pi / 2

var e = Angle(radians: -3) // Almost -pi

e.degrees // -171.9

e += d // Wraps over -180 degrees

e.degrees // -81.9

-- Howard.

## ···

On 19 June 2017 at 13:32, David Sweeris via swift-users < swift-users@swift.org> wrote:

On Jun 18, 2017, at 19:30, Nevin Brackett-Rozinsky via swift-users < > swift-users@swift.org> wrote:

Is there a way to restrict the associated values of an enum? For example,

suppose I have this type:

enum Angle {

case radians(Double)

case degrees(Double)

}

I want to ensure that the radians values is always in [0, 2π) and the

degrees values is always in [0, 360). Ideally I would like to write an

initializer which is called when the user writes eg. “let x: Angle =

.degrees(-45)” and contains the logic to wrap the provided value into the

allowed range (in this case by adding a multiple of 360).

I don’t see a way to do it. Is this possible?

The closest I’ve found is to create auxiliary types such as

struct Degree { … }

struct Radian { … }

and give them appropriate initializers, then use them for the associated

values. However that is undesirable because it adds an extra level of depth

to get at the actual numeric values.

Is there a better way?

Not off the top of my head, at least not without changing the syntax.

You could add two inits, with different argument labels, and not directly

set the case: “let x = Angle(degrees: -45)”

You could add "public var radians: Double { get {...} set {...} }" (and

again for "degrees") to `Angle`. The getter would need to switch on self

to figure out if you need to convert between the two (like if someone said

".radians(2).degrees"): “var x = Angle.radians(0); x.degrees = -45"

Alternately, you could add "subscript() -> Double" and switch on self in

both the setter and the getter to figure out if you should be wrapping

at 2π or 360 and to do any conversions: “var x: Angle = .radians(0); x[] =

-45"

Hope that helps

- Dave Sweeris

_______________________________________________

swift-users mailing list

swift-users@swift.org

https://lists.swift.org/mailman/listinfo/swift-users