> 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