Swift: compile-time error with optional chaining and nil-coalescing - swift

I have a strange problem with optional chaining and nil-coalescing in Swift.
Can anyone explain why the following code won't compile:
class A<T> {
var val: T
var x: A<T>?
var y: A<T>?
init(t:T){
val = t
}
func test() -> [T] {
return (self.x?.test() ?? []) + [val] + (self.y?.test() ?? [])
}
}
But when writing
func test() -> [T] {
return (self.x?.test() ?? []) + ([val] + (self.y?.test() ?? []))
}
It does?
The error says:
Cannot convert value of type '[T]?' to expected argument type '[_]?'
For me it looks a lot like a compiler bug.

Type inferring is hard with complicated expressions. That [_] usually means that type is unknown - could not be inferred. Simplifying the expression solves the problem in 99% of the time:
class A<T> {
var val: T
var x: A<T>?
var y: A<T>?
init(t:T){
val = t
}
func test() -> [T] {
let xResult = self.x?.test() ?? []
let yResult = self.y?.test() ?? []
return xResult + [val] + yResult
}
}
Another 99% of the time, type inferring problems can be solved by specifying a type explicitly:
return (self.x?.test() as [T]? ?? []) + [val] + (self.y?.test() ?? [])
You have found a workaround yourself. That workaround with parenthesis removes certain number of type inferring paths.
Reported as SR-4309

Related

Generic function query

After reading this article by Tim Ekl - "Swift Tricks: Searching for Objects by Type", I thought "surely there's a generic function there..." so I coded this:
class One {}
let mixedArray:[Any] = ["One", 1, 1.0, One()]
func filterType1<T>(array: [Any]) -> [T] { // Compiles fine, but cannot be called
return array.flatMap( { $0 as? T })
}
//let f1 = filterType1<Int>(array: mixedArray) // syntax error
//let f1 = filterType1(array: mixedArray) // generic parameter 'T' could not be inferred
On reflection, it's obvious that the compiler can't infer the type of T at compile time if I cannot specify the type in the function name, and so to accomplish this, I need to make the seemingly spurious change:
func filterType2<T>(sampleType: T, array: [Any]) -> [T] {
return array.flatMap( { $0 as? T })
}
let f2 = filterType2(sampleType: 2, array: mixedArray)// [1] as required
However, my question is "why does the definition of the function compile, when it is impossible to call?"
Try this:
let f1: [Int] = filterType1(array: mixedArray) // [1]
Another approach:
let f1 = filterType1(array: mixedArray) as [Int]

How to denote mutable parameters in closures with Swift > 2.2?

Perhaps this is an Xcode 8 beta issue, however, prior to 2.2 the var keyword is allowed to prepend parameters in function signatures:
func (var stringName: String) { ... }
This is has since been deprecated in lieu of there being little benefit over inout
func (stringName: inout String) { ... }
I've attempted the following in a map closure, and though I don't receive a deprecation warning as mildly expected I should, the error was rather a segmentation fault: 11
let demoString = ["hi", "there", "world"].map { (var word) -> String in
let firstChar = word.remove(at: word.startIndex)
}
The error kicks in as soon as I attempt to mutate the (assumedly mutable) word variable.
I've attempted other variation e.g. using inout
let demoString = ["hi", "there", "world"].map { (word: inout String) -> String in
let firstChar = word.remove(at: word.startIndex)
}
But the compiler complains that this erroneously changes the signature of the closure altogether and won't compile.
Obviously, the workaround is simply to copy the variable to a local one within the closure:
let demoString = ["hi", "there", "world"].map { (word) -> String in
let tempWord = word
let firstChar = tempWord.remove(at: tempWord.startIndex)
}
However, I am interested in knowing if this is expected functionality & whether or not there is a way of mutating a parameter passed into a closure directly?
Closures can mutate inout arguments just fine:
var str1 = "Pine"
var str2 = "Juniper"
let closure = { (s1: inout String, s2: inout String) -> Void in
s1 = "Oak"
s2 = "Chestnut"
}
closure(&str1, &str2)
print(str1, str2)
The problem you are facing is Array.map doesn't have a method signature that includes an inout parameter.
The only way around this that I can think of is to extend Array and add the map method yourself:
extension Array {
func map<T>(_ closure: (inout T) -> Void) -> Array<T> {
var arr = [T]()
for i in 0..<self.count {
var temp : T = self[i] as! T
closure(&temp)
arr.append(temp)
}
return arr
}
}
var hi = "hi", there = "there", world = "world"
var demoStrings = [hi, there, world]
var result = demoStrings.map { (word: inout String) in
word.remove(at: word.startIndex)
}
print(result) // ["i", "here", "orld"]
As per SE-0003 var parameters no longer exist in Swift 3.
Fundamentally, you shouldn't be mutating the parameters given from map, anyway. Instead: use non-mutating functions, or make a local mutable copy.
In this case, there's no reason to be using remove(_:) just to get the first character. This would require copying a String's memory (omitting the first character), solely to get the character that was removed. It's quite clunky.
In this case, you can just get the first property (from the Collection protocol) on the characters property of String.
Try this:
let demoStrings = ["hi", "there", "world"]
let firstLetters = demoStrings.map {(word: String) -> String in
return word.characters.first
}
or for short:
let firstLetters = demoStrings.map{ $0.characters.first }

