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

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.

Related

What aren't these two ways of expressing map function equivalent?

I got a surprise today while looking at another SO question:
let s = "1,a"
let arr = s.split(separator: ",")
let result = arr.compactMap{Int($0)} // ok
let result2 = arr.compactMap(Int.init) // error
Why is line 3 legal but line 4 is not? I would have thought these two ways of saying "coerce the incoming parameter to Int if possible" would be completely equivalent.
I understand that line 4 is choking on the Subsequence, and I see how to get out of the difficulty:
let result2 = arr.map(String.init).compactMap(Int.init) // ok
What I don't understand is why they both don't choke in the same way.
Looks like the Int.init overload that accepts a Substring has the following signature:
public init?<S>(_ text: S, radix: Int = 10) where S : StringProtocol
So, Int($0) works because it uses the default radix, but there isn't an Int.init(_:) that accepts a Substring - there's only Int.init(_:radix:) that does - and so it fails.
But if there was one:
extension Int {
public init?<S>(_ text: S) where S : StringProtocol {
self.init(text, radix: 10)
}
}
then this would work:
let result1 = arr.compactMap(Int.init)
In fact the first version (Int($0)) calls this initializer, which has two parameters (one of them has a default value):
#inlinable public init?<S>(_ text: S, radix: Int = 10) where S : StringProtocol
If I define a custom initializer like so, then the second example works too.
extension Int {
init?<S>(_ string: S) where S: StringProtocol {
// convert somehow, e.g: self.init(string, radix: 10)
return nil
}
}
let result2 = arr.compactMap(Int.init)
It seems to me that if I write Int.init in the compactMap, it can call only the exact initializer (or function), and the second parameter of the first called initializer cannot be inferred.
Another example:
func test1<S>(param1: S) -> String where S: StringProtocol {
return ""
}
func test2<S>(param1: S, defaultParam: String = "") -> String where S: StringProtocol {
return ""
}
extension Sequence {
func customCompactMap<ElementOfResult>(_ transform: (Element) -> ElementOfResult?) -> [ElementOfResult] {
compactMap(transform)
}
}
arr.customCompactMap(test1)
arr.customCompactMap(test2) // error
I think the function references cannot hold any default values. Unfortunately I didn't find any official reference to this, but seems interesting.
Proof, last example:
func test3(param1: String, defaultParam: String = "") { }
let functionReference = test3
functionReference("", "")
functionReference("") // error
Here the functionReference's type is (String, String) -> (), even though the test3 function has a default value for the second parameter. As you can see functionReference cannot be called with only one value.
I tried looking for the Swift forum post where someone on the core team explained this, but sorry, I couldn't find it. You can go asking there and get clarification on this point:
Default arguments don't actually produce overloads.
Instead, using default arguments at call site is syntactic sugar for using all arguments. The compiler inserts the defaults for the ones you don't use.
A few results of that…
You cannot use functions with default arguments as closures with simplified signatures. You have to wrap them in new closures, as you demonstrated in your question.
func ƒ(_: Int = 0) { }
let intToVoid: (Int) -> Void = ƒ // compiles
// Cannot convert value of type '(Int) -> ()' to specified type '() -> Void'
let voidToVoid: () -> Void = ƒ
Methods with different default argument patterns, that look the same at call site, are not considered overrides.
class Base {
func ƒ(_: Any? = nil) -> String { "Base" }
}
final class Derived: Base {
// No `override` required.
func ƒ() -> String { "Derived" }
}
Base().ƒ() // "Base"
Derived().ƒ() // "Derived"
(Derived().ƒ as (Any?) -> String)("argument") // "Base"
Default arguments do not allow for satisfaction of protocol requirements.
protocol Protocol {
func ƒ() -> String
}
// Type 'Base' does not conform to protocol 'Protocol'
extension Base: Protocol { }

How can I change an inout parameter from within a escaping closure?

