> On 19. Jun 2017, at 20:03, Karl Wagner <razie...@gmail.com> wrote:
> 
> 
>> On 19. Jun 2017, at 04:30, Nevin Brackett-Rozinsky via swift-users 
>> <swift-users@swift.org <mailto: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 <mailto: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

Oh, and one thing to note is that by making static initialiser functions, you 
can still pass angles in to functions by calling ".degrees(90)”  or 
“.radians(.pi/4)”, so you kind-of emulate the convenience of using enums. IIRC, 
RawOptionSet does a similar trick.

The above struct is source-compatible with an equivalent enum representation; 
it all comes down to implementation details, and for this, the struct is more 
efficient.

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

Reply via email to