We can pass functions in Swift like this:
func celebrate(callback: (String, String) -> String) -> String {
return callback("🥝", "🥥") + "🍸"
}
func glam(item1: String, item2: String) -> String {
return item1 + "đź‘ " + item2
}
celebrate(callback: glam) // glam function being passed
Recently I learned that it can also be passed this way:
celebrate(callback: glam(item1:item2:)) // great for function overloading
I thought that it would be awesome if this syntax allowed to bind values when passing the function, something like JavaScript allows with .bind(). It could allow to pass functions requiring more parameters that those used by the caller:
func fun(item1: String, item2: String, item3: String) -> String {
return item1 + "🎤" + item2 + "🎮" + item3
}
celebrate(callback: fun) // obviously won't work, because the arguments number doesn't match
// this is the goal, but with invalid syntax:
celebrate(callback: fun(item1:item2="đźŽ":item3:))
I know this example is a little twisted, but I hope the point is clear. I'm trying to give a static value to one of the parameters during passing a function.
Can it be done with Swift?
Swift functions are unfortunately not that flexible :(
What you are trying to do is quite similar to currying, which used to be a feature of Swift, but later removed.
You can try to modify the curry function show in this question to curry a function's second argument, but that is not so useful, as it is not a general solution. You would have to write a separate curry for 1 parameter functions, 2 parameter functions, 3 parameter functions etc. For each of those, you also need a separate curry to curry the 1st parameter, the 2nd parameter, the 3rd parameter... There are simply too many cases.
I would just use a closure:
celebrate(callback: {fun(item1: $0, item2: "đźŽ", item3: $1)})
Related
Swift function parameters not accepted. Missing argument?
Calculate(theA, theB) //error: Missing argument label 'sideB:' in call
func Calculate(sideA: Int, sideB: Int) -> Int {
var ans = sideA + sideB
return ans;
}
You are missing the sideB: in your function call. I didn't want to rewrite your code (since you posted an image) but here's the working function call.
func calcButton(sender: AnyObject) {
let a: Int = 10
let b: Int = 11
calculate(a, sideB: b) //<-- Missing it here
}
func calculate(sideA: Int, sideB: Int) -> Int {
let a = sideA + sideB
return a
}
you might also want to have both variables in the function call so you can do this instead:
func calcButton(sender: AnyObject) {
let a: Int = 10
let b: Int = 11
calculate(sideA: a, sideB: b)
}
func calculate(sideA A: Int, sideB B: Int) -> Int {
return A + B
}
Just an FYI, use tab completion instead of writing out the function. Xcode will let you know all the function variables with placeholders so you can type them in.
you have missed the sideB param name in swift 3 first parameter is optional but second param is Mandatory that _ in there That’s an underscore. It changes the way the method is called. To illustrate this, here’s a very simple function:
func doStuff(thing: String) {
// do stuff with "thing"
}
It’s empty, because its contents don’t matter. Instead, let’s focus on how it’s called. Right now, it’s called like this:
doStuff(thing: "Hello")
You need to write the name of the thing parameter when you call the doStuff() function. This is a feature of Swift, and helps make your code easier to read. Sometimes, though, it doesn’t really make sense to have a name for the first parameter, usually because it’s built into the method name.
When that happens, you use the underscore character like this:
func doStuff(_ thing: String) {
// do stuff with "thing"
}
That means “when I call this function I don’t want to write thing, but inside the function I want to use thing to refer to the value that was passed in.
I am new to iOS coding and I am stuck in closures feature of SWIFT. I have referred to many tutorials and found that closures are self written codes which can be used in many ways eg. as arguments in function call,parameters in function definition,variables. I am giving below an example below with my associated thoughts about the code & questions. Please help me if I am wrong in my understanding. I know I am wrong at many points,so please rectify me.
1.1st Part
func TEST(text1:String,text2:String,flag: (S1:String,S2:String)->Bool)//In this line,I think,I am using flag is a closure which is passed as parameter in a function. And if so why doesn't it follow the standard closure syntax?
{
if flag(S1: text1, S2: text2) == true//I want to check the return type what flag closure gets when it compares the both string during function call. Why can't I write as if flag == true as flag is the name of the closure and ultimately refers to the return type of the closure?
{
print("they are equal")
}
else
{
//
}
}
2nd Part
This part is the most troublesome part that really confuses me when I am calling the function. Here I am also using the same closure. What is happening over here? How is the closure being used? Is it capturing values or something else?
TEST("heyy", text2: "heyy") { (S1, S2) -> Bool in
S1==S2
}
Thanks for your kind consideration.
Your closure usage is ok. A closure is some code that can be passed to be executed somewhere else. In your case you can choose to pass the real test you want to the function TEST, simple string test or case-insensitive test, etc. This is one of the first usage of closure: obtain more genericity.
And yes closures capture something, it captures some part of the environnement, i.e. the context in which they are defined. Look:
var m = "foo"
func test(text1:String, text2:String, testtFunc: (s1:String, s2:String) -> Bool) {
m = "bar"
if testFunc(s1: text1, s2: text2) { print("the test is true") }
}
m = "baz"
test("heyy", text2: "heyy") { (s1, s2) -> Bool in
Swift.print("Value for m is \(m)")
return s1==s2
}
The closure captures m (a variable that is defined in the context in which you define the closure), this means that this will print bar because at the time the closure is executed, the captured m equals to bar. Comment bar-line and baz will be printed; comment baz-line and foo will be printed. The closure captures m, not its value, m by itself, and this is evaluated to the correct value when the closure is evaluated.
Your first function works like this :
arguments :
String 1
String 2
a function that takes two Strings as arguments and returns a Bool
body :
execute the function (flag) with text1 and text2 and check the result.
The function doesn't know at all what you are testing, it only knows that two pieces of text are needed and a Bool will be returned.
So this function allows you to create a general way of handling different functions that all have two Strings as input. You can check for equality or if the first pieces of text is a part of the second and so on.
This is useful for many things and not so far from how array filtering / sorting / map works.
2nd Part :
This is just how you call a function with a closure.
TEST("heyy", text2: "heyy") { (S1, S2) -> Bool in
S1 == S2
}
You can also call it like this :
func testStringEqualityFor(text:String, and:String) -> Bool {
return text == and
}
TEST("hey", text2: "hey", flag: testStringEqualityFor)
Instead of using the trailing closure syntax to pass an unnamed function, you now pass a named function as one of the arguments.
It al becomes a lot clearer when you simplify it.
This is a function that takes another function as an argument.
Now we can call/use this function inside it. The argument function takes a bool as it's argument. So we give it a true
func simpleFunctionWithClosure(closure:(success:Bool) -> Void) {
// use the closure
closure(success: true)
}
When we use the function we need to pass it a function. In Swift you have the trailing closure syntax for that, but that is only available (and even then optional) to the first function as argument.
Trailing closure syntax means that instead of passing a named function you can write:
myFunction { arguments-for-closure-as-tuple -> return-for-closure-as-tuple in
function-body
}
The closure will receive an argument of Bool and returns nothing so Void.
In the body we can handle the arguments and do stuff with them.
But it is important to remember that what is inside the closure is not called directly. It is a function declaration that will be executed by simpleFunctionWithClosure
// use the function
simpleFunctionWithClosure { (success) -> Void in
if success {
print("Yeah")
} else {
print("Ow")
}
}
Or with a named function :
func argumentFunction(success:Bool) -> Void {
if success {
print("Yeah")
} else {
print("Ow")
}
}
simpleFunctionWithClosure(argumentFunction)
Compiler wouldn't have any expectation how your closure is for. For example in your first case, it could not estimate the closure's intake parameters is always just reflect to the first and second parameters of the TEST function when we are always able to write the following code :
func Test(str1:String,str2:String,closure:(String,String)->Bool){
if closure(str[str1.startIndex...str1.startIndex.advanced(2)],str2[str2.startIndex.advanced(1)...str2.endIndex])
{ ... }else{ ... }
//Just an example, everybody know no one write their code like this.
}
The second case, I thought you've just overlooked the syntax sugar:
For a trailing closure A->B:
{ a:A -> B in a.bValue() }
is equal to :
{ a:A -> B in return a.bValue() }
Also, I think this TEST function isn't a good example when the task of it can be done without using closure. I think you can write a map function by yourself for a better understand of why and when to use closure.
It seems to me that there is a discrepancy in Swift's syntax between calling an initializer and a function with at least one paremeter.
Let's consider these two examples:
class SimpleClass {
var desc: String
init(desc: String) {
self.desc = desc
}
}
let aClass = SimpleClass(desc: "description")
and
func simpleFunc(a: Int, b:Int) -> Int {
return a + b;
}
let aVal = simpleFunc(5, b: 6)
It seems odd to me that the compiler forces you to omit the first label in a function call, otherwise you will get an error "Extraneous argument label 'a:' in call". Whereas if we want to omit the first label during initilization, you get the error "Missing argument label 'desc:' in call".
The language guide says:
When calling a function with more than one parameter, any argument after the first is labeled according to its corresponding parameter name.
Source: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Functions.html
The arguments to the initializer are passed like a function call when
you create an instance of the class.
Source: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/GuidedTour.html
I'm new to Swift so I hope I didn't miss something, but this seems like a syntax discrepancy, because initializers/ constructors are just kind of functions and forcing to omit the first label in a function call seems inconsistent to me.
That's because Swift focuses on readability; function calls to be able to be read like a sentence. See this, specifically the section on "Local and External Parameter Names for Methods". Your function, to comply with this style, should be more like:
func add(a: Int, to b: Int) -> Int {
return a + b
}
let c = add(1, to: 2)
I'm trying to build a function with swift that will map an array, divide each value in the array by 3, then spit out a new array. This is what I have so far:
func divideby3Map<T, U>(y: [T], z: T -> U) -> [U] {
let array = [int]()
let divideby3Array = array.map { [y] / 3 }
return dividedby3Array
}
divideby3Map([1,2,3,4,5])
Where T and U are the original array, and the new array being returned respectively, and it's done using generics.
I'm sure this isn't written properly, I'm stuck in terms of the right syntax. For example, since the array being returned is represented by the generic [U], I assume I have to use it somewhere in the array being returned, not sure where though.
When writing a generic function, it’s sometimes easier to approach it in 3 steps: first write the code stand-alone using a specific type. Then write the code as a function, still with a specific type. Finally, change the function to be generic.
The first part, dividing an array by 3, can be done like this:
let a = [1,2,3,4,5]
// map is run on the array of integers, and returns a new
// array with the operation performed on each element in a:
let b = a.map { $0 / 3 }
// so b will be [0,0,1,1,1]
// (don’t forget, integer division truncates)
Note the closure you provide between the { } is an operation that will be applied to each element of the array. $0 represents the element, and you divide it by 3. You could also write it as a.map { i in i / 3 }.
To put this into its own function:
func divideby3Map(source: [Int]) -> [Int] {
return source.map { $0 / 3 }
}
No need to declare a fresh array – map will create one for you. You can then return that directly (you can assign it to a temporary if you prefer, but that isn’t really necessary).
Finally, if you want to make it generic, start by adding a placeholder:
func divideby3Map<T>(source: [T]) -> [T] {
return source.map { $0 / 3 }
}
Note, there’s only a need for one placeholder, T, because you are returning the exact same type you are passed in.
Except… this won’t compile, because the compiler doesn’t know that T is guaranteed to provide two critical things: the ability to divide (a / operator), and the ability to create new T from integer literals (i.e. to create a T with value 3 to divide by). Otherwise, what if we passed an array of strings or an array of arrays in?
To do this, we need to “constrain” T so our function will only accept as arguments types that provide these features. One such protocol we can use to constrain T is IntegerType, which does guarantee these features (as well as some other ones like +, * etc):
func divideby3Map<T: IntegerType>(source: [T]) -> [T] {
return source.map { $0 / 3 }
}
divideby3Map(a) // returns [0,0,1,1,1]
let smallInts: [UInt8] = [3,6,9]
divideby3Map(smallInts) // returns [1,2,3]
Is it possible to use Shorthand Argument Names with a Swift function. Closures have this feature, but since a function is in itself a closure, there might be a way to access parameters with name omitted. In detail here is what my query is:
You can implement a closure with shorthand argument name like this:
someFunction(param1, { $0 > $1 })
There is no need to provide parameter names in a closure, simply use $0, $1 etc.
For a function you may define it like so:
func functionC(Int, String) {
}
Omitting the param names here does not give any compiler error. Probably this is a swift feature. So does this mean I can access the params without name. If yes, then how?
This may be a bug or half implemented feature now because I can't find anything in the documentation about it. Using a function like you described compiles and runs fine:
func functionC(Int, String) {
println("function called")
}
functionC(10, "hello")
but attempting to use the arguments with the closure syntax $0 and $1 results in the error: Anonymous closure argument not contained in a closure which pretty clearly states you aren't allowed to use anonymous arguments in a function.
I think the purpose of this then is to be able to have the same method signature of a required function even if you don't have to use all of the arguments like this example:
protocol myProtocol{
func requiredFunc(Int, String) -> Bool
}
class myClass: myProtocol{
func requiredFunc(x: Int, String) -> Bool{
return x > 10
}
}
You can use the shorthand arguments in a closure because the compiler can infer the parameters and their types. But it is not fair to say that func functionC(Int, String) is the same as a closure with no parameter list. A closure declared the same way will not work either. Just like your function declaration, this closure is not valid because the parameters were declared:
{ (Int, String) -> Bool in
return $0 > $1
}
Short hand arguments don't work for functions. According to the compiler they work only for closures.
Coming to your stated case:
func functionC(Int, String) {
}
here it doesn't report any error because you just defined the function to take arguments of Int and String but cannot be used anywhere since they're not assigned. So there is no purpose to take this case other to verify how the compiler works.