Generics question - can you handle both optional and non-optional args with the same function?

Hi All.

I’m trying to write a utility method that is kind of the opposite of “x ?? y”, which means “x != nil ? x : y”. I want “x != nil ? f(x) : nil” This is not difficult to write, but if you have "f(x)->z" and "g(x)->z?", I seem to need two versions of the function in order to handle it. Is there any way to do this with only one function?

public func fnil<XType,RetType> (
    _ x:XType?,
    _ f:(XType)->RetType
    )
    -> RetType?
{
    if let x = x {
        return f(x)
    } else {
        return nil
    }
}

public func fnil<XType,RetType> (
    _ x:XType?,
    _ g:(XType)->RetType?
    )
    -> RetType?
{
    if let x = x {
        return g(x)
    } else {
        return nil
    }
}

    private func f(_ x:Int) -> Int {
        return x
    }
    
    private func g(_ x:Int) -> Int? {
        if x == 5 {
            return nil
        } else {
            return x
        }
    }
    
    func testFnil() {
        XCTAssertNil(fnil(nil, {f($0)}))
        XCTAssertEqual(7, fnil(7,{f($0)}))
        
        XCTAssertNil(fnil(nil, {g($0)}))
        XCTAssertEqual(7, fnil(7, {g($0)}))
        XCTAssertNil(fnil(5, {g($0)}))
    }

Thanks!

-Kenny

Hi,

Two points:

1) What you want to do is a common operation in functional programming called flatMap. Optional type already supports it. To get “x != nil ? f(x) : nil” you say:

x.flatMap(f)

2) You don’t need multiple versions, because there is a subtype-supertype relationship between functions: You need to specify the most general signature in your single function. The most general form of the function is:

(XType) throws -> RetType?

To handle throwing case, you will need to define the return type of your own function as:

fnil<...> (...) rethrows -> RetType?

Then it will accept any of the following signatures and it just works:

func f0(_ x: XType) -> RetType
func f1(_ x: XType) -> RetType?
func f2(_ x: XType?) -> RetType
func f3(_ x: XType?) -> RetType?
func f0t(_ x: XType) throws -> RetType
func f1t(_ x: XType) throws -> RetType?
func f2t(_ x: XType?) throws -> RetType
func f3t(_ x: XType?) throws -> RetType?

Hope this helps,
Hooman

···

On Jan 11, 2018, at 5:55 PM, Kenny Leung via swift-users <swift-users@swift.org> wrote:

Hi All.

I’m trying to write a utility method that is kind of the opposite of “x ?? y”, which means “x != nil ? x : y”. I want “x != nil ? f(x) : nil” This is not difficult to write, but if you have "f(x)->z" and "g(x)->z?", I seem to need two versions of the function in order to handle it. Is there any way to do this with only one function?

public func fnil<XType,RetType> (
    _ x:XType?,
    _ f:(XType)->RetType
    )
    -> RetType?
{
    if let x = x {
        return f(x)
    } else {
        return nil
    }
}

public func fnil<XType,RetType> (
    _ x:XType?,
    _ g:(XType)->RetType?
    )
    -> RetType?
{
    if let x = x {
        return g(x)
    } else {
        return nil
    }
}

    private func f(_ x:Int) -> Int {
        return x
    }
    
    private func g(_ x:Int) -> Int? {
        if x == 5 {
            return nil
        } else {
            return x
        }
    }
    
    func testFnil() {
        XCTAssertNil(fnil(nil, {f($0)}))
        XCTAssertEqual(7, fnil(7,{f($0)}))
        
        XCTAssertNil(fnil(nil, {g($0)}))
        XCTAssertEqual(7, fnil(7, {g($0)}))
        XCTAssertNil(fnil(5, {g($0)}))
    }

Thanks!

-Kenny

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

I’m trying to write a utility method that is kind of the opposite of “x ?? y”, which means “x != nil ? x : y”. I want “x != nil ? f(x) : nil”

A good name for this method might be ‘map’ ;-)

This is not difficult to write, but if you have "f(x)->z" and "g(x)->z?",

And a good name for this one might be ‘flatMap’. ;-)

I seem to need two versions of the function in order to handle it. Is there any way to do this with only one function?

I don’t think so; if this were possible, there probably wouldn’t be the two methods to do this in the standard library.

Charles

···

On Jan 11, 2018, at 7:55 PM, Kenny Leung via swift-users <swift-users@swift.org> wrote:

Yep, it looks like Optional.flatmap() is what I need. Thanks!

-Kenny

···

On Jan 11, 2018, at 6:55 PM, Hooman Mehr <hooman@mac.com> wrote:

Hi,

Two points:

1) What you want to do is a common operation in functional programming called flatMap. Optional type already supports it. To get “x != nil ? f(x) : nil” you say:

x.flatMap(f)

2) You don’t need multiple versions, because there is a subtype-supertype relationship between functions: You need to specify the most general signature in your single function. The most general form of the function is:

(XType) throws -> RetType?

To handle throwing case, you will need to define the return type of your own function as:

fnil<...> (...) rethrows -> RetType?

Then it will accept any of the following signatures and it just works:

func f0(_ x: XType) -> RetType
func f1(_ x: XType) -> RetType?
func f2(_ x: XType?) -> RetType
func f3(_ x: XType?) -> RetType?
func f0t(_ x: XType) throws -> RetType
func f1t(_ x: XType) throws -> RetType?
func f2t(_ x: XType?) throws -> RetType
func f3t(_ x: XType?) throws -> RetType?

Hope this helps,
Hooman

On Jan 11, 2018, at 5:55 PM, Kenny Leung via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

Hi All.

I’m trying to write a utility method that is kind of the opposite of “x ?? y”, which means “x != nil ? x : y”. I want “x != nil ? f(x) : nil” This is not difficult to write, but if you have "f(x)->z" and "g(x)->z?", I seem to need two versions of the function in order to handle it. Is there any way to do this with only one function?

public func fnil<XType,RetType> (
    _ x:XType?,
    _ f:(XType)->RetType
    )
    -> RetType?
{
    if let x = x {
        return f(x)
    } else {
        return nil
    }
}

public func fnil<XType,RetType> (
    _ x:XType?,
    _ g:(XType)->RetType?
    )
    -> RetType?
{
    if let x = x {
        return g(x)
    } else {
        return nil
    }
}

    private func f(_ x:Int) -> Int {
        return x
    }
    
    private func g(_ x:Int) -> Int? {
        if x == 5 {
            return nil
        } else {
            return x
        }
    }
    
    func testFnil() {
        XCTAssertNil(fnil(nil, {f($0)}))
        XCTAssertEqual(7, fnil(7,{f($0)}))
        
        XCTAssertNil(fnil(nil, {g($0)}))
        XCTAssertEqual(7, fnil(7, {g($0)}))
        XCTAssertNil(fnil(5, {g($0)}))
    }

Thanks!

-Kenny

_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users