Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to execute a closure on an optional type without unwrapping it?

I have a Swift optional type and I want to chain a transformation to it (map) and eventually pass it to a closure that prints it.

I want avoid unwrapping (using !) the optional type because I don't want to execute the closure unless we have something inside the optional.

In Scala we use foreach as a map that returns Unit.

val oi = Option(2)
oi.map(_+1).foreach(println)

In Swift I get only until the increment (and the conversion to string)

let oi:Int? = 1
let oiplus = oi.map({$0 + 1}).map({String($0)})
// now we have "2"

Now how do I give this to print() only in the case the optional is not nil?

like image 444
sscarduzio Avatar asked Jan 31 '26 12:01

sscarduzio


2 Answers

I would just map over it

let oi: Int? = 1
let oiplus = oi.map{ $0 + 1 }.map { String($0) }
oiplus.map { print($0) }

Arguably not nice to map for producing a side-effect, but there's no foreach for Optional in Swift. This also produces an unused result warning in Swift 2.

If the semantic (and the warning) bug you, you can always define your own foreach for Optional. It's pretty straightforward:

extension Optional {
  func forEach(f: Wrapped -> Void) {
    switch self {
    case .None: ()
    case let .Some(w): f(w)
    }
  }
}

and then

let oi: Int? = 1
let oiplus = oi.map{ $0 + 1 }.map { String($0) }
oiplus.forEach { print($0) }

which resembles scala as much as possible.

like image 82
Gabriele Petronella Avatar answered Feb 03 '26 04:02

Gabriele Petronella


While I usually do not recommend using map for side-effects, in this case I believe it's the cleanest Swift.

_ = oiplus.map{ print($0) }

There is now a SequenceType.forEach method that is explicitly for this problem, but Optional is not a SequenceType. You could of course extend Optional to add a forEach-like method (I'd probably call it apply in Swift).

It's worth noting that this just avoids the if-lit, which is barely any longer:

if let o = oiplus { print(o) }

But it admittedly doesn't chain as nicely as:

_ = oi
    .map { $0 + 1 }
    .map { print($0) }

See also http://www.openradar.me/23247992, which is basically Gabriele's answer.

like image 21
Rob Napier Avatar answered Feb 03 '26 04:02

Rob Napier