What exactly differentiate two functions in Swift? - swift

I wonder what makes two "similar functions" different with each other and can be called unambiguously
This is what I've learned from self-study
The uniqueness comes from: Function name + Argument order and argument name + return type. The combination of them must be unique to be able to make the function unique (Please see the example below)
Nmu1 and Num2 would cause ambiguous, because the return type (doesn't has the so called return name, the function name already act as this role) is not referred when calling a function
Although Num3 function has a different parameter name, it won't uniquely separate it from the function in Num1 and Num2. Because the argument name won't referred when the function had been called therefore, only a different argument name won't make a function unique; And the function in Num4 is different with all functions above, because its signature Function name + Argument order and argument name + return type is unique among all previous 3 functions.
Num5 and Num6 functions are different with each other, for they have different argument orders when they were defined
//Num1
func foo(guy name: String) -> String {
return "Hi \(name)"
}
//Num2
func foo(guy name: String) {
print("Hi \(name)")
}
//Num3
func foo(guy called: String) -> String {
return "Hi \(called)"
}
//Num4
func foo(dude name: String) -> String {
return "What's up \(name)"
}
//Num5
func foo(man name: String, from place: String) {
print("Hi! I'm \(name) I come from \(place)")
}
//Num6
func foo(from place: String, man name: String) {
print("Hi! I'm \(name) I come from \(place)")
}
Question: I might miss or possibly even misunderstood some parts. It would be very nice of you that you can correct me and add the parts I missed
Thanks
[Update]
Let's discuss a bit more on this issue. Let's started with the question that discuss the difference between Argument and Parameter. This question makes a lot of sense here!
Back to the quote from the official swift document
Each function parameter has both an argument label and a parameter name. The argument label is used when calling the function; each argument is written in the function call with its argument label before it. The parameter name is used in the implementation of the function. By default, parameters use their parameter name as their argument label.
Argument, Parameter, Argument Labels and Parameter Names are different. The difference here can be used to differential functions.
Functions with same parameter name, same parameter order, same return type and even same function body can be differentiated from different argument Labels.
func foo(dude name: String) -> Int {
print("Hi \(name)!")
return 1
}
func foo(man name: String) -> Int {
print("Hi \(name)!")
return 1
}
//foo(dude: String) & foo(man: String) is identical when calling
For more information please address to Multiple functions with the same name

Defined as Obj-C- compatible methods, Num1 and Num2 can not exist together, and you will have error defining them in NSObject subclass; however, you can mark them as #nonobjc and error will gone, since Swift able to choose correct overload by return type expected:
//Num1
#nonobjc func foo(guy name: String) -> String {
return "Hi \(name)"
}
//Num2
#nonobjc func foo(guy name: String) {
print("Hi \(name)")
}
All your other points, except this mark, seems correct to me

Related

How to get function's parameter value without parameter name in swift?

func test(_: [Int]) {
print("xxx")
}
test([1,2,3])
I saw this code is valid in swift, how can I get the value passed into test?
To explicitly answer your question:
how can I get the value passed in test?
You can't, in the same way that after
let _ = someFunction()
you have no way to get at the return value of someFunction. That's really the whole point of _ which basically means "I'm ignoring this intentionally".
In order to get the parameter value inside the function, you have to specify the parameterName. Swift's function has this kind of structure :
func someFunction(argumentLabel parameterName: Int) {
// In the function body, parameterName refers to the argument value
// for that parameter.
}
where argumentLabel is used on the function caller, for example
func eat(byWhom person: String){}
would become
eat(byWhom: "Victor")
in the caller code.
On the other hand, you can get the "Victor" value from the parameterName :
func eat(byWhom person: String){
print(person) // should print "Victor"
}
create function like this
func test(_ value: [Int]) { }
and then you can call it like this.
test([1,2]) without mentioning the parameter name.
There's no chance to get functions parameter value w/o its name. That is why it is being used. But you can do this:
func test(_ value: [Int]) {
print(value)
}
test([1,2,3])

Passing Arguments to nested functions in Swift

