This would be similar to using x?.m() to skip a method call when the object x is nil.
As for syntax, perhaps you could try m(x, y?, z) to indicate that y may be nil even though the corresponding formal parameter is not an optional. The return type of m would be turned into an optional if necessary.
1 Like
nuclearace
(Erik Little)
2
Why would you want to do such a thing? That seems like it would hide potential logic errors around nils that the programmer should probably otherwise handle.
I think this sort of thing has been discussed before, but in the context of an expression that would have otherwise created an optional to begin with. Such as this:
struct Thing {
func doSomething(_ x: Int) -> Int { x + 1 }
func doSomething2() -> Int? {
return nil
}
}
let a = Thing() as Thing?
let b = a?.doSomething(a?.doSomething2())
I don't feel this kind of pattern is common enough to require sugaring it. And even then, I would be highly skeptical about allowing sugar that looks like it allows passing an optional to something that doesn't allow optionals.
Right now you can do y.map { m(x, $0, z) } which does return an optional, but I have also often wished for a cleaner equivalent of ?.
I think the problem with m(x, y?, z) is that the "?" is buried in the middle so it's less clear what's going on. I haven't come up with a better alternative, though.
4 Likes
jlukas
(Jacob Lukas)
4
I’ve used a mapAll helper for the case when you have many arguments that may be nil:
// Given optional a, b, c, and non-optional d
let result = mapAll(a, b, c) { a, b, c in doSomething(a, b, c, d) }
It’s a pretty easy helper to write, but it does have to be written for all of the arities you need.
2 Likes
something like
(a, b, c).map{ foo($0, $1, $2) }
would be nice
1 Like
Similar to how zip(_:_:) takes a tuple of Sequences and returns a Sequence of tuples, a zip(_:_:_:) that takes a tuple of Optionals and returns an Optional tuple would help with this:
func zip <Wrapped1, Wrapped2, Wrapped3> (
_ optional1: Wrapped1?,
_ optional2: Wrapped2?,
_ optional3: Wrapped3?
) -> (Wrapped1, Wrapped2, Wrapped3)? {
guard let wrapped1 = optional1,
let wrapped2 = optional2,
let wrapped3 = optional3 else {
return nil
}
return (wrapped1, wrapped2, wrapped3)
}
Using it in a concrete, somewhat contrived example:
import UIKit
extension UIColor {
convenience init (red: Int, green: Int, blue: Int) {
precondition(red >= 0 && red <= 255, "Invalid red component")
precondition(green >= 0 && green <= 255, "Invalid green component")
precondition(blue >= 0 && blue <= 255, "Invalid blue component")
self.init(
red: CGFloat(red) / 255.0,
green: CGFloat(green) / 255.0,
blue: CGFloat(blue) / 255.0,
alpha: 1.0
)
}
}
print("Pick a number between 0 and 255", terminator: ": ")
let greenComponent = readLine()
let shadeOfGreen = zip(0, Int(greenComponent), 0).map(UIColor.init(red:green:blue:))
1 Like
jrose
(Jordan Rose)
7
It has been discussed before:
(and possibly elsewhere)
I've definitely run into the need for this myself, but I haven't seen a way to spell it that's significantly better than map for clarity.
It's not particularly discoverable though for people who rarely use map.
jrose
(Jordan Rose)
9
Eh, optional chaining is only discoverable because you can code-complete it. That (probably?) wouldn't happen for arguments.
IMO, whenever the best solution for something is to use map/flatMap/reduce/filter, then Swift should be modified to allow an elegant solution that looks more like typical Swift code than something out of a functional language.
ddddxxx
11
I wrote a library to do so: GitHub - ddddxxx/Pipeline: Syntactic sugar for Swift optional chaining.
It's super useful when you want to (pass?.aLongOptionalChaining()?.to ?< aFunctionTakingNonOptionalValues(:))?.ThenDoSomethingElse() .
1 Like
KRR
(Roman)
12
Swift definitely needs something like that. The current syntax is ridiculously complex for such an easy goal.