3

I'm playing around with Swift extensions, and bumped my head against a strange bug, while trying to extend Bool:

typealias Task = ()->()
extension Bool{
    func untilFalse(task: Task){

        while !self {println(self); task()}
    }
}

var i = 2

(i < 1).untilFalse{
    println(i)
    println("\(i) bottles of beer on the wall, \(i) bottles of beer.")
    i--
    println("Take one down and pass it around, \(i) bottles of beer on the wall.")
}

For some reason the loop just goes on and on, even after the boolean expression has become true.

Any ideas of what might be going on?

cfischer
  • 24,452
  • 37
  • 131
  • 214

2 Answers2

3

The issue is that the expression i < 1 will be evaluated once, resulting in false. It will not be continually re-evaluated. To achieve this you would have to replace it with a function or closure.

If you really want to, you could re-write your code as follows:

typealias Task = ()->()
typealias BooleanExpression = () -> Bool

infix operator *** {}

func *** (b: BooleanExpression, t: Task) {
  while !b() { t() }
}

var i = 2

let exp: BooleanExpression = { i < 1 }
exp *** {
  println(i)
  println("\(i) bottles of beer on the wall, \(i) bottles of beer.")
  i--
  println("Take one down and pass it around, \(i) bottles of beer on the wall.")
}

But that is pretty darn ugly!

ColinE
  • 68,894
  • 15
  • 164
  • 232
3

Along the same lines as Colin's expression, but building on it with auto closure, you could use:

typealias Task = ()->()

infix operator *** {}
func ***(expression:@autoclosure ()->Bool, task:Task) {
    while !expression() {
        task()
    }
}

var i = 2

(i < 1) *** {
    println(i)
    println("\(i) bottles of beer on the wall, \(i) bottles of beer.")
    i--
    println("Take one down and pass it around, \(i) bottles of beer on the wall.")
}

Which simplifies things a little. Of course the natural syntax would be to just use something like:

func untilFalse(expression:@autoclosure ()->Bool, block:()->()) {
    while(!expression()) {
        block()
    }
}

var i = 2
untilFalse(i < 1) {
    println("\(i) bottles of beer on the wall, \(i) bottles of beer.")
    i--
    println("Take one down and pass it around, \(i) bottles of beer on the wall.")
}

Which builds on autoclosure and trailing block syntax to seemingly add a new statement type to the language

David Berry
  • 40,941
  • 12
  • 84
  • 95