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

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 { }

Related

Using generic methods in protocols Swift

I believe I have some misunderstanding of how generics work. I have the protocol:
protocol CommandProtocol {
func execute<T>() -> T
func unExecute<T>() -> T
}
And a class that conforms to it:
class CalculatorCommand: CommandProtocol {
...
func execute<String>() -> String {
return calculator.performOperation(operator: `operator`, with: operand) as! String
}
func unExecute<Double>() -> Double {
return calculator.performOperation(operator: undo(operator: `operator`), with: operand) as! Double
}
...
}
The calculator.performOperation() method actually returns Double, but here I just try to play with generics so I replace return type from Double to String.
After that, I have a class which invokes these methods:
class Sender {
...
// MARK: - Public methods
func undo() -> Double {
if current > 0 {
current -= 1
let command = commands[current]
return command.unExecute()
}
return 0
}
func redo() -> Double? {
if current < commands.count {
let command = commands[current]
current += 1
let value: Double = command.execute()
print(type(of: value))
return command.execute()
}
return nil
}
...
}
In the undo() method everything works as expected (one thing that I did not understand fully is how Swift really knows whether the unExecute value will return Double or not, or compiler infers it based on the undo() return type?)
But in the redo() method, I am calling the execute() method which returns String, but the method expects Double, so I thought that my program would crash, but not, it works totally fine as if execute() method returns Double.
Please, could someone explain to me what exactly happens under the cover of this code? Thank you in advance.
You are correct that you misunderstand generics. First, let's look at this protocol:
protocol CommandProtocol {
func execute<T>() -> T
func unExecute<T>() -> T
}
This says "no matter what type the caller requests, this function will return that type." That's impossible to successfully implement (by "successfully" I mean "correctly returns a value in all cases without crashing"). According this protocol, I'm allowed to write the following code:
func run(command: CommandProtocol) -> MyCustomType {
let result: MyCustomType = command.execute()
return result
}
There's no way to write an execute that will actually do that, no matter what MyCustomType is.
Your confusion is compounded by a subtle syntax mistake:
func execute<String>() -> String {
This does not mean "T = String," which is what I think you expect it to mean. It creates a type variable called String (that has nothing to do with Swift's String type), and it promises to return it. when you later write as! String, that means "if this values isn't compatible with the type requested (not "a string" but whatever was requested by the caller), then crash.
The tool that behaves closer to what you want here is an associated type. You meant to write this:
protocol CommandProtocol {
associatedType T
func execute() -> T
func unExecute() -> T
}
But this almost certainly won't do what you want. For example, with that, it's impossible to have an array of commands.
Instead what you probably want is a struct:
struct Command {
let execute: () -> Void
let undo: () -> Void
}
You then make Commands by passing closures that do what you want:
let command = Command(execute: { self.value += 1 },
undo: { self.value -= 1 })
Alternately, since this is a calculator, you could do it this way:
struct Command {
let execute: (Double) -> Double
let undo: (Double) -> Double
}
let command = Command(execute: { $0 + 1 }, undo: { $0 - 1 })
Then your caller would look like:
value = command.execute(value)
value = command.undo(value)
You think this returns a Swift.Double, but no. This code is no different than using T instead of Double. Swift does not require the names of generic placeholders to match what you put in a protocol.
func unExecute<Double>() -> Double {
return calculator.performOperation(operator: undo(operator: `operator`), with: operand) as! Double
}
You're not actually looking for generic methods. You want this, instead.
protocol CommandProtocol {
associatedtype ExecuteValue
associatedtype UnExecuteValue
func execute() -> ExecuteValue
func unExecute() -> UnExecuteValue
}

Swift5 #dynamicMemberLookup function

#dynamicMemberLookup subscript function.
no use parameterName is Error.
why?
#dynamicCallable is OK.
#dynamicMemberLookup
struct DynamicMember {
// OK.
subscript(dynamicMember string: String) -> String {
return string
}
// NG.
// none parameterName func. Why error?
// #dynamicMemberLookup attribute requires 'Dynamic' to have a 'subscript(dynamicMember:)' method that accepts either 'ExpressibleByStringLiteral' or a keypath
/*
subscript(dynamicMember: String) -> String {
return dynamicMember
}
*/
}
let dm = DynamicMember()
print(dm.dynamicProperty)
#dynamicCallable no parameter name OK.
#dynamicCallable
struct DynamicCall {
// OK.
func dynamicallyCall(withArguments list: [Int]) -> [Int] {
return list
}
// OK.
// none parameterName func.
// dynamicCallable is OK.
/*
func dynamicallyCall(withArguments: [Int]) -> [Int] {
return withArguments
}
*/
}
let dc = DynamicCall()
print(dc(1, 2, 3))
Compiler bug?
Please tell me who you know.
dynamicallyCall is a function, but dynamicMemberLookup is a subscript, which has variations. It is done intentionally
a) to allow key path access (for your example)
print(dm[dynamicMember: "dynamicProperty"])
b) but to disallow ambiguous, something like
dm["dynamicProperty"]
See official details on Swift Evolution

