Operators as callback functions in Swift - swift

In Swift, operators are declared as functions. Does it mean they can also be used as callback functions? If so, how? If not, why?
Idea is, depending on the values of two different numbers, apply different operators to them.

In Swift functions are first class types, you can pass them as arguments to other functions expecting them.
A callback is nothing more than a parameter of another function.
Putting this all together means that yes, you can use an operator as a callback, as long as its type is compatible.
For example you can do:
[1, 2, 3, 4].reduce(0, combine: +)
// => 10
That is possible because + has type (Int, Int) -> Int, which matches the expected type for the combine: parameter of reduce when called on an array of Int.
Another example:
func foo(a: Int, f: (Int, Int) -> Int) -> Int {
return { a, f in
return f(a, 42)
}
}
foo(1, -)
// => 41

Related

Implicit cast function receiving tuple

I just found out there's an implicit cast :
From function taking n parameters (A, B, ...) -> R
To function taking a n-tuple ((A, B, ...)) -> R
Example n°1
func withTuple(_ a: (Int, Int)) { }
func withoutTuple(_ a: Int, _ b: Int) { }
func call(tupleFunction: ((Int, Int)) -> ()) {
tupleFunction((1, 2))
}
call(tupleFunction: withTuple)
call(tupleFunction: withoutTuple) // Magic here
(Valid Swift 4.2 code)
Example n°2
[(1, 2), (3, 3)].map(*) // Magic here
Is this behaviour documented somewhere?
Before Swift 3, one was able to call a function either by explicitly specifying its arguments, or by passing a well-crafted tuple. However this way of calling functions was removed when SE-0029 was implemented.
Basically, the following was possible:
func buildDescription(name: String, age: Int) -> String {
return "Hi, I am \(name), and I am \(age)"
}
buildDescription("John Doe", age: 21)
// or, via a tuple, giving the same result
buildDescription(("John Doe", name: 21))
The Swift forums have this post regarding the above change (emphasis mine):
The proposal has been accepted for Swift 3. We acknowledge that we're removing a useful feature without providing an equally expressive drop-in replacement. However, maintaining this behavior in the type checker is a severe source of implementation complexity, and actively interferes with our plans to solidify the type system.
So it looks like the call-function-by-tuple support was prohibited only at the type checker level, meaning you cannot directly pass tuples to functions, however the internals of the compiler remained the same, which allow indirect passes of tuples, like in the examples from the question.

Anonymous function with no curly braces and no argument labels?

I saw some code on another question that seems to create an anonymous function (closure expression) with some unusual syntax:
let plus: (Int, Int) -> Int = (+)
I understand the left side—that it's declaring a constant of type (Int, Int) -> Int (a function that takes two Integers and returns an Integer). But what is (+)? How can it declare a function without curly brackets, and how does it refer to the two arguments when there are no argument labels of any kind?
The function takes two arguments, adds them together, and returns the result. If I replace the + operator with a different one (say a *), the operation changes. So is it some kind of shorthand for {$0 + $1}? If so, what is the logic behind this shorthand?
Actually, this is no shorthand.
plus is a variable of type (Int, Int) -> Int. You can assign it any object that is of this type (or any of its subtypes). A literal lambda closure is certainly of this type, but actually a named function or method would also do. And that is exactly what is happening here.
It is assigning the operator method object named + to the variable.
This is mentioned sort-of implicitly in the Closures chapter of the language guide:
Operator Methods
There’s actually an even shorter way to write the closure expression above. Swift’s String type defines its string-specific implementation of the greater-than operator (>) as a method that has two parameters of type String, and returns a value of type Bool. This exactly matches the method type needed by the sorted(by:) method. Therefore, you can simply pass in the greater-than operator, and Swift will infer that you want to use its string-specific implementation:
reversedNames = names.sorted(by: >)
So, what the code is doing is assigning the Operator Method + to the variable plus. + is simply the name of the function assigned to the variable. No magic shorthand involved.
Would you be surprised to see this?
let plus: (Int, Int) -> Int = foo
+ is an infix operator and a function name in Swift. There are many such functions defined on many types (it is overloaded).
You can define + for your own custom type. For example:
struct Foo {
var value: Int
static func +(_ lhs: Foo, _ rhs: Foo) -> Foo {
return Foo(value: lhs.value + rhs.value)
}
}
var f1 = Foo(value: 5)
var f2 = Foo(value: 3)
let f3 = f1 + f2
print(f3.value) // 8
This works:
let plus: (Int, Int) -> Int = (+)
because the signature of the + function has been fully specified, so Swift is able to identify the correct + function.
And if we want to assign our new + function to plus:
let plus: (Foo, Foo) -> Foo = (+)
It's really no different than this:
func add(_ a: Int, _ b: Double) -> Double {
return Double(a) + b
}
let plus: (Int, Double) -> Double = add
print(plus(3, 4.2)) // 7.2
So why the parentheses? Why specify (+) instead of just +?
+ is also a unary operator in Swift.
For example, you can say:
let x = +5
So just trying to say:
let plus: (Int, Int) -> Int = +
confuses the compiler because it is treating the + as a unary prefix operator and it is expecting the + to be followed by something else such as 5. By surrounding it with parentheses, the Swift compiler then stops trying to parse + as a unary operator and treats is just as its function name. Even if + weren't a unary prefix operator, Swift would still be expecting values on either side of the +, so the parentheses tell Swift that you aren't providing any inputs to the function, but just want the function itself.
You can refer to the + function without the parentheses in situations where it isn't ambiguous. For example:
var arr = [1, 2, 3]
let sum = arr.reduce(0, +)
(+) by itself is an operator method. You can declare your own operator like this:
precedencegroup CompositionPrecedence {
associativity: left
higherThan: AssignmentPrecedence
}
infix operator •: CompositionPrecedence
func •(a: Int, b: Int) -> Int {
return a + b
}
Usage will be the same:
var closure: (Int, Int) -> Int = (•)
print("\(closure(1, 2))")

