Add `filter` and `ifSome(then:)` to Optional

extension Optional {
	func ifSome<Result>(then closure: (Wrapped) throws -> Result) rethrows -> Optional {
		if let wrapped = self { try _ = closure(wrapped) }
		return self //for chaining to ifNone(then:)
	}
	
	func ifNone<Result>(then closure: () throws -> Result) rethrows {
		if self == nil { try _ = closure() }
	}
}

func doSomething(i: Int) { print(i) }
func doSomethingElse() { print("no value!") }

let i: Int? = 5

i.ifSome(then: doSomething)
 .ifNone(then: doSomethingElse)

This is a contrived example of the usage, but it follows from your earlier point:

The same wrapping a long chain in a for loop is a bit mesy, the same could be said for if. The variable declaration is at the very start of the whole expression, before the chain, yet its usage is in the block at the very end, after the chain:

if let result = Something() // result defined all the way up here
	.doSomething()
	.map { $0.something }
	.filter({ $0.isValid }) { // can't use trailing closure syntax on last line
	use(result) //result used all the way down here
}
else {
	handleNil()
}

compared to:

Something()
    .doSomething()
    .map { $0.something }
    .filter{ $0.isValid } // can use trailing closure here
    .ifSome(then: { result in use(result) }) // result declared close to where its used
    // could even just be: .ifSome(then: use)
    .ifNone(then: handleNil)

What would the forEach equivalent be?

Something()
    .doSomething()
    .map { $0.something }
    .filter{ $0.isValid }
    .forEach(use)
    .ifEmpty(the: handleNil) // Empty? I thought it was called Optional.none or nil!
2 Likes