Integer conversion to generic type for calculating average. Non-nominal type does not support explicit initialization

I am trying to create a generic Queue class that has an average function however I am having trouble doing so because I need a protocol that somehow says that T(Int) is a valid operation.
This was my attempt
class Queue<T:Numeric & Comparable> {
private var array:[T]
.....
func average() -> T {
return sum() / T(array.count)
}
}
However for obvious reasons the compiler says that I cant do that because T does not support explicit initialization. What is the name of a protocol that implements this behavior or how can I code my own?
Note that Numeric Protocol also includes FloatingPoint types, so you should constrain your Queue generic type to BinaryInteger. And regarding your average return type you should return Double instead of the generic integer. Your Queue class should look like this:
class Queue<T: BinaryInteger & Comparable> {
private var array: [T] = []
init(array: [T]) {
self.array = array
}
func sum() -> T {
return array.reduce(0, +)
}
func average() -> Double {
return array.isEmpty ? 0 : Double(Int(sum())) / Double(array.count)
}
// If you would like your average to return the generic type instead of Double you can use numericCast method which traps on overflow and converts a value when the destination type can be inferred from the context.
// func average() -> T {
// return sum() / numericCast(array.count)
// }
}
Playground Testing
let queue = Queue(array: [1,2,3,4,5])
queue.sum() // 15
queue.average() // 3
If you would like to extend an array of Numeric, BinaryInteger or FloatingPoint types you can check this answer.

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)")

Using a Type Variable in a Generic

