Swift: More Fun with Reflection

Today, I was kicking around in a playground because (1) writing about playgrounds is kind of a thing right now and (2) because my kids were away and I had some time. I started trying to mess with tuples, and more specifically to zip them because tuples and structs tend to have related data in similar positions.

In any case, I ended up creating a small set of what I call  EVIL UNIVERSE SPOCK UTILITIES using mirroring and goatees. I thought I’d throw these out there to see whether they’re generally useful or interesting:

// MARK: Mirror-based Utilities
// These work across arrays, structures, tuples
// Goatees are mandatory

// This function maps a closure across anything
func mirrorDo<S>(structure:S, closure:(Int, String, Any)->()) {
    let mirror = reflect(structure)
    for index in 0 ..< mirror.count {
        closure(index, mirror[index].0, mirror[index].1.value)
    }
}

// This function collects the results of the mapped closure
func mirrorMap<S>(structure:S, closure:(Int, String, Any)->Any)->[Any]{
    let mirror = reflect(structure)
    var results = [Any]()
    for index in 0 ..< mirror.count {
        let result = closure(index, mirror[index].0, mirror[index].1.value)
        results += [result]
    }
    return results
}

// This converts nearly anything into an array
func mirrorToArray<S>(structure:S) -> [Any] {
    return mirrorMap(structure){return $2}
}

// This function zips things into array pairs
func mirrorZipToArray<S, T>(s:S, t:T)->[[Any]] {
    var array = [[Any]]()
    for each in zip(mirrorToArray(s), mirrorToArray(t)) {
        array += [mirrorToArray(each)]
    }
    return array
}

For example, you might look at all the elements or fields in a tuple or structure:

for test : Any in [tuple1, rect, point, transform] {
    println("\(test.dynamicType): ")
    mirrorDo(test){ (index: Int, field : String, value : Any) -> () in
        println("type:\(value.dynamicType), index: \(index), field: \(field), value: \(value)")
    }
}

Or you might convert items to an array:

for test : Any in [tuple1, rect, array2, point, transform] {
    println("Converting \(test) to array")
    println(mirrorToArray(test))
}

Or you might just zip stuff together into arrays:

mirrorZipToArray(array3, array2)
mirrorZipToArray(tuple1, tuple2)

You’ll find the code and a bunch of examples that showcase their use in this gist.

Two other things that simply pleased my aesthetics today. First: I really like that you can apply maps to zips.

var results = map(zip(["a", "b", "c"], ["d", "b", "e"])){$0==$1}

And also you can pass tuples to functions and closures as parameter sets:

func plus(a: Int, b: Int) -> Int {return a + b}
let xx = (2, 3); let yy = (5, 9)
plus(xx)
plus(yy)

Best of all, you can combine these approaches to zip and then use the tuples in a function call like this:

map(zip([1, 2, 3, 4], [5, 6, 7, 8])){plus($0)}

Isn’t that cool for a boring Sunday?

Comments are closed.