[swift-users] Restricting associated values
Howard Lovatt
howard.lovatt at gmail.com
Mon Jun 19 00:33:00 CDT 2017
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 at swift.org> wrote:
>
> On Jun 18, 2017, at 19:30, Nevin Brackett-Rozinsky via swift-users <
> swift-users at 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 at swift.org
> https://lists.swift.org/mailman/listinfo/swift-users
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-users/attachments/20170619/d2ba98fa/attachment.html>
More information about the swift-users
mailing list