Implementing some Ruby builtins in Swift

Type 1. into irb and hit tab, and it’ll ask you if you want to see all 105 possibilities. Try it with [ ] and you’ll be offered 155. Ruby comes with a lot of built-in methods on its default types.

It’s debatable how useful all of these are. The idiom in Swift seems much more to be like in the C++ STL, where there are algorithms that operate on collections, rather than collections having methods that operate on themselves. To see more about this, read this article on writing algorithms against collections.

But for the sake of looking at a few Swift language features, let’s implement some of the Ruby popular ones. Maybe they’d be useful making a Ruby refugee feel at home.

First off, the Int loops. Lots of “hey look, ruby is cool” intros kick off with this one:

10.times do |i|
   "ruby is cool!"
end

This is pretty easy to add to the Swift Int using an extension:

extension Int {
    func times(f: () -> () ) {
        for _ in 0..self {
            f()
        }
    }
}

10.times { "swift is cool too!" }

Note the _ instead of a variable in the for, because the value isn’t actually needed.

Next, two for alternatives, upto and downto:

1.upto(3) {|i| print "#{i}.. "} # prints 1.. 2.. 3..
3.downto(1) {|i| print "#{i}.. "} # prints 3.. 2.. 1..

in Swift would be:

extension Int {
    func upto(n: Int, f: (Int) -> () ) {
        for i in self...n {
            f(i)
        }
    }
    func downto(n: Int, f: (Int) -> () ) {
        for i in reverse(n...self) {
            f(i)
        }
    }
}

Note the use of the inclusive ... this time, unlike with times which used the convention of a zero base up to but not including n so needed non-inclusive .., because this time we want to start at self and count inclusively to n. Humorously, Ruby also has inclusive and exclusive ranges but .. and ... are the opposite way around.

For downto, the range is wrapped in a reverse in order to count backwards.

Next, some extensions to array, in a similar vein to map or reduce.

Ruby’s each is like map, but doesn’t generate any return value:

extension Array {
    func each(f: (Element) -> ()) {
        for e in self {
            f(e)
        }
    }
}

Element is a Type Property representing the type of value the Array contains, to this function works generically for type contained by array.

Ruby’s reverse_each does the same but in the opposite direction:

extension Array {
    func each(f: (Element) -> ()) {
        for e in Swift.reverse(self) {
            f(e)
        }
    }
}

Again, we use reverse to go backwards through the array. Wait, you might think, isn’t it inefficient to reverse the array just to iterate over it? But that’s not what reverse does – instead it generates a lazy collection that serves up the values in reverse order.

Note, we have to prefix it Swift.reverse to disambiguate it from the Array member function reverse – which would result in creating a whole new array in reversed order, if we used it.

Ruby’s delete_if is the opposite of Swift’s filter – it takes a discriminator function and returns any elements that don’t match. We could easily just implement it from scratch with a for loop, but instead let’s try and implement it by re-using filter.

One way to do this is to first create what’s called a “decorator”. Decorators are functions that take other functions, and return a new function that has changed the behaviour of the original function in some useful way.

What we want is a decorator that takes a function that returns a boolean, and then returns the opposite of that. Here it is:

// function that takes a function, f, that takes an element
// and returns a bool, and returns a function that applies f
// then returns the opposite
func not_f<T>(f: (T)->Bool) -> (T)->Bool {
    // this returns a closure that captures f
    return { !f($0) }
}

If you’re a bit hazy on closures and variable capture, read this.

Armed with not_f, we can define delete_if:

extension Array {
    func delete_if(f: (Element) -> Bool) -> Element[] {
        return self.filter(not_f(f))
    }
}

Finally, Ruby’s equivalent of filter is called select. It’s identical, it’s just called by a different name. Is there some way we could alias filter if we wanted?

Yup, like this:

extension Array {
    var select: ((Element) -> Bool) -> Element[] { 
        return Array.filter(self) 
    }
}

You can now use select in exactly the same way you use filter. This is quite a silly thing to do, but it does demonstrate a couple of language features: read-only computed properties that return closures, and references to member functions.

Read-only computed properties are used like regular properties i.e. can be accessed using the dot syntax, but behave like functions that take no arguments and return a value. In this case we have defined a property select that returns a function when you call it. We’re just returning an existing member function (more on that next) but you could have more complex logic that created and returned different kinds of closures.

The reason it’s nice to implement these as computed properties is you don’t need to use a () after the property name. This means if you want to compute a function for someone to use, they can use it like this: x.computedfun(). If computedfun was itself a function, you’d have to write this: x.computedfun()(), which looks odd. Other than that, they behave just like functions that return functions.

So what is select actually returning? To understand that, you need need to realize you can reference a classes’ member functions. See the following code:

class MyClass {
    let n: Int

    init(_ i: Int) { n = i }

    func multiply(i: Int) { 
        println("n: \(n) * i: \(i) = \(n*i)")
    }

    func add(i: Int) { 
        println("n: \(n) + i: \(i) = \(n*i)")
    }
}

let a = MyClass(2)
let b = MyClass(5)

// prints "n: 2 * i: 10 = 20"
a.multiply(10)

// prints "n: 5 + i: 10 = 50"
b.add(10)

// this takes a reference to the member 
// function multiply of MyClass
var memfunref = MyClass.multiply

// you can't use memfunref directly.
// you have to apply an instance of
// MyClass to it:
var instancefunref = memfunref(a)

// now you can call it, and it's
// like calling a.multiply, so
// this prints "n: 2 * i: 10 = 20" 
instancefunref(10)

// MyClass.add has the same type as
// MyClass.multiply, they're both (Int)->(),
// so you can assign it to memfunref too:
memfunref = MyClass.add

// and apply a different instance of MyClass
instancefunref = memfunref(b)

// this prints "n: 5 + i: 10 = 50"
instancefunref(10)

// or you can skip the instancefunref step
// prints "n: 5 + i: 10 = 50" again
memfunref(b)(10)

Now we can see what that computed property is doing. It’s returning the member function filter of Array, then applying self to it, and returning that. The caller then can call the returned function () and it runs filter against the object they fetched it from. Like I said, bit silly, but it demonstrates a feature that could probably be put to better use in other cases.

If you liked this post, this one is also about implementing Ruby features in Swift, in this case implementing the ||= operator using the @auto_closure Swift feature.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s