Why are the arguments to this function still a tuple? - swift

I have an extension to Dictionary that adds map, flatMap, and filter. For the most part it's functional, but I'm unhappy with how the arguments to the transform and predicate functions must be specified.
First, the extension itself:
extension Dictionary {
init<S:SequenceType where S.Generator.Element == Element>(elements: S) {
self.init()
for element in elements {
self[element.0] = element.1
}
}
func filter(#noescape predicate:(Key, Value) throws -> Bool ) rethrows -> [Key:Value] {
return [Key:Value](elements:try lazy.filter({
return try predicate($0.0, $0.1)
}))
}
}
Now then, since the predicate argument is declared as predicate:(Key, Value), I would expect the following to work:
["a":1, "b":2].filter { $0 == "a" }
however, I have to actually use:
["a":1, "b":2].filter { $0.0 == "a" }
This is kind of confusing to use since the declaration implies that there are two arguments to the predicate when it's actually being passed as a single tuple argument with 2 values instead.
Obviously, I could change the filter function declaration to take a single argument (predicate:(Element)), but I really prefer it to take two explicitly separate arguments.
Any ideas on how I can actually get the function to take two arguments?

When you are using closures without type declaration, the compiler has to infer the type. If you are using only $0 and not $1, the compiler thinks that you are declaring a closure with only one parameter.
This closure then cannot be matched to your filter function. Simple fix:
let result = ["a":1, "b":2].filter { (k, _) in k == "a" }
Now also remember that tuples can be passed to functions and automatically match the parameters:
func sum(x: Int, _ y: Int) -> Int {
return x + y
}
let params = (1, 1)
sum(params)
Then the behavior with ["a":1, "b":2].filter { $0.0 == "a" } can be explained by type inferring. There are two possibilities and the compiler just chose the wrong one because it thought you want to have a closure with one argument only - and that argument had to be a tuple.

You can add a comparison function to allow you to compare a Dictionary.Element and a Key
func ==<Key: Hashable,Value>(lhs:Dictionary<Key,Value>.Element, rhs:Key) -> Bool {
return lhs.0 == rhs
}

Related

What is the Swift compiler doing with my return type? Is it somehow casting?

