Consider type Foo
:
class Foo {
var isBaz: Bool {
return false
}
func bar() {
print("some boring print")
}
}
Now let's say I want to iterate through a collection of class instances and call some function on each of them:
let someFoos: [Foo] = [Foo(), Foo(), Foo()]
someFoos.forEach { $0.bar() }
This syntax is quite compact, but it feels a bit awkward. Also, it cannot be used everywhere. For example, in an if
statement condition:
if someFoos.contains { $0.isBaz } {
// compiler error: statement cannot begin with a closure expression
}
if someFoos.contains($0.isBaz) {
// compiler error: anonymous closure argument not contained in a closure
}
if someFoos.contains({ $0.isBaz }) {
// this is correct, but requires extra pair of parentheses
}
Ideally, it would be nice to write something like
someFoos.forEach(Foo.bar)
but as of Swift 2.1 this is not a correct syntax. Such way of referencing the function would be similar to the following:
func bar2(foo: Foo) -> Void {
print("some boring print")
}
someFoos.forEach(bar2)
Is there a better way to reference instance function? How do you prefer to write such expressions?
There are two different problems here. The trailing closure syntax can be used when calling a function and the last parameter is a closure, so
let b1 = someFoos.contains({ $0.isBaz })
let b2 = someFoos.contains { $0.isBaz }
are fully equivalent. However, the trailing closure syntax can be problematic in the condition of an if-statement:
if someFoos.contains({ $0.isBaz }) { } // OK
if someFoos.contains { $0.isBaz } { } // Compiler error
if (someFoos.contains { $0.isBaz }) { } // OK, as noted by R Menke
We can only speculate why the second one does not work. It could be that the compiler
takes the first {
as the start of the if-body. Perhaps this will
change in a future version of Swift but probably it is not worth
the effort.
The other problem is about curried functions.
someFoos.forEach(bar2)
compiles because bar2
has the type Foo -> Void
, and that is exactly
what the forEach()
method expects. Foo.bar
, on the other hand,
is a curried function (see http://oleb.net/blog/2014/07/swift-instance-methods-curried-functions/) which takes the instance as the first
argument. It has the type Foo -> () -> ()
. So
Foo.bar(someFoo)
is a closure with type () -> ()
, and
Foo.bar(someFoo)()
calls the bar
method on the someFoo
instance.
(Note: The following is not meant as an actual recommendation, but only as a demonstration about curried functions and fun with closures!)
To pass Foo.bar
directly as an argument to forEach()
we need to
"swap" the order of the parameters. Haskell has a "flip" function for that purpose,
and it is also possible in Swift (see e.g. How to write a flip method in Swift?):
func flip<A, B, C>(f: A -> B ->C) -> B -> A ->C {
return { b in { a in f(a)(b) } }
}
Then flip(Foo.bar)
has the type () -> Foo -> ()
, so
the void argument of the bar
method can be applied
flip(Foo.bar)()
to get a Foo -> ()
closure, and
flip(Foo.bar)()(someFoo)
calls the bar
method on the someFoo
instance.
And now we can call
someFoos.forEach (flip(Foo.bar)())
without using a closure expression { .. }
!!
If isBaz
were a method instead of a property
func isBaz() -> Bool { return false }
then you could do the same in the if-expression:
if someFoos.contains(flip(Foo.isBaz)()) {
// ...
}
Again, this is only meant as a demonstration. Also properties
are not curried functions, so this cannot be done with
your isBaz
property.
The $0
syntax is there to help you create a shortcut, but if you don't like it you can use the more complete form:
someFoos.forEach { thisFoo in thisFoo.bar() }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With