I was trying to modify a parameter of function from within a escaping closure as below:
var completion = [()->Void]()
func addCompletion(closure: #escaping ()->Void) {
completion.append(closure)
}
func testAdd(v: inout Int) {
addCompletion{
v = 1 // Compiler tells 'Escaping closure captures 'inout' parameter 'v'
print("hello1 \(v)")
}
}
var x = 0
testAdd(v:&x)
for comp in completion {
comp()
}
I was wondering if there is another way to let escaping closure change surrounding variable (as function's inout parameter), besides making it into reference type as a class object.
inout is specified to have a "copy-in copy-out" behaviour. The value of x is copied when you call the function. In the function body, the copy of x can be modified, or whatever. Then, when the function returns, the copy is copied again, and assigned to the original x. "Call-by-reference" could happen as an optimisation.
This explains why you can't modify an inout parameter in an escaping closure. The swift compiler can't possibly know when every escaping closure returns, to copy the modified value back.
You can use an actual pointer:
func testAdd(v: UnsafeMutablePointer<Int>) {
addCompletion{
v.pointee = 1 // you just need to change all references to "v" to "v.pointee"
print("hello1 \(v.pointee)")
}
}
Alternatively, if you don't like pointers, you can also do this trick (adapted from my other answer):
func testAdd(modifyV: #escaping ((inout Int) -> Void) -> Void) {
addCompletion{
modifyV { v in // here you can change v however you like!
v = 1
print("hello1 \(v)")
}
}
}
You just need to change the caller to:
testAdd { $0(&x) }

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 closure omission example?

When I read the Swift documentation provided by Apple, I found some concept not clear for me...
When a closure’s type is already known, such as the callback for a delegate, you can omit the type of its parameters, its return type, or both. Single statement closures implicitly return the value of their only statement.
let mappedNumbers = numbers.map({ number in 3 * number })
print(mappedNumbers)
What does it means the callback for a delegate? Could you give me an example? If both are omitted, should we need the in keyword?
There is nothing simpler. In this case the meaning of delegate is that the closure is used as a variable. Consider the following example:
class Downloader {
var onDownloaded: ((Data) -> Void)?
func startDownloading() {
...
}
}
Used as:
let downloader = Downloader()
downloader.onDownloaded = { data in
print("Downloaded: \(data.count) B")
}
downloader.startDownloading()
As you can see, I did not specify neither the type or the return value in the closure, because the left side of the expression (.onDownloaded =) provides them.
The same applies for method parameters:
func startDownloading(onDownloaded: ((Data) -> Void)?) {
...
}
However, we still need in in the closure. The keyword separates the parameter names from the closure body. Of course, we could make the parameters anonymous:
downloader.onDownloaded = {
print("Downloaded: \($0.count) B")
}
It states that the parameter type can be inferred from the delegate. A delegate is a protocol, which is where you define the types of the method parameters. This means that when implementing the delegate method, the compiler already knows about the method types through the declared protocol.
An example:
let sortedAnimals = animals.sort { (one: String, two: String) -> Bool in
return one < two
}
The first simplification is related to the parameters. The type inference system can calculate the type of the parameters in the closure:
let sortedAnimals = animals.sort { (one, two) -> Bool in return one < two }
The return type can also be inferred:
let sortedAnimals = animals.sort { (one, two) in return one < two }
The $i notation can substitute the parameter names:
let sortedAnimals = animals.sort { return $0 < $1 }
In single statement closures, the return keyword can be omitted:
let sortedAnimals = animals.sort { $0 < $1 }
For strings, there's a comparison function which makes string comparison even shorter:
let sortedAnimals = animals.sort(<)
Each step outputs the same result and it is for you to decide what is concise, but readable at the same time.

In Swift,there's no way to get the returned function's argument names?

When a function's return value is another function,there's no way to get the returned function's argument names.Is this a pitfall of swift language?
For example:
func makeTownGrand(budget:Int,condition: (Int)->Bool) -> ((Int,Int)->Int)?
{
guard condition(budget) else {
return nil;
}
func buildRoads(lightsToAdd: Int, toLights: Int) -> Int
{
return toLights+lightsToAdd
}
return buildRoads
}
func evaluateBudget(budget:Int) -> Bool
{
return budget > 10000
}
var stopLights = 0
if let townPlan = makeTownGrand(budget: 30000, condition: evaluateBudget)
{
stopLights = townPlan(3, 8)
}
Be mindful of townPlan,townPlan(lightsToAdd: 3, toLights: 8) would be much more sensible to townPlan(3, 8), right?
You're correct. From the Swift 3 release notes:
Argument labels have been removed from Swift function types... Unapplied references to functions or initializers no longer carry argument labels.
Thus, the type of townPlan, i.e. the type returned from calling makeTownGrand, is (Int,Int) -> Int — and carries no external argument label information.
For a full discussion of the rationale, see https://github.com/apple/swift-evolution/blob/545e7bea606f87a7ff4decf656954b0219e037d3/proposals/0111-remove-arg-label-type-significance.md