The print function can print integers and can be called like print(5). I want to do the same on an [Int] array.
However attempting to do the same on a forEach fails.
[1,2,3].forEach(print)
gives
expression failed to parse:
error: repl.swift:20:17: error: cannot convert value of type
'(Any..., String, String) -> ()' to expected argument type
'(Int) throws -> Void'
[1,2,3].forEach(print)
^
How can I get Swift to perform this conversion from (Any, ... String, String) -> () to (Int) throws -> Void without resorting to the workarounds listed below?
Aren't function types contravariant in the parameter position, meaning that since Any is a supertype of Int, (Any, ... String, String) -> () is a subtype of (Int) throws -> Void (since () is the same as Void) and therefore by the Liskov substitution principle, print should be a perfectly acceptable argument to forEach in this case?
Is it an issue with the number of (variadic AND optional!) parameters in (Any, ... String, String)?
Workarounds
These work but they seem unnecessary to me.
Creating an inline closure using shorthand argument name [1,2,3].forEach { print($0) } or defining a function func printInt(_ n: Int) { print(n) }; [1,2,3].forEach(printInt) both work.
This suggests that the issue is at least partly with the number of arguments. However I am not clearly understanding what is happening here and would appreciate any help.
Yes. There is no syntax for function type parameters that say "this is an optional parameter". When you treat
printas a first class object, its optional parameters automatically becomes required.Furthermore, the type of the first parameter is
Any.... This is not the same asAny. It is not a supertype ofIntorAny, so LSP does not apply here, even if the optional parameters problem is magically solved.Therefore, you have to wrap it in some way if you want to pass it directly to
forEach. Rather than wrapping it just forInt, you can consider wrapping it forAny:Then, because of LSP, you can pass this directly to
forEachfor collections of any element type.Personally though, I wouldn't bother, and just use
forEach { print($0) }.