Swift block syntax failure to infer type

Can anyone explain why this single line block with an implicit return compiles:
let r = withUnsafePointer(&msg) {
dn_expand(UnsafePointer($0), eomorig: UnsafePointer($0).advancedBy(msg.count), comp_dn: UnsafePointer($0).advancedBy(offset), exp_dn: &buf, length: buf.count)
}
but this version refactored where the only difference is to avoid the multiple calls to UnsafePointer($0) doesn't:
let s = withUnsafePointer(&msg) {
let p = UnsafePointer($0)
return dn_expand(p, eomorig: p.advancedBy(msg.count), comp_dn: p.advancedBy(offset), exp_dn: &buf, length: buf.count)
}
with error message:
Cannot convert value of type 'inout [UInt8]' (aka 'inout Array<UInt8>') to expected argument type 'inout _'
The dn_function being called is just a trivial wrapper around dn_expand from libresolv:
public static func dn_expand(msg: UnsafePointer<UInt8>, eomorig: UnsafePointer<UInt8>, comp_dn: UnsafePointer<UInt8>, exp_dn: UnsafeMutablePointer<CChar>, length: Int) -> Int {
return Int(res_9_dn_expand(msg, eomorig, comp_dn, exp_dn, Int32(length)))
}
As already said in the comments, withUnsafePointer() is not the
correct way to get a pointer to the element storage. It compiles, but
gives unexpected results, as the following example demonstrates:
var msg: [UInt8] = [1, 2, 3, 4]
func foo(x: UnsafePointer<UInt8>) {
print(x[0])
}
withUnsafePointer(&msg) {
foo(UnsafePointer($0))
}
This prints "random" numbers, but not the expected 1. The correct
way is to call the withUnsafeBufferPointer() method on the array:
msg.withUnsafeBufferPointer {
foo($0.baseAddress)
}
In your case that would be
let r = msg.withUnsafeBufferPointer {
dn_expand($0.baseAddress, eomorig: $0.baseAddress + $0.count, ...)
}
Here the return type of the closure is inferred automatically because
it is a "single-expression" closure. If the closure contains more
than one expression, you have to specify its type:
let r = msg.withUnsafeBufferPointer { bufPtr -> Int in
let p = bufPtr.baseAddress
return dn_expand(p, eomorig: p + msg.count, ...)
}
or let the compiler infer the return type from the context:
let r: Int = msg.withUnsafeBufferPointer { bufPtr in
let p = bufPtr.baseAddress
return dn_expand(p, eomorig: p + msg.count, ...)
}

Map and flatMap difference in optional unwrapping in Swift 1.2