I have this question except for Swift. How do I use a Type variable in a generic?
I tried this:
func intType() -> Int.Type {
return Int.self
}
func test() {
var t = self.intType()
var arr = Array<t>() // Error: "'t' is not a type". Uh... yeah, it is.
}
This didn't work either:
var arr = Array<t.Type>() // Error: "'t' is not a type"
var arr = Array<t.self>() // Swift doesn't seem to even understand this syntax at all.
Is there a way to do this? I get the feeling that Swift just doesn't support it and is giving me somewhat ambiguous error messages.
Edit: Here's a more complex example where the problem can't be circumvented using a generic function header. Of course it doesn't make sense, but I have a sensible use for this kind of functionality somewhere in my code and would rather post a clean example instead of my actual code:
func someTypes() -> [Any.Type] {
var ret = [Any.Type]()
for (var i = 0; i<rand()%10; i++) {
if (rand()%2 == 0){ ret.append(Int.self) }
else {ret.append(String.self) }
}
return ret
}
func test() {
var ts = self.someTypes()
for t in ts {
var arr = Array<t>()
}
}
Swift's static typing means the type of a variable must be known at compile time.
In the context of a generic function func foo<T>() { ... }, T looks like a variable, but its type is actually known at compile time based on where the function is called from. The behavior of Array<T>() depends on T, but this information is known at compile time.
When using protocols, Swift employs dynamic dispatch, so you can write Array<MyProtocol>(), and the array simply stores references to things which implement MyProtocol — so when you get something out of the array, you have access to all functions/variables/typealiases required by MyProtocol.
But if t is actually a variable of kind Any.Type, Array<t>() is meaningless since its type is actually not known at compile time. (Since Array is a generic struct, the compiler needs know which type to use as the generic parameter, but this is not possible.)
I would recommend watching some videos from WWDC this year:
Protocol-Oriented Programming in Swift
Building Better Apps with Value Types in Swift
I found this slide particularly helpful for understanding protocols and dynamic dispatch:
There is a way and it's called generics. You could do something like that.
class func foo() {
test(Int.self)
}
class func test<T>(t: T.Type) {
var arr = Array<T>()
}
You will need to hint the compiler at the type you want to specialize the function with, one way or another. Another way is with return param (discarded in that case):
class func foo() {
let _:Int = test()
}
class func test<T>() -> T {
var arr = Array<T>()
}
And using generics on a class (or struct) you don't need the extra param:
class Whatever<T> {
var array = [T]() // another way to init the array.
}
let we = Whatever<Int>()
jtbandes' answer - that you can't use your current approach because Swift is statically typed - is correct.
However, if you're willing to create a whitelist of allowable types in your array, for example in an enum, you can dynamically initialize different types at runtime.
First, create an enum of allowable types:
enum Types {
case Int
case String
}
Create an Example class. Implement your someTypes() function to use these enum values. (You could easily transform a JSON array of strings into an array of this enum.)
class Example {
func someTypes() -> [Types] {
var ret = [Types]()
for _ in 1...rand()%10 {
if (rand()%2 == 0){ ret.append(.Int) }
else {ret.append(.String) }
}
return ret
}
Now implement your test function, using switch to scope arr for each allowable type:
func test() {
let types = self.someTypes()
for type in types {
switch type {
case .Int:
var arr = [Int]()
arr += [4]
case .String:
var arr = [String]()
arr += ["hi"]
}
}
}
}
As you may know, you could alternatively declare arr as [Any] to mix types (the "heterogenous" case in jtbandes' answer):
var arr = [Any]()
for type in types {
switch type {
case .Int:
arr += [4]
case .String:
arr += ["hi"]
}
}
print(arr)
I would break it down with the things you already learned from the first answer. I took the liberty to refactor some code. Here it is:
func someTypes<T>(t: T.Type) -> [Any.Type] {
var ret = [Any.Type]()
for _ in 0..<rand()%10 {
if (rand()%2 == 0){ ret.append(T.self) }
else {
ret.append(String.self)
}
}
return ret
}
func makeArray<T>(t: T) -> [T] {
return [T]()
}
func test() {
let ts = someTypes(Int.self)
for t in ts {
print(t)
}
}
This is somewhat working but I believe the way of doing this is very unorthodox. Could you use reflection (mirroring) instead?
Its possible so long as you can provide "a hint" to the compiler about the type of... T. So in the example below one must use : String?.
func cast<T>(_ value: Any) -> T? {
return value as? T
}
let inputValue: Any = "this is a test"
let casted: String? = cast(inputValue)
print(casted) // Optional("this is a test")
print(type(of: casted)) // Optional<String>
Why Swift doesn't just allow us to let casted = cast<String>(inputValue) I'll never know.
One annoying scenerio is when your func has no return value. Then its not always straightford to provide the necessary "hint". Lets look at this example...
func asyncCast<T>(_ value: Any, completion: (T?) -> Void) {
completion(value as? T)
}
The following client code DOES NOT COMPILE. It gives a "Generic parameter 'T' could not be inferred" error.
let inputValue: Any = "this is a test"
asyncCast(inputValue) { casted in
print(casted)
print(type(of: casted))
}
But you can solve this by providing a "hint" to compiler as follows:
asyncCast(inputValue) { (casted: String?) in
print(casted) // Optional("this is a test")
print(type(of: casted)) // Optional<String>
}