I have a method:
func allRegions() -> [MappedRegion] {
return self.items.lazy.compactMap { item in item.valveAny }.flatMap { valve in valve.regions }
}
I was frankly surprised this worked. I'm doing lazy stuff here, but it's apparently having a lazy sequence that turns into a sequence of MappedRegion be the same.
Then I was doing some poor mans timing and modified the function to read:
func allRegions() -> [MappedRegion] {
let startTime = Date()
let result = self.items.lazy.compactMap { item in item.valveAny }.flatMap { valve in valve.regions }
self.sumRender += (Date() - startTime)
return result
}
But that created an error:
Cannot convert return expression of type 'LazySequence<FlattenSequence<LazyMapSequence<LazyMapSequence<LazyFilterSequence<LazyMapSequence<LazySequence<[StatusRowItem]>.Elements, ValveAbstract?>>, ValveAbstract>.Elements, [MappedRegion]>>>' (aka 'LazySequence<FlattenSequence<LazyMapSequence<LazyMapSequence<LazyFilterSequence<LazyMapSequence<Array<StatusRowItem>, Optional<ValveAbstract>>>, ValveAbstract>, Array<MappedRegion>>>>') to return type '[MappedRegion]'
That was initially a surprise. I found that if I specified the return type of result as [MappedRegion] all was happy (e.g. let result:[MappedRegion] = ...).
What is going on here? I get that the original one line function is inferring the result type as [MappedRegion], so I'm probably not getting much benefits from the lazy use. But what confuses me, is that this coercion from a lazy sequence to a fixed array automagically is reminiscent of casting in C, and I thought that Swift didn't do casting?
No, there is no casting going on. There are simply two different flatMap functions being called. LazyMapSequence has two flatMap(_:) functions (well, technically four, but two are deprecated).
In your first code block, this function is inferred (because this version of flatMap has a return type that matches your allRegions function's return type):
func flatMap<SegmentOfResult>(_ transform: (Element) throws -> SegmentOfResult) rethrows -> [SegmentOfResult.Element] where SegmentOfResult : Sequence
And in your second code block, this function is inferred (because there is no type annotation on your local variable that's forcing it to choose the above version of flatMap):
func flatMap<SegmentOfResult>(_ transform: #escaping (Element) -> SegmentOfResult) -> LazySequence<FlattenSequence<LazyMapSequence<LazyMapSequence<Base, Element>, SegmentOfResult>>> where SegmentOfResult : Sequence

In Swift 3, what is a way to compare two closures?

Suppose you have two closures of type (Int)->() in Swift 3 and test to see if they are the same as each other:
typealias Baz = (Int)->()
let closure1:Baz = { print("foo \($0)") }
let closure2:Baz = { print("bar \($0)") }
if(closure1 == closure2) {
print("equal")
}
This fails to compile, giving the message:
Binary operator '==' cannot be applied to two '(Int)->()' operands
OK, well, how then can we compare two closures of the same type, to see if they are the same?
I'm pretty sure there is no way to determine if two closures are equal.
Obviously, a logical equality check is out of the question. That would be equivalent to finding an answer to the halting problem. (Just test to see if your code is equivalent to a piece of code that loops forever. If it is, it doesn't halt. If it isn't, it does halt.)
In theory you might expect the === operator to test if two closures are the exact same piece of code, but that gives an error when I try it in Playground.
Playground execution failed: error: MyPlayground.playground:1:20: error: cannot check reference equality of functions; operands here have types '(Int) -> ()' and '(Int) -> ()'
let bar = closure1 === closure2
~~~~~~~~ ^ ~~~~~~~~
Having thought about it, I'm sure the reason why that doesn't work is because you can't be sure that the closures really are equal. A closure is not just the code, but also the context in which it was created including any captures. The reason you can't check for equality is that there is no meaningful way in which two closures are equal.
To understand why thew captures are important, look at the following code.
func giveMeClosure(aString: String) -> () -> String
{
return { "returning " + aString }
}
let closure1 = giveMeClosure(aString: "foo")
let closure2 = giveMeClosure(aString: "bar")
Are closure1 and closure2 equal? They both use the same block of code
print(closure1()) // prints "returning foo"
print(closure2()) // prints "returning bar"
So they are not equal. You could argue that you can check the code is the same and the captures are the same, but what about
func giveMeACount(aString: String) -> () -> Int
{
return { aString.characters.count }
}
let closure3 = giveMeACount(aString: "foo")
let closure4 = giveMeACount(aString: "bar")
print(closure3()) // prints 3
print(closure4()) // prints 3
Apparently these closures are equal. It's not possible to implement any reasonable definition of equality that will work in every case, so Apple has instead not even tried. This is safer than providing an incomplete implementation that is wrong in some cases.
In the case where you want to track your own closures, uses them as Dictionary keys, etc., you can use something like this:
struct TaggedClosure<P, R>: Equatable, Hashable {
let id: Int
let closure: (P) -> R
static func == (lhs: TaggedClosure, rhs: TaggedClosure) -> Bool {
return lhs.id == rhs.id
}
var hashValue: Int { return id }
}
let a = TaggedClosure(id: 1) { print("foo") }
let b = TaggedClosure(id: 1) { print("foo") }
let c = TaggedClosure(id: 2) { print("bar") }
print("a == b:", a == b) // => true
print("a == c:", a == c) // => false
print("b == c:", b == c) // => false

Why does use of closure shorthand-named variables has to be exhaustive in a singular return expression in Swift?

The following piece of code are erroneous in Swift.
func foo(closure: (Int, Int) -> Int) -> Int {
return closure(1, 2)
}
print(foo(closure: {$0}))
func foo(closure: (Int, Int) -> Int) -> Int {
return closure(1, 2)
}
print(foo(closure: {return $0}))
The error given by XCode playground is Cannot convert value of type '(Int, Int)' to closure result type 'Int'.
While the following pieces of code are completely fine.
func foo(closure: (Int, Int) -> Int) -> Int {
return closure(1, 2)
}
print(foo(closure: {$0 + $1}))
func foo(closure: (Int, Int) -> Int) -> Int {
return closure(1, 2)
}
print(foo(closure: {$1; return $0}))
func foo(closure: (Int, Int) -> Int) -> Int {
return closure(1, 2)
}
print(foo(closure: {a, b in a}))
It seems that in a situation where arguments to a closure are referred to by shorthand argument names, they must be used exhaustively if the the body of the closure only consists of the return expression. Why?
If you just use $0, the closure arguments are assumed to be a tuple instead of multiple variables $0, $1 etc. So you should be able to work around this by extracting the first value of that tuple:
print(foo(closure: {$0.0}))
Your "why" is like asking "why is an American football field 100 yards long?" It's because those are the rules. An anonymous function body that takes parameters must explicitly acknowledge all parameters. It can do this in any of three ways:
Represent them using $0, $1, ... notation.
Represent them using parameter names in an in line.
Explicitly discard them by using _ in an in line.
So, let's take a much simpler example than yours:
func f(_ ff:(Int)->(Void)) {}
As you can see, the function f takes one parameter, which is a function taking one parameter.
Well then, let's try handing some anonymous functions to f.
This is legal because we name the parameter in an in line:
f {
myParam in
}
And this is legal because we accept the parameter using $0 notation:
f {
$0
}
And this is legal because we explicitly throw away the parameter using _ in the in line:
f {
_ in
}
But this is not legal:
f {
1 // error: contextual type for closure argument list expects 1 argument,
// which cannot be implicitly ignored
}

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.

Generalized type constraints with Swift

As an exercise, I'm trying to extend Array in Swift to add a sum() member function. This should be type safe in a way that I want a call to sum() to compile only if the array holds elements that can be added up.
I tried a few variants of something like this:
extension Array {
func sum<U : _IntegerArithmeticType where U == T>() -> Int {
var acc = 0
for elem in self {
acc += elem as Int
}
return acc
}
}
The idea was to say, “OK, this is a generic function, the generic type must be something like an Int, and must also be the same as T, the type of the elements of the array”. But the compiler complains: “Same-type requirement make generic parameters U and T equivalent”. That's right, and they should be, with the additional contraint T : _IntegerArithmeticType.
Why isn't the compiler letting me do this? How can I do it?
(I know that I should later fix how things are added up and what the return type exactly is, but I'm stuck at the type constraint for now.)
As per Martin R's comment, this is not currently possible. The thing I'm tempted to use in this particular situation would be an explicit passing of a T -> Int conversion function:
extension Array {
func sum(toInt: T -> Int?) -> Int {
var acc = 0
for elem in self {
if let i = toInt(elem) {
acc += i
}
}
return acc
}
}
Then I can write stuff like this:
func itself<T>(t: T) -> T {
return t
}
let ss = ["1", "2", "3", "4", "five"].sum { $0.toInt() }
let si = [1, 2, 3, 4].sum(itself)
An explicit function has to be passed, though. The (itself) part can of course be replaced by { $0 }. (Others have called the itself function identity.)
Note that an A -> B function can be passed when A -> B? is needed.