What Learning Swift Looks Like: Typing

Continuing on from part one I started to get a lot deeper into immutability and static type system, especially when involving structs and classes.

I’d argue that a large part of why Swift comes across being modern and ‘script like’ to read is due to type inference. This system allows Swift programs to define variables without being explicit about the type.

let an_int = 10  
let a_double = 10.0  
let a_string = “This is a string”  

Even though I haven’t specified the type explicitly (let an_int: Int = 10) the compiler absolutely does. This can lead to some really confusing errors if you’re just starting with statically typed languages:

let velocity: CGFloat = 234  
let distance = 1223.00  
let duration = distance / velocity  
$ Binary operator ‘/‘cannot be applied to operands of type ‘Double’ and ‘CGFloat’

Type inference will declare distance as a Double which is not compatible with a CGFloat

let velocity: CGFloat = 234  
let duration = 1223.00 / velocity  
$ 5.226495726495727

In this example the compiler will ensure that duration is a CGFloat because it infers that you can only divide by velocity if 1223.00 is also a CGFloat. You can even do let duration = velocity / 1223 without the decimal and get the same results.

You could also typecast to make the first example work.

let velocity: CGFloat = 234.00  
let distance = 1223.00  
let duration = distance / Double(velocity)  
$ 5.226495726495727

This would make sense if you didn’t define the initial types yourself (such as a return from an existing function), but in this trivial example you should have just typed them correctly in the first place…

You can also create your own type aliases which simply allows you to give an existing type a new name

typealias MyType = Double  
let velocity: MyType = 234  
let distance: Double = 1223  
let duration = distance / velocity  

This is super useful in cases where you want complex types to be named for clean code. You’ll see this a lot with dictionary types:

typealias Response = [String: AnyObject]  
let response: Response = [ “id”: 1, “is_valid”: true ]  

This type alias makes code surrounding web responses much clearer than seeing [String: AnyObject] throughout your codebase.

Super useful tip: You can quickly debug the type of any variable by using toString(the_variable.dynamicType) which will print out the type as a string. e.g. toString(10.dynamicType) will be ”Swift.Int”.

So how does immutability effect struct and class structures?

struct YourFace {  
  var pretty = false
}
let x = YourFace()  
let y = YourFace()  
x = y  
$ Cannot assign to ‘let’ value ‘x’

Okay that’s fair..

x.pretty = true  
x  
$ { pretty true }

This shows that each variable defines it’s own mutable state. x is immutable but pretty is mutable, thus you can change it regardless of the mutability of the instance.

let x = YourFace()  
let y = YourFace()  
let a = [x, y]  
var b = a.reverse()  

In this case we’re using one of the many Array methods available which return a “copy” of an Array which is changed from the original.

I’m specifically looking for deep / shallow copy details.

b.pop()  
b  
$ [{pretty false}]
a  
$ [{pretty false}, {pretty false}]

Okay so they are copies, not references or slices so that’s useful. It turns out there was a lot of concern with this in early versions of Swift language in betas.

Next step is to check how the objects within the Array are copied over.

b[0].pretty = false  
a[1].pretty  
$ true

Deep copies, that’s good to know. So I’m assuming that functions are all pass by value, not pass by reference?

func makeItOppositeDay(faces: [YourFace]) -> [YourFace] {  
    return faces.map { face in
        YourFace(pretty: !face.pretty)
    }
}
let c = makeItOppositeDay(a)  
a  
$ [{pretty false}, {pretty false}]
c  
$ [{pretty true}, {pretty true}]

Yep, that makes sense. So passing a huge array to a function could be a pretty big hit I suppose.

I can say with confidence the hardest part of moving to Swift from dynamic languages has been the type system. It’s been a learning curve, but nothing to be scared of. I’m convinced that Dealing with these issues as you program from compiler failures is a lot better than dealing with them in a production system or that == v === mess…

Having said that I wish I never had to see another horrible compilation error like this again…

func makeItOppositeDay(faces: [YourFace]) {  
    return faces.map { face in
        YourFace(pretty: !face.pretty)
    }
}
$ ‘Array<YourFace>’ is not convertible to ‘()’

Wat. This is a horribly useless way to explain that the function makeItOppositeDay has a return of Void and you’re attempting to return an Array of YourFace structs…