1

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.

5
  • 3
    Do you think another developer seeing Array.map(a) will have any idea what the statement does? Commented Oct 25, 2015 at 15:37
  • Yes, because Array.map() is defined in swift standard library. What I want is something like this: Array.map(a) gives function of type (Int -> T) -> [T] Commented Oct 25, 2015 at 15:51
  • @zaph I edited question. Commented Oct 25, 2015 at 18:14
  • The problem is that given the statement: map(l)(a) there is no indication that an array with squared values will be returned, I have to go looking for the declaration of l and I have lost my focus. Even map({$0 * $0})(a) or map(squareValue)(a) is better. Write for readability for the next developer—it may even be you next year. Commented Oct 25, 2015 at 18:47
  • 2
    @zaph question is just about how to use Array.map, not about me in the next year or another developer, and l is just some lambda Int -> Int , it doesn't matter what inside it. Commented Oct 25, 2015 at 19:54

2 Answers 2

2

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)
Sign up to request clarification or add additional context in comments.

Comments

2

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.