Skip function call when required parameter is nil?

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

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

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

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.

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.

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

Swift definitely needs something like that. The current syntax is ridiculously complex for such an easy goal.