Is there any way to append an element/value to a tuple?

Is there anyway to add a new element to a tuple?
var tuple = (v1: 1,v2: 2)
tuple.v3 = 3 // Error
"error: value of tuple type '(v1: Int, v2: Int)' has no member 'v3'"
No. Each tuple with a different number of elements or a different type of elements represents a different type in the Swift type system. Hence, once you create a tuple, you cannot append elements to it, because that would change the type of the tuple.
Some really basic examples of tuples and their types:
let tupleWithTwoInts = (1,2) //has type (Int,Int)
let tupleWithThreeInts = (1,2,3) //has type (Int,Int,Int)
let tupleWithTwoStrings = ("a","b") //has type (String,String)
let tupleWithIntAndString = (1,"a") //has type (Int,String)
let tupleWithStringAndInt = ("a",1) //has type (String,Int)
Even the order of the elements make a difference in the type of a tuple.
type(of: tupleWithIntAndString) == type(of: tupleWithStringAndInt) //false
If you just need to support some small number of operations, then you can write a function for it.
For example, extending a 2D vector into 3D:
func expand(_ v: (x: Int, y: Int), z: Int) -> (x: Int, y: Int, z: Int) {
return (x: v.x, y: v.y, z: z)
}
While there is no extensible way to convert your array to a tuple as stated above, if you arrived at this question because you are making reusable components, and you like the convenience that variadic declarations can provide, you can move your method body into one that takes an array and let your variadic declaration be an alternate wrapper for the same method.
This
func buttonModels(_ buttonModels: ButtonModel...) {
self.buttonModels = buttonModels
}
Becomes:
func buttonModels(_ buttonModels: ButtonModel...) {
self.buttonModels(buttonModels)
}
func buttonModels(_ buttonModels: [ButtonModel]) {
self.buttonModels = buttonModels
}
It seems to me that you are looking to use a dictionary, not a tuple. See the docs: Tuple
EDIT: As pointed out, tuples can indeed be mutable if you declare them as a var and can be used to hold editable data. My bad!

Function parameter type in Swift

In Swift Programming Language Guide, under Function Type section, it says “Because the parameter type and the return type can be a tuple type, function types support functions and methods that take multiple parameters and return multiple values.”
Note the use of word "can be" - does it mean parameter type can be something else as well? Or parameter type has to be tuple only?
you can either call a function with tuples OR with arguments
e.g.
func sum(a: Int, b: Int) -> Int {
return a + b
}
you could call this:
let numbers = (40,2)
sum(numbers)
or the old way like
sum(40,2)
A tuple means a set of parameters instead of "single" parameters.
The default way means one value per parameter like this:
func setValue( myValue:Int ) { ... }
setValue( 3 )
On the other hand a tuple can be more than one parameter:
func setValues( myValues:(Int,String) ) { ... }
setValues( (1, "Hello") )
So tuples are possible, but the common way is to use only single values

Swift functions accepting tuples

