Curried closures in Swift3? - swift

Why
let arr = [1,2,3,4,5]
let filtered = arr.filter { $0 < 3 }
and why not?
let filtered = arr.filter(<3)
if I can use operator function:
[1,2,3].sorted(by: >)

The signatures of Sequence:s filter(...) and sorted(...) are as follows
func filter(_ isIncluded: (Self.Iterator.Element) throws -> Bool) rethrows -> [Self.Iterator.Element]
func sorted(by areInIncreasingOrder: (Self.Iterator.Element, Self.Iterator.Element) -> Bool) -> [Self.Iterator.Element]
Both methods expect a closure as their argument; the former one of type (Self.Iterator.Element) -> Bool, and the latter a one of type (Self.Iterator.Element, Self.Iterator.Element) -> Bool). < is function fulfilling the latter for Comparable types (specifically (Int, Int) -> Bool in your example), whereas <3 isn't a closure at all.
You could define your own function specifically for this purpose (thanks #vacawama)
func lessThan(_ value: Int) -> ((Int) -> Bool) {
return { $0 < value }
}
let arr = [1,2,3,4,5]
let filtered = arr.filter(lessThan(3))
print(filtered) // [1, 2]
But generally it might be more simple to just supply a closure on the fly to higher order functions such as filter and sorted.

Related

is there a more elegant syntax for Swift Filter with 2 parameters

Is there a more elegant way to filter with an additional parameter (or map, reduce).
When I filter with a single parameter, we get a beautiful easy to ready syntax
let numbers = Array(1...10)
func isGreaterThan5(number:Int) -> Bool {
return number > 5
}
numbers.filter(isGreaterThan5)
However, if I need to pass an additional parameter to my function it turns out ugly
func isGreaterThanX(number:Int,x:Int) -> Bool {
return number > x
}
numbers.filter { (number) -> Bool in
isGreaterThanX(number: number, x: 8)
}
I would like to use something like
numbers.filter(isGreaterThanX(number: $0, x: 3))
but this gives a compile error annonymous closure argument not contained in a closure
You could change your function to return a closure which serves
as predicate for the filter method:
func isGreaterThan(_ lowerBound: Int) -> (Int) -> Bool {
return { $0 > lowerBound }
}
let filtered = numbers.filter(isGreaterThan(5))
isGreaterThan is a function taking an Int argument and returning
a closure of type (Int) -> Bool. The returned closure "captures"
the value of the given lower bound.
If you make the function generic then it can be used with
other comparable types as well:
func isGreaterThan<T: Comparable>(_ lowerBound: T) -> (T) -> Bool {
return { $0 > lowerBound }
}
print(["D", "C", "B", "A"].filter(isGreaterThan("B")))
In this particular case however, a literal closure is also easy to read:
let filtered = numbers.filter( { $0 > 5 })
And just for the sake of completeness: Using the fact that
Instance Methods are Curried Functions in Swift, this would work as well:
extension Comparable {
func greaterThanFilter(value: Self) -> Bool {
return value > self
}
}
let filtered = numbers.filter(5.greaterThanFilter)
but the "reversed logic" might be confusing.
Remark: In earlier Swift versions you could use a curried function
syntax:
func isGreaterThan(lowerBound: Int)(value: Int) -> Bool {
return value > lowerBound
}
but this feature has been removed in Swift 3.

How to fix : "passing non-escaping parameter to function expecting #escaping closure"

I'm experimenting usage of closurse for First Order Predicate calculus, and
I intend to define the following function :
func ASSUM<U, V>(p: #escaping Pred<U>) -> (Pred<U>) -> Pred<(U, V)> {
return { q in AND1(p: p, q: q) }
}
that takes as parameter a predicate p: Pred<U>, where Pred<U> is a typealias for (T) -> Bool:
typealias Pred<T> = (T) -> Bool
The return of ASSUM is a Predicate transformer closure of type (Pred<U>)->Pred<(U,V)>.
However the compiler return the following error :
Passing non-escaping parameter 'q' to function expecting an #escaping closure
I understand that the function AND1 as defined requests an escaping parameter :
func AND1<U, V>(p: #escaping Pred<U>, q: #escaping Pred<V>) -> Pred<(U, V)> {
return { (x, y) in (p(x) && q(y)) }
}
but I did not succeed in explicitly making q in { q in AND1(p: p, q: q) } escaping.
How can I fix this?
You must explictly add the #escaping attribute to the argument of the return type closure of ASSUM:
typealias Pred<T> = (T)->Bool
func AND1<U, V>(p: #escaping Pred<U> , q: #escaping Pred<V>) -> Pred<(U, V)> {
return { (x,y) in (p(x) && q(y)) }
}
func ASSUM<U, V>(p: #escaping Pred<U>) -> (#escaping Pred<V>) -> Pred<(U, V)> {
/* ^ note: I believe you
want V here, not U */
return { AND1(p: p, q: $0) }
}
In the returned closure, q (anonymous $0 argument) is correctly inferred as #escaping (and needn't be explicitly marked as such, as pointed out by #Hamish, thanks!).
Note also that the generic type V in ASSUM must be inferred by explicit type annotation (or conversion) by the caller, as it is not included in any of the arguments to ASSUM.
/* example usage */
let foo = { $0 < 2 }
let bar = { $0 != "bar" }
let fooAnd: (#escaping Pred<String>) -> Pred<(Int, String)> = ASSUM(p: foo)
let fooAndBar = fooAnd(bar)
print(fooAndBar((1, "foo"))) // true
print(fooAndBar((1, "bar"))) // false
print(fooAndBar((2, "foo"))) // false
Finally, ALLCAPITAL function names is not in line with the Swift naming convention: you should prefer camelCase naming instead (see e.g. the Swift API guidelines for additional details).

About Swift functions, named parameters, and type management

Suppose I have a function overloaded as such:
func doMath(mathOption: String) -> (Int...) -> Double {
...
return average
}
func doMath(mathOption: String) -> ([Int]) -> Double {
...
return average
}
Side note: Function average itself is overloaded to accept both an array as an input or a list of parameters.
Two questions:
1 - How do I reference which function I am referring to?
For example:
let doAverage = doMath(mathOption: "average")
How do I specify which doMath function I'm calling? Swift is confused and can't infer from the next line:
If I later write:
doAverage(1,2,3,4)
2 - How do I name parameters? The original average function is called thus:
average(nums: 1,2,3,4)
I have to name the parameters. Yet with doAverage, I can't name parameters because of how the return type is defined.
3 - How could I create a type (perhaps using struct?) to simplify this hypothetical code.
Thanks for any help, explanation, or answers you offer!
Edit, to clarify 3, here is the expanded version of the situation:
func sumAll(nums: [Int]) -> Double {
return Double(nums.reduce(0, { (a,b) in a+b}))
}
func sumAll(nums: Int...) -> Double {
return sumAll(nums: nums)
}
func average(nums: [Int]) -> Double {
return sumAll(nums: nums) / Double(nums.count)
}
func average(nums: Int...) -> Double {
return average(nums: nums)
}
func doMath(mathOption: String, nums: Int...) -> Double {
if mathOption == "average" {
return average(nums: nums)
} else {
return sumAll(nums: nums)
}
}
typealias mathReturnType1 = (Int...) -> Double
typealias mathReturnType2 = ([Int]) -> Double
func doMath(mathOption: String) -> mathReturnType1 {
return average
}
func doMath(mathOption: String) -> mathReturnType2 {
return average
}
I've used typealias to create two example types. Could a type be overloaded somehow to handle both situations? To me, this makes sense in that if the same function is being overloaded to handle different inputs, why not the type? Perhaps this is a naive perspective or perhaps there is a way to express what I'm thinking in Swift?
How to reference the function? Just specify the type!
func doMath(mathOption: String) -> (Int...) -> Double {
return { (values: Int...) -> Double in
return Double(values.reduce(0, +)) / Double(values.count)
}
}
func doMath(mathOption: String) -> ([Int]) -> Double {
return { (values: [Int]) -> Double in
return Double(values.reduce(0, +)) / Double(values.count)
}
}
let average1 = doMath(mathOption: "x") as (Int...) -> Double
print(average1(1, 2, 3))
or
let average1: (Int...) -> Double = doMath(mathOption: "x")
print(average1(1, 2, 3))
I would also advise to to name that type using a typealias.
Your second question - you cannot name parameters in function types.
You can pass the function you want done to doMath as a parameter. And use generics so you have some extensibility.
func doMath<T>(using op: (T) -> Double, with value: T) -> Double {
return op(value)
}
doMath(using: sumAll, with: [1,2,3])
// returns 6
Edit: It's having trouble with the variadic parameter.
Another edit: Found a workaround.
func doMath<T>(using op: ([T]) -> Double, with value: T...) -> Double {
return op(value)
}
func doMath<T>(using op: (T) -> Double, with value: T) -> Double {
return op(value)
}
doMath(using: sumAll, with: 1,2,3,4,5) //15
doMath(using: sumAll, with: [1,2,3,4,5]) // 15
Also, here's a prettier way to write that reduce:
Double(nums.reduce(0, +))

Using Array.map instead of array.map

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.
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)
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.

How do I write map that works for all types in Swift?

As an exercise, I'm implementing a map function that takes an array and a function and applies the function to all elements of the array, but I don't know how to declare it such that it works for any type of array.
I can do something like
func intMap(var arr: [Int], fun: (Int) -> Int) -> [Int] {
for i in 0 ..< arr.count {
arr[i] = fun(arr[i])
}
return arr
}
intMap([1,2,3], {x in return x * x})
But this only works for int.
What is the type signature for Swift's built-in map?
Edit:
So I was missing the fact that I can declare param type signatures without declaring their types explicitly.
func myMap<T>(var arr: [T], fun: (T) -> T) -> [T] {
for i in 0 ..< arr.count {
arr[i] = fun(arr[i])
}
return arr
}
myMap([1,2,3], fun: {
x in return x * x
})
Create a new Playground
Just under where it has import UIKit type import Swift
Command click on the word Swift
This will open the Swift library and you can see all the type definitions there.
And you can see:
extension CollectionType {
/// Return an `Array` containing the results of mapping `transform`
/// over `self`.
///
/// - Complexity: O(N).
#warn_unused_result
#rethrows public func map<T>(#noescape transform: (Self.Generator.Element) throws -> T) rethrows -> [T]
Edited to add
Alternatively, you can write a more generalised map
func myMap<T, U>(var arr: [T], fun: T -> U) -> [U] {
var a: [U] = []
for i in 0 ..< arr.count {
a.append(fun(arr[i]))
}
return a
}
Which returns a new array, of a possibly different type, which you can see for yourself by putting this in your playground.
let a = [1, 2, 3]
let b = myMap(a, fun: { x in Double(x) * 2.1 })
a
b