Reputation: 1044
Here is what I can do in Swift:
extension Int {
func square() -> Int { return self * self }
}
And then call it like this: 3.square()
, that gives me 9
. Also, i can do it like so: Int.square(3)
, and it will give me () -> (Int)
. So, Int.square(3)()
gives 9
.
But if I write let array = [1, 2, 3]; Array.map(array)
it gives error Cannot convert value of type 'Array<Int>' to expected argument of type '[_]'
Question is, how I can use Array.map in that way?
EDIT Ok, I'll try to explain my problem in details. Now, I have function like this:
func map<T, U>(f: T -> U) -> [T] -> [U] {
return { ts in
ts.map(f)
}
}
It works, but only on arrays. There are many types that have map function, and it not very nice to declare global function like that for every type. So, lets say there is type C that have map function C<T> -> (T -> U) -> C<U>
Also, lets say I have function f
, that transform A -> B -> C
into B -> A -> C
.
So, it looks like I can do something like this:
let array = [1, 2, 3]
let square: Int -> Int = {$0 * $0}
map(square)(array) // [1, 4, 9], works fine
f(Array.map)(square)(array) // Error
Question is not about code readability but about how Swift's type system works.
Upvotes: 1
Views: 404
Reputation: 13243
Array.map
function is defined as:
public func map<T>(self: [Self.Generator.Element]) -> (@noescape Self.Generator.Element throws -> T) rethrows -> [T]
The problem here is that the compiler cannot infer the return type of the transform
function or T
. So you have to define it the two following ways:
// variable declaration
let mapping: (Int -> Int) throws -> [Int] = Array.map(array)
// or (especially useful for further function calls)
aFunction(Array.map(array) as (Int -> Int) throws -> [Int])
You can also see that the map
function is marked as rethrows
which gets "translated" to throws
if you use the function. (It looks like a bug but closures don't have rethrows
which could be the reason for this behavior).
So the function f
could look like this in order to use it with Array.map
:
// where
// A is the array
// B is the function
// C the type of the returned array
func f<A, B, C>(f2: A -> (B throws -> C)) -> B -> (A throws -> C) {
return { b in
{ a in
try f2(a)(b)
}
}
}
// or with a forced try! so you don't have to use try
func f<A, B, C>(f2: A -> (B throws -> C)) -> B -> A -> C {
return { b in
{ a in
try! f2(a)(b)
}
}
}
// call f (use try if you use the first implementation)
let square: Int -> Int = {$0 * $0}
f(Array.map)(square)
Upvotes: 2
Reputation: 524
In the example with square the compiler can infer the type of the expression. In other words:
let f = Int.square(3)
is the same as
let f:() -> Int = Int.square(3)
However, map is a generic function that is parameterized on the closure return type:
public func map<T>(@noescape transform: (Self.Generator.Element) throws -> T) rethrows -> [T]
Consequently, this generates an error because the compiler doesn't know what T is:
let f = Array<Int>.map([1, 2, 3])
However, you can explicitly tell it what T is like this:
let f: ((Int) throws -> Int) throws -> [Int] = Array.map([1, 2, 3])
try! f({$0 * $0})
I think that answers your first question about square and map. I don't completely understand the rest of your question about converting A -> B -> C to B -> A -> C. Maybe you can provide more info on what f would look like.
Upvotes: 2