Both map and flatMap are defind on ImplicitlyUnwrappedOptional, but they differ (obviously) in their definition according to the documentation:
func map(f: #noescape (T) -> U) -> U!
If self == nil, returns nil. Otherwise, returns f(self!).
func flatMap(f: #noescape (T) -> U!) -> U!
Returns f(self)! iff self and f(self) are not nil.
I tried using them with a simple example:
let number: Int? = 1
let res1 = number.map { $0 + 1 }.map { $0 + 1 }
let res2 = number.flatMap { $0 + 1 }.flatMap { $0 + 1 }
res1 //3
res2 //3
But they produced the same results even if number was nil.
So my question is, what is the actual difference between them if I apply map or flatMap to ImplicitlyUnwrappedOptionals? Which one should I choose over the other and when?
(Remark: The answer has been updated to reflect the syntax changes in Swift 3 and later, such as the abolishment of ImplicitlyUnwrappedOptional.)
Optional.map() and Optional.flatMap() are declared as follows (I have omitted the throws/rethrows modifiers which are irrelevant here):
func map<U>(_ transform: (Wrapped) -> U) -> U?
func flatMap<U>(_ transform: (Wrapped) -> U?) -> U?
Let's consider a simplified version of your first example using “map”:
let number: Int? = 1
let res1 = number.map { $0 + 1 }
print(res1) // Optional(2)
number has the type Int? and the closure type is inferred as (Int) -> Int. U is Int, and the type of the return value is Int?. number is not nil, so it is unwrapped and passed 1 is passed to the closure. The closure returns 2 and map returns Optional(2). If number were nil then the result would be nil.
Now we consider a simplified version of your second example with “flatMap”:
let number: Int? = 1
let res2 = number.flatMap { $0 + 1 }
print(res2) // Optional(2)
flatMap expects a closure of type (Wrapped) -> U?, but { $0 + 1 } does not return an optional. In order to make it compile, the compiler converts this to
let res2 = number.flatMap { return Optional($0 + 1) }
Now the closure has type (Int) -> Int?, and U is Int again. Again, number is unwrapped and passed to the closure. The closure returns Optional(2) which is also the return value from flatMap. If number were nil or if the closure would return nil then the result would be nil.
So there is indeed no difference between these invocations:
let res1 = number.map { $0 + 1 }
let res2 = number.flatMap { $0 + 1 }
However that is not what flatMap is meant for. A more realistic example would be
func foo(_ s : String?) -> Int? {
return s.flatMap { Int($0) }
}
print(foo("1")) // Optional(1)
print(foo("x")) // nil (because `Int($0)` returns nil)
print(foo(nil)) // nil (because the argument is nil)
Generally, map takes a closure of type (Wrapped) -> U and transforms
Optional<Wrapped>.none --> Optional<U>.none
Optional<Wrapped>.some(wrapped) --> Optional<U>.some(transform(wrapped))
flatMap takes a closure of type (Wrapped) -> U? and transforms
Optional<Wrapped>.none --> Optional<U>.none
Optional<Wrapped>.some(wrapped) --> transform(wrapped)
Here transform(wrapped) can be Optional<U>.none as well.
If (as in your example) flatMap is called with a closure which does not return an optional then the compiler converts it to an optional automatically, and there is no difference to map anymore.
This is not possible with map() where the mapping closure has the signature (T) -> U.
That's not quite right. In my view, Martin R's answer doesn't quite get at the heart of the problem, which is that the docs do not correctly describe the difference between map and flatMap.
The difference is not what kind of closure they take. Each will happily accept a closure that produces a nonOptional or a closure that produces an Optional — despite what the docs say, and despite the difference in their declarations. All of these expressions compile:
let i : Int? = nil
let result1 = i.map {_ in "hello"} // map, closure produces nonOptional
let result2 = i.flatMap {_ in "hello"} // flatMap, closure produces nonOptional
let result3 = i.map {_ in Optional("hello") } // map, closure produces Optional
let result4 = i.flatMap {_ in Optional("hello") } // flatMap, closure produces Optional
Okay, so what's the actual difference? It's what flatMap does just in case the closure does produce an Optional: it unwraps it, thus preventing a double-wrapped Optional:
let i : Int? = nil
let result1 = i.map {_ in "hello"} // String?
let result2 = i.flatMap {_ in "hello"} // String?
let result3 = i.map {_ in Optional("hello") } // String?? // double-wrapped
let result4 = i.flatMap {_ in Optional("hello") } // String? // not double-wrapped
That is the only difference between map and flatMap.
flatMap resolves nested optionals whereas map does not.
flatmap
var temp: Int? = 3
var flag: Bool = false
print(temp.flatMap { $0 < 5 ? 1 : nil } ?? .zero)
// output: 1
map
var temp: Int? = 3
var flag: Bool = false
print(temp.map { $0 < 5 ? 1 : nil } ?? .zero)
// output: Optional(Optional(1))
[map vs compactMap vs flatMap]
Swift Optional map vs flatMap
Let's create our own simple realisation of Optional type and implement map and flatMap functions
enum CustomOptional<T> {
case none
case some(T)
public init(_ some: T) {
self = .some(some)
}
func map<U>(_ transform: (T) -> U) -> CustomOptional<U> {
switch self {
case .some(let value):
let transformResult: U = transform(value)
let result: CustomOptional<U> = CustomOptional<U>(transformResult) //<-- force wrap the transformResult
return result
case .none:
return .none
}
}
func flatMap<U>(_ transform: (T) -> CustomOptional<U>) -> CustomOptional<U> {
switch self {
case .some(let value):
let transformResult: CustomOptional<U> = transform(value)
let result: CustomOptional<U> = transformResult
return result
case .none:
return .none
}
}
}
map - can return Optional Optional
flatMap - can flat Optional Optional to Optional
Optional.map { () -> T } -> Optional<T>
Optional.map { () -> Optional<T> } -> Optional<Optional<T>>
Optional.flatMap { () -> Optional<T> } -> Optional<T>
map: returned value of transformed function is wrapped into Optional of returned value of map function
flatMap: returned value of transformed function is the same as returned value of flatMap function
//problem
//T == Int, U == CustomOptional<String>
//map<U>(_ transform: (T) -> U) -> CustomOptional<U>
//map<CustomOptional<String>>(_ transform: (Int) -> CustomOptional<String>) -> CustomOptional<CustomOptional<String>>
let result: CustomOptional<CustomOptional<String>> = CustomOptional(1).map { int in
return CustomOptional("Hello: \(int)")
}
//solution
//T == Int, U == String
//flatMap<U>(_ transform: (T) -> CustomOptional<U>) -> CustomOptional<U>
//flatMap<U>(_ transform: (Int) -> CustomOptional<String>) -> CustomOptional<String>
let result5: CustomOptional<String> = CustomOptional(1).flatMap { int in
return CustomOptional("Hello: \(int)")
}
[Swift Functor, Applicative, Monad]

Swift Array of Functions

I'm very new to Swift, but slowly learning by following the Stanford iTunes U course. I have a question about storing and calling functions in an array.
The code I have (below) seems to store the function properly, but when I try to call one of the functions I get this error: '(IntegerLiteralConvertible, IntegerLiteralConvertible) -> $T6' is not identical to (String, Op).
I found this answer that was helpful in getting to where I am, but now I'm stuck.
enum Op {
case binary((Double, Double) -> Double)
}
var ops = [String: Op]()
func addOperation(symbol: String, op: Op) {
ops[symbol] = op
}
addOperation("×", Op.binary(*))
addOperation("÷", Op.binary({ $1 / $0 }))
addOperation("+", Op.binary(+))
addOperation("−", Op.binary({ $1 - $0 }))
var newValue = ops["÷"](6, 3) // Produces the error
My understanding was that ops["÷"] should be the function I stored in the array earlier. Am I not calling it properly?
#Nate Cook answer is corret but why do you have to use enum in this case? Consider using typealias like following :
var ops = [String: Op]()
func addOperation(symbol: String, op:Op) {
ops[symbol] = op
}
addOperation("×", (*))
addOperation("÷", ({ $1 / $0 }))
addOperation("+", (+))
addOperation("−", ({ $1 - $0 }))
var newValue = ops["÷"]?(3,6)
// you have to put this outside of any class
public typealias Op = (Double, Double) -> Double
You have two problems there. First, subscripting a dictionary returns an optional value, so ops["÷"] is an Op? that needs to be unwrapped before you can use it.
Second, the associated value of an enum can't be accessed directly—you can only get the value when pattern matching in a switch statement. I'd suggest adding a computed property to Op that does the work for you:
enum Op {
case binary((Double, Double) -> Double)
var binaryCall: ((Double, Double) -> Double)? {
switch self {
case let .binary(operation):
return operation
}
}
}
Then you would call it like this instead:
var newValue = ops["÷"]?.binaryCall?(6, 3)
// Optional(0.5)
An alternate method of implementation would be to just build an dictionary of binary operations, like so (you still need to unwrap once, though):
typealias BinaryOp = (Double, Double) -> Double
var binaryOps: [String: BinaryOp] = [:]
binaryOps["×"] = { $0 * $1 }
binaryOps["÷"] = { $1 / $0 }
newValue = binaryOps["÷"]?(6, 3)