I'm taking a course on Closures and leading up to that is an understanding of nested functions. I find the following example confusing. You assign the nested function to a variable:
let person = personInTheHouse()
The variable is called as follows:
print(person(“playing cricket”)) // prints “The person is playing cricket.”
We are passing a String ("playing cricket") to the function. What I can't wrap my head around is the function does not have a string as a parameter. That is what I'm not understanding.
func personInTheHouse() -> ((String) -> String) {
Full code:
func personInTheHouse() -> ((String) -> String) {
func doProcess(process: String) -> (String) { // nested function
return “The person is \(process).”
}
return doProcess // or return doProcess(process:)
}
let person = personInTheHouse()
print(person(“playing cricket”)) // prints “The person is playing cricket.”
You are mistaking with this:
You assign the nested function to a variable:
let person = personInTheHouse()
personInTheHouse is a nested function, but personInTheHouse() is the result of applying () to personInTheHouse, its return type is (String) -> String.
So, the type of person is (String) -> String, a normal (non-nested) function. It has a parameter of String, and you can pass a String “playing cricket” to person.
Inside your personInTheHouse, referencing function name without parameters like doProcess (or doProcess(process:)) represents the function itself as a closure. But adding actual parameter like personInTheHouse() represents the result of the function call.
personInTheHouse is function that takes no argument and returns another function. When you write:
let person = personInTheHouse()
person is a function too. What kind of function is it? If you read the definition of personInTheHouse, you see that it returns doProcess, a function that takes a String and returns a String. So person == doProcess — it takes a String and returns a String.
That why you can write
print(person("playing cricket"))

What does unnamed parameters must be written with the empty name '_' mean?

I'm learning Swift from a book and we are using Playgrounds to build out a class. I received an error that reads: unnamed parameters must be written with the empty name '_'.
I understand an underscore in Swift means "to ignore" but if I add an underscore followed by a space then I receive the error: Parameter requires an explicit type which is fairly easy to understand, meaning that a parameter must be declared as a certain type. :)
I'd like to know exactly what the error "unnamed parameters must be written with the empty name '_'" is trying to say in layman terms because its not making much sense to a noob like me.
Here is the code from the playground up to this point:
//: Playground - noun: a place where people can play
import UIKit
var str = "Hello, playground"
func fahrenheitToCelsius(fahrenheitValue: Double)-> Double {
var result: Double
result = (((fahrenheitValue - 32) * 5) / 9)
return result
}
var x = fahrenheitToCelsius(fahrenheitValue: 15.3)
print(x)
class Door{
var opened: Bool = false
var locked: Bool = false
let width: Int = 32
let height: Int = 72
let weight: Int = 10
let color: String = "Red"
//behaviors
func open(_ Void)->String{
opened = true
return "C-r-r-e-e-a-k-k-k...the door is open!"
}
func close(_ Void)->String{
opened = false
return "C-r-r-e-e-a-k-k-k...the door is closed!"
}
func lock(_ Void)->String{
locked = true
return "C-l-i-c-c-c-k-k...the door is locked!"
}
func unlock(_ Void)->String{
locked = false
return "C-l-i-c-c-c-k-k...the door is unlocked!"
}
}
I guess, your code was something like this, when you get unnamed parameters must be written with the empty name '_'.
func open(Void)->String{
opened = true
return "C-r-r-e-e-a-k-k-k...the door is open!"
}
Seems you are an experienced C-programmer.
In Swift, single-parameter functions (including methods) should have this sort of header:
func functionName(paramLabel paramName: ParamType) -> ResultType
When the paramLabel and paramName are the same, it can be like this:
func functionName(paramName: ParamType) -> ResultType
You can use _ both for paramLabel and paramName, so this is a valid function header in Swift, when a single argument should be passed to the function and it is not used inside the function body:
func functionName(_: ParamType) -> ResultType
But in old Swift, you could write something like this in the same case:
func functionName(ParamType) -> ResultType
Which is not a valid function header in the current Swift. So, when Swift compiler find this sort of function header, it generates a diagnostic message like: unnamed parameters must be written with the empty name '_' which is suggesting you need _: before the ParamType.
The actual fix you need is included in the Lawliet's answer. You have no need to put Void inside the parameter when your function takes no parameters.
func open()->String{
opened = true
return "C-r-r-e-e-a-k-k-k...the door is open!"
}
From my understanding, this is a practice from objective C that is carried and respected in swift. In objective C style, you name your parameters, but when you don't need them for description or readability purposes, you can just use _. Here's an example
init(_ parameter: Type)
Objective C protocols also follow this naming convention -
tableView(_ tableView: UITableView.......)
// in swift
protocol MyCustomProtocol: AnyObject {
func controller(_ controller: MyCustomControllerClass, DidFinishLoadingSomething something: Type)
}
When you do want to name your parameters in your functions, you can -
class CustomClass {
init(withUserId id: String)
}
// to use the above:
CustomClass(withUserId: "123123")
func insert(newIndexPath indexPath: IndexPath)
...
insert(newIndexPath: myNewIndexPath) // This is how you would use the above function
To help with your problem specifically, you specified that your func open does not need a parameter name. But you never specified what your parameter is. If you do want to pass a parameter, call it func open(_ open: Bool) -> String { , or if you don't want a parameter for that function, just use func open() -> String {
Parameter requires an explicit type. Therefore, the func open(_ Void)->String function declaration causes a compile error. If you just want to write a function that has no argument, remove _ Void.
func open()->String{
opened = true
return "C-r-r-e-e-a-k-k-k...the door is open!"
}
According to Apple's Swift book, the underscore (_) can be used in various cases in Swift.
Function: If you don't want an argument label for a parameter, _ can be used rather than having an explicit argument.
func sumOf(_ arg1: Int, arg2: Int) -> Int{
return arg1 + arg2
}
sumOf(1, arg2: 5)
Numeric Literals: Both Int and Float can contain _ to get better readability.
let oneBillion = 1_000_000_000
let justOverOneThousand = 1_000.000_1
Control Flow: If you don't need each value from a sequence, you can ignore the values by using an _, aka the Wildcard Pattern, in place of a variable name.
let base = 2
let power = 10
var result = 1
for _ in 1...power {
result *= base
}
Tuples: You can use _ to ignore parts of a tuple.
let http404Error = (404, "Not Found")
// Decompose to get both values
let (statusCode, statusMessage) = http404Error
print("The status code is \(statusCode)")
print("The status message is \(statusMessage)")
// Decompose to get the status code only
let (justTheStatusCode, _) = http404Error
print("The status code is \(justTheStatusCode)")

Swift, generic function: Why is one argument label needed, the other is not?

The Swift Playground has this function:
func repeatItem<Item>(item: Item, numberOfTimes: Int) -> [Item] {
var result = [Item]()
for _ in 0..<numberOfTimes {
result.append(item)
}
return result
}
let strArray: [String] = repeatItem("knock", numberOfTimes:4) //!!!!
Why is there a numberOfTimes: in the function call and why does removing it give me the error "missing argument label"? More confusingly, why does adding an argument label to "knock" give me "extraneous argument label"?
EDIT:
Also this piece of code has not arguments labels in the call:
func anyCommonElements <T: SequenceType, U: SequenceType where T.Generator.Element: Equatable, T.Generator.Element == U.Generator.Element> (lhs: T, _ rhs: U) -> Bool {
for lhsItem in lhs {
for rhsItem in rhs {
if lhsItem == rhsItem {
return true
}
}
}
return false
}
anyCommonElements([1, 2, 3], [3])
Question 1
This is by construction of Swift. From Swift language guide for functions - Function Parameter Names:
By default, the first parameter omits its external name, and the
second and subsequent parameters use their local name as their
external name. All parameters must have unique local names. Although
it’s possible for multiple parameters to have the same external name,
unique external names help make your code more readable.
...
If you do not want to use an external name for the second or
subsequent parameters of a function, write an underscore (_) instead
of an explicit external name for that parameter.
Note from above that you can supersede this demand by placing an underscore _ in front of 2nd (and onward) parameter name. In your case:
func repeatItem<Item>(item: Item, _ numberOfTimes: Int) -> [Item] { ...
Finally note that this has nothing to do with generics, but with Swift functions in general.
Question 2
Try replacing your line
let strArray: [String] = repeatItem("knock", numberOfTimes:4) //!!!!
with
let strArray = [String](count: 4, repeatedValue: "knock")
This uses the initialiser for array objects with repeated entries.
func repeatItem<Item>(item: Item, numberOfTimes: Int) -> [Item]
is a method that takes two parameters, the first is item and the second is named numberOfTimes.
In Swift, when you call a method or a function you have to write the name of the parameters followed by ":" and the its value.
In Swift the name of the first parameter can be omitted.

Why do I have to pass arguments to classes in key-value form?

class NamedShape {
var numberOfSides: Int = 0
var name: String
init(name: String) {
self.name = name
}
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides named \(name)"
}
}
I have the following example class. To create a new instance, I do
let shape = NamedShape(name: "Test")
The arguments are captured by the init() function, right? However, if I do this:
let shape = NamedShape("Test")
I get a missing argument label error!
However, if I define a silly function like this:
func printInt(numberIn: Int) {
println(numberIn)
}
I can invoke it just fine using:
printInt(5)
However, if I attempt to invoke it with the arguments formatted in the way I create a class:
printInt(numberIn: 5)
I get an extraneous argument label error!
Help a swift noob understand. Why do I need to label class arguments, but I can't label function arguments? Why are functions and classes different this way? I'm sure there's something I'm missing.
Because initializer functions aren’t called with a function name, the parameter types and names are used to disambiguate among multiple init() functions. Thus, init() function parameters have external names by default.
You can explicitly opt out of default parameter names with an underscore:
init(_ name: String) {
self.name = name
}
init() functions are special cases and all parameters are required to have an external name.
https://developer.apple.com/library/ios/documentation/swift/conceptual/Swift_Programming_Language/Initialization.html#//apple_ref/doc/uid/TP40014097-CH18-XID_306