Is it possible to pass in a tuple into a function as long as their types match up?
When I try it, I get a missing argument in parameter error:
var myTuple = ("Text",10,"More Text")
func myFunction(a:String, b:Int, c:String) {
// etc...
}
myFunction(myTuple)
It was possible, although was deprecated in Swift 2.2:
In Swift 2.1 and earlier it was possible to use a carefully crafted tuple to fill the parameters of a function. So, if you had a function that took two parameters, you could call it with a two-element tuple as long as the tuple had the correct types and element names.
...
This syntax — affectionately called “tuple splat syntax” — is the antithesis of idiomatic Swift’s self-documenting, readable style, and so it’s deprecated in Swift 2.2.
https://swift.org/blog/swift-2-2-new-features/
I came here wanting to know how to pass a tuple as a function parameter. The answers here focus on a different case. I'm not entirely clear what the OP was after.
In any case, here is how to pass a tuple as a parameter. And, for good measure, how to do it variadically.
func acceptTuple(tuple : (Int, String)) {
print("The Int is: \(tuple.0)")
print("The String is '\(tuple.1)'")
}
acceptTuple((45, "zebras"))
// Outputs:
// The Int is: 45
// The String is 'zebras'
func acceptTuples(tuples : (Int, String) ...) {
var index = 0
// note: you can't use the (index, tuple) pattern in the for loop,
// the compiler thinks you're trying to unpack the tuple, hence
/// use of a manual index
for tuple in tuples {
print("[\(index)] - Int is: \(tuple.0)")
print("[\(index)] - String is '\(tuple.1)'")
index++
}
}
acceptTuples((45, "zebras"), (17, "armadillos"), (12, "caterpillars"))
//Outputs
//[0] - Int is: 45
//[0] - String is 'zebras'
//[1] - Int is: 17
//[1] - String is 'armadillos'
//[2] - Int is: 12
//[2] - String is 'caterpillars'
Passing tuples in can be a quick and convenient approach, saving you from having to create wrappers etc. For example, I have a use case where I am passing a set of tokens and parameters to create a game level. Tuples makes this nice and compact:
// function signature
class func makeLevel(target: String, tokens: (TokenType, String)...) -> GameLevel
// The function is in the class Level. TokenType here is an Enum.
// example use:
let level = Level("Zoo Station", tokens:
(.Label, "Zebra"),
(.Bat, "LeftShape"),
(.RayTube, "HighPowered"),
(.Bat, "RightShape"),
(.GravityWell, "4"),
(.Accelerator, "Alpha"))
Yes, it's possible under these conditions:
the tuple must be immutable
the number of values in the tuple, their type, and their order must match the parameters expected by the function
named parameters must match external names in the function signature
non-named parameters must match parameters without external name in the function signature
So, your code is ok, the only thing you have to do is turning the tuple into an immutable one (i.e. using let and not var):
let myTuple = ("Text", 10, "More Text")
func myFunction(a:String, b:Int, c:String) {
// etc...
}
myFunction(myTuple)
One more example with external names:
let myTuple = ("Text", paramB: 10, paramC: "More Text")
func myFunction(a:String, paramB b:Int, paramC c:String) {
// etc...
}
myFunction(myTuple)
In your tuple, it appears as though you must name them and then refer to them as such:
so your code should be
var myTuple = (val1: "Text", val2: 10, val3: "More Text")
func myFunction(a:String, b:Int, c:String) {
// etc...
}
myFunction(myTuple.val1, myTuple.val2, myTuple.val3)
The tuple has named values (val1, val2, val3) which you set and then reference, when you pass in myTuple, to the function myFunction(), it appears as though you are just filling 1 of the 3 available arguements - and with the wrong type to boot! This is the equivalent of storing the types in a tuple, then taking them out for a function call. However, if you want a function to actually take a tuple as a parameter, see below:
var myTuple = (val1: "Text", val2: 10, val3: "More Text")
func tupleFunc(a:(String, Int, String)) {
}
tupleFunc(myTuple)
Yes, but that's the wrong structure: you're passing three variables called a, b, and c rather than a tuple with those components.
You need parentheses around the whole thing:
var myTuple = ("Text", 10, "More Text")
func myFunction(a:(x: String, y: Int, z: String)) {
println(a)
}
myFunction(myTuple)
You can use the following feature: Swift allows you to pass a function (f1) with any number of parameters (but without inout parameters) as a parameter of type (TIn) -> TOut to another function. In this case, TIn will represent a tuple from the parameters of the function f1:
precedencegroup ApplyArgumentPrecedence {
higherThan: BitwiseShiftPrecedence
}
infix operator <- :ApplyArgumentPrecedence
func <-<TIn, TOut>(f: ((TIn) -> TOut), arg: TIn) -> TOut {
return f(arg)
}
func sum(_ a: Int, _ b: Int) -> Int {
return a + b
}
print(sum <- (40, 2))
In swift 3.0, we should not able to pass the tuple directly to the function.If we did so, it shows the error message as "This type has been removed in swift 3.0"
func sum(x: Int, y: Int) -> Int
return x+y }
let params = (x: 1, y: 1)
let x = params.0
let y = params.1
sum(x: x, y: y)
Hope it helps you!!
The best option for now seems to be to just save it to a compound variable or use the build in dot syntax
let (val1, val2) = (1, 2)
func f(first: Int, second: Int) { }
f(first: val1, second: val2)
let vals = (1, 2)
f(first: vals.0, second: vals.1)
That feature called implicit tuple splat was removed in swift 3.
You can find more detailed explanation on the removal proposal here
Some suggestions to keep using tuple as an argument is by doing so:
func f1(_ a : (Int, Int)) { ... }
let x = (1, 2)
f1(x)
func f2<T>(_ a : T) -> T { ... }
let x = (1, 2)
f2(x)