Playing with Swift

I really like what we’ve seen so far of Swift, Apple’s new programming language. For starters, I was happy to learn that references cannot be nil[1], and integer arithmetic is checked by default[2].

Let’s look at how Swift deals with sequences, and learn a little bit about generics on the way. Arrays, for example, are sequences, so you can iterate over them with a for loop:

for x in [1,2,3] {
    println(x)
}

Lots of other types are sequences, too, like strings, dictionaries and ranges. What makes them sequences is that they all implement the Sequence protocol:

protocol Sequence {
    typealias GeneratorType : Generator
    func generate() -> GeneratorType
}

I’m not sure why Swift elects to do generic protocols via typealias instead of just having the protocol take type arguments, like C# does, which might look something like this:

protocol Sequence<T> {
    func generate() -> Generator<T>
}

Whatever the reason might be, we can see that a sequence can generate a Generator, which is defined as follows:

protocol Generator {
    typealias Element
    func next() -> Element?
}

Generators have a next method which returns either the next element in the sequence, or nil, when the end has been reached. The for loop from above is probably mostly equivalent to

var gen = [1,2,3].generate()
while let x = gen.next() {
    println(x)
}

The equivalent of Sequence and Generator in C# are IEnumerable and IEnumerator.

The map function takes a sequence and a function, returning a new sequence containing the results of applying the function to the elements of the given sequence. For example:

for x in map([1,2,3], {i in i*2}) {
    println(x)
}

produces

2
4
6

It’s important to realize that the sequence which map returns is not necessarily of the same type as the one it is passed:

println([1,2,3])
println(map([1,2,3], {i in i*2}))

prints

[1, 2, 3]
VSs17MapCollectionView (has 2 children)

Also, sequences can generate their elements lazily:

var seq = map([1,2,3], { (i: Int) -> Int in
    println("processing " + String(i))
    return i*2
    })
var gen = seq.generate()
println(gen.next())

prints

processing 1
2

Let’s write a function that takes a sequence and returns an array containing all the sequence’s elements!

First we need to figure out the type of this function. It turns out that this doesn’t work:

func toArray (seq: Sequence) -> seq.GeneratorType.Element[]

The compiler complains that seq is an undeclared type. This doesn’t work, either:

func toArray (seq: Sequence) -> Sequence.GeneratorType.Element[]

Here the complaint is that Sequence.GeneratorType is ambiguous. That’s because we can’t tell the compiler that the two Sequence types are the same. Maybe we can use generics to solve the problem:

func toArray<S:Sequence> (seq: S) -> S.GeneratorType.Element[] {
    var arr = S.GeneratorType.Element[]()
    ...
    return arr
}

Swift is now happy with the type of the function, but unfortunately it doesn’t let us instantiate the array. Instead it thinks we’re trying to subscribe S.GeneratorType.Element, which isn’t possible, because it’s a type and not a value. As far as I can tell, the only way to get around this problem is to introduce a second generic argument, which we can force to the correct type via a where clause:

func toArray<S : Sequence,
             T where T == S.GeneratorType.Element>
        (seq : S) -> T[] {
    var arr = T[]()
    for x in seq {
        arr.append(x)
    }
    return arr
}

println(toArray(map([1,2,3], {x in x*2})))

And there we have it:

[2, 4, 6]

  1. Swift would be the first mainstream programming language that corrects Tony Hoare’s “billion dollar mistake”, after barely 60 years of all others repeating it.  ↩

  2. It seems we’re finally learning that it’s better for a program to halt than to give away all your secrets and then launch all the nuclear missiles in the case of a bug.  ↩

Advertisements

3 thoughts on “Playing with Swift

  1. Thanks for the post. The following seems to allow you to get rid of the second generic argument:

    func toArray
    (seq : S) -> S.GeneratorType.Element[] {
    var arr: S.GeneratorType.Element[] = []
    for x in seq {
    arr.append(x)
    }
    return arr
    }

    • It doesn’t compile on the latest beta. That said the compiler complaints make perfect sense: S is an undeclared type and even if you add it as a generic type you’re still not telling the compiler whether it implements the Sequence protocol or not.

  2. On the latest beta at the time of writing this (Xcode 6.1 6A1030) you have to use SequenceType instead of Sequence, Generator instead of GeneratorType and array types are now written with brackets around the element type (which is good because there’s no ambiguity with subscription).
    The code is now much cleaner:


    func toArray1 (seq: S) -> [S.Generator.Element] {
    var arr:[S.Generator.Element] = []
    for x in seq {
    arr.append(x)
    }
    return arr
    }

Comments are closed.