Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ambiguous reference to member 'print' - (scope within Swift closure?)

Tags:

closures

swift

This compiles:

let s = SignalsService()
s.addListener( "key", callback: { a, b in print( "success" ) } )

This does not:

let s = SignalsService()
let cb = { a, b in print( "success" ) }
s.addListener( "key", callback: cb )

Throwing the error Ambiguous reference to member 'print' at the let cb = ... line.

Why is that then?

like image 578
Joseph Beuys' Mum Avatar asked Oct 17 '15 10:10

Joseph Beuys' Mum


2 Answers

In

s.addListener( "key", callback: { a, b in print( "success" ) } )

the compiler can infer the type of the closure from the context, i.e. from the type of the addListener() method. If that method is for example declared as

func addListener(key : String, callback: (Int, Int) -> Void)

then the compiler can infer that the argument

{ a, b in print( "success" )

is a closure taking two Int parameters and returning Void.

In

let cb = { a, b in print( "success" ) }

there is no such context, therefore the compiler cannot know the type of the closure. The return type can be inferred as Void because the closure consists of a single expression, but you have to specify the types of the parameters, e.g.

let cb = { (a : Int, b : Int) in print( "success" ) }
like image 119
Martin R Avatar answered Nov 14 '22 06:11

Martin R


Expanding on @MartinR's excellent answer.

Swift needs to be able to infer that cb is of type (Int, Int)->(), or you could explicitly set the type:

let cb: (Int, Int)->() = { a, b in print( "success" ) }

Then you could notice that a and b are unused and replace them with _:

let cb: (Int, Int)->() = { _ in print( "success" ) }

Why can we get away with a single _ when the function takes 2 parameters? In this case, Swift knows there are 2 parameters, so the _ takes the place of the tuple containing all of the parameters. It replaces (_, _).


You can use _ with @MartinR's answer:

let cb = { (_:Int, _:Int) in print( "success" ) }

or you can write it like:

let cb = { (_:(Int, Int)) in print( "success" ) }

which can be read as:

cb takes two parameters of type Int which it ignores and prints "success"

like image 2
vacawama Avatar answered Nov 14 '22 08:11

vacawama