Welcome to WuJiGu Developer Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
388 views
in Technique[技术] by (71.8m points)

Propagate an optional through a function (or Init) in Swift

does anyone have a (better) way to do this?

Lets say I have a optional Float

let f: Float? = 2

Now I want to cast it to a Double

let d = Double(f) //fail

This will obviously fail but is there a way to chain the optional through the function like you can with calculated variables? What I am doing now is this:

extension Float {
    var double: Double { return Double(self) }
}
let d: Double? = f?.double

But I really do not like putting a cast as a calculated variable.

Another option I have considered using is this:

public func optionalize<A,B>(_ λ : @escaping (A) -> B) -> (A?) -> B? {
    return { (a) in
        guard let a = a else { return nil }
        return λ(a)
    }
}
let d: Double? = optionalize(Double.init)(f)

I realize I can guard the value of 'f' to unwrap it. However in many cases the optional value will be the parameter for a function that returns an optional. This leads to intermediate values in the guard. As seen in this example:

func foo(_ a: String?) throws -> Float {
    guard 
        let a = a,
        let intermediate = Float(a)
    else { throw.something }
    return intermediate
}

Here it is possible for the cast from String to Float to fail also. At least with a calculated variable this foo function is a bit cleaner

extension String {
    var float: Float? { return Float(self) }
}

func foo(_ a: String?) throws -> Float {
    guard 
        let a = a?.float
    else { throw.something }
    return a
}

I do not want to rewrite optional versions of frequent inits.

Any ideas will be much appreciated. Thanks!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

You can simply use Optional's map(_:) method, which will return the wrapped value with a given transform applied if it's non-nil, else it will return nil.

let f : Float? = 2

// If f is non-nil, return the result from the wrapped value passed to Double(_:),
// else return nil.
let d = f.map { Double($0) }

Which, as you point out in the comments below, can also be said as:

let d = f.map(Double.init)

This is because map(_:) expects a transformation function of type (Float) -> Double in this case, and Double's float initialiser is such a function.

If the transform also returns an optional (such as when converting an String to a Int), you can use flatMap(_:), which simply propagates a nil transform result back to the caller:

let s : String? = "3"

// If s is non-nil, return the result from the wrapped value being passed to the Int(_:)
// initialiser. If s is nil, or Int($0) returns nil, return nil.
let i = s.flatMap { Int($0) }

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to WuJiGu Developer Q&A Community for programmer and developer-Open, Learning and Share
...