Tchelyzt Tchelyzt - 7 months ago 9
Swift Question

Symmetry in using structures for conversion. What is best practice?

I've been playing with Swift and I encoded an obvious conversion structure:

struct MutableAngle {
var degrees : CGFloat
var radians : CGFloat {
return degrees * CGFloat(M_PI) / 180.0
}

init(inRadians : CGFloat) {
degrees = inRadians * 180.0 / CGFloat(M_PI)
}
init(inDegrees : CGFloat) {
degrees = inDegrees
}
}


Now this is fine but inelegant since it doesn't treat degrees and radians symmetrically although it does give mutability. This is really a structure which should be called Degrees and which can provide radians. For instance, I can write:

var angle : MutableAngle
angle.degrees = 45.0


but not

var angle : MutableAngle
angle.radians = 0.75


Here's another version of the structure:

struct ImmutableAngle {
let degrees : CGFloat
let radians : CGFloat

init(inRadians : CGFloat) {
radians = inRadians
degrees = inRadians * 180.0 / CGFloat(M_PI)
}
init(inDegrees : CGFloat) {
degrees = inDegrees
radians = inDegrees * CGFloat(M_PI) / 180.0
}
}


Switching from var to let makes it immutable and I can enforce the conversion in the constructors which I'm now obliged to use. I must write:

var angle = ImmutableAngle(inDegrees: 45.0)


or

var angle : ImmutableAngle(inRadians: 0.75)


So its symmetric. It also has the merit that a calculation is not required every time I want to use the radians.

Here's a final version:

struct Angle {
let degrees : Float
let radians : Float

init(inRadians : Float ) {
radians = inRadians
degrees = radians * Float (180 / M_PI)
}
init(inDegrees : Float ) {
degrees = inDegrees
radians = degrees * Float (M_PI / 180)
}
}


Use as follows:

var alpha = Angle(inDegrees: 45)
alpha.degrees // returns 45
alpha.radians // returns 0.7853982

// alpha.radians = 0.9 ... is now illegal with let constants
// must use constructor ... provided alpha was defined using 'var'
// i.e. the struct itself is mutable
alpha = Angle(inRadians: 0.9)
alpha.radians // returns 0.7853982
alpha.degrees // returns 45

Answer

Two things here:

  1. In Swift you don't need a separate mutable type for value types - that's handled by whoever is instantiating the type by using let or var.

  2. Your radians computed property only has a getter - you can do what you want with both a setter and a getter.

My implementation:

struct Angle {
    var degrees : CGFloat = 0
    var radians : CGFloat {
        get {
            return degrees * CGFloat(M_PI) / 180.0
        }
        set {
            degrees = newValue * 180.0 / CGFloat(M_PI)
        }
    }

    init(inRadians : CGFloat) {
        radians = inRadians
    }
    init(inDegrees : CGFloat) {
        degrees = inDegrees
    }
}

Useage:

// immutable
let angle = Angle(inDegrees: 180)
println(angle.radians)
// next line gives an error: can't assign to an immutable instance
angle.radians = angle.radians * 2

// mutable copy
var mutableAngle = angle
mutableAngle.degrees = 10
println(mutableAngle.radians)
// 0.1745...
mutableAngle.radians = CGFloat(M_PI)
println(mutableAngle.degrees)
// 180.0
Comments