> On 19. Jun 2017, at 04: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?
> 
> Nevin
> _______________________________________________
> swift-users mailing list
> swift-users@swift.org
> https://lists.swift.org/mailman/listinfo/swift-users 
> <https://lists.swift.org/mailman/listinfo/swift-users>

I suggested a type like this when Xiaodi announced his maths library, but a 
more efficient implementation would look like this:

public struct Angle<T: FloatingPoint> {
    public let radians: T
    public var degrees: T {
        return (radians / .pi) * 180
    }
    
    public static func radians(_ rads: T) -> Angle {
        return Angle(radians: rads)
    }
    public static func degrees(_ degs: T) -> Angle {
        return Angle(radians: (degs / 180) * .pi)
    }
}

Floating-points don’t have extra inhabitants, so the enum representation would 
occupy { float size + 1 byte } of storage, with the extra byte marking which 
enum case you have.

A better approach is to store a single, normalised value (in this case, the 
‘radians' value), and to provide initialisers which validate and normalise 
those input values. In your case, you wrap them to an allowed range. 

I think the best-practice advice in this situation would be to consider 
switching: will anybody need to switch over the cases of your enum? In this 
case, no - Angle<T> is just a wrapper which statically verifies that the angle 
is in the expected “notation”; You want to put an angle of either notation in, 
and grab the same angle out in another notation. The underlying stored notation 
is an implementation detail, so a struct is better.

- Karl
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

Reply via email to