Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I bind different associated values in a Swift enum to the same var?

I made a little "Angle" enum so that I could program with different interchangeable angular formats:

enum Angle {
    case Radians(CGFloat)
    case Degrees(CGFloat)
    case Rotations(CGFloat)
}

I find that there's some redundant boilerplate code with this approach though. For example, I wanted to add a computed var that just returned the raw underlying associated float:

var raw:CGFloat {
    switch self {
    case let .Radians(value):
        return value
    case let .Degrees(value):
        return value
    case let .Rotations(value):
        return value
    }
}

I tried to change that to read:

case .Radians(let value), .Degrees(let value):
    return value

I hoped it would be clever enough to realize it was only ever going to resolve one match, so the conflict was ignorable. But no such device. Compiler says it can't resolve the conflict between the two let value statements.

So is there a bit of idiomatic cleverness I haven't discovered with Swift enum's yet that would make it so I don't have to repeat myself so much there?

Another similar example, was when I implemented the * function:

func * (lhs:Angle, rhs:CGFloat) -> Angle {
    switch lhs {
    case .Radians:
        return .Radians(lhs.raw * rhs)
    case .Degrees:
        return .Degrees(lhs.raw * rhs)
    case .Rotations:
        return .Rotations(lhs.raw * rhs)
    }
}

It seems like there should be a simpler way to express that: "Make the same enum type with my associated value times the scalar argument".

(I'm not sure I've got a good title on this question, feel free to improve/suggest better)

like image 772
Travis Griggs Avatar asked Mar 15 '23 15:03

Travis Griggs


1 Answers

That's an interesting case: An enum somehow isn't the right data type, because the value isn't either in radians or in degrees, both are just angles and not really different things. Also typealias Radians = Double wouldn't work because there's no unit safety.

Maybe you can use something like this though:

import Darwin

struct Angle {
    enum Unit {
        case Radians
        case Degrees
        case Rotations

        var radiansFactor : Double {
            switch self {
            case Radians: return 1
            case Degrees: return 180.0 / M_PI
            case Rotations: return 1 / 2 / M_PI
            }
        }
    }

    var unit : Unit {
        didSet {
            value /= oldValue.radiansFactor
            value *= unit.radiansFactor
        }
    }
    var value : Double
}

func * (var lhs: Angle, rhs: Double) -> Angle {
    lhs.value *= rhs
    return lhs
}

var angle = Angle(unit: .Degrees, value: 180)

angle.value             // 180.0
angle.unit = .Radians
angle.value             // 3.141592...
angle.unit = .Rotations
angle.value             // 0.5

Oh and as for the answer to your original question: No you cannot.

like image 143
Kametrixom Avatar answered Apr 25 '23 04:04

Kametrixom