Using properties as functions in filter/map directly

I would like to be able to write this:

let numbers = "abc123def456".filter(Character.isWholeNumber) 
// numbers should now be "123456"

But I can't, I have to write it like this

let numbers = "abc123def456".filter{ $0.isWholeNumber } 

Am I missing something or is this something that actually doesn't work?

Basically I want Character.isWholeNumber (and similar instance properties on other types) to be a function of type (Character) -> Bool because the property is of type Bool, similar to how Character.init(content:) is a function of type (Unicode.Scalar) -> Character which I can use in map and friends.

1 Like

Once SE-0249 goes in, you'll be able to write "abc123def456".filter(\.isWholeNumber)

2 Likes

Eh, no - and I'll explain why.

It's kind of annoying, but unapplied property references aren't closures like function references are. Even then, instance methods don't work because there's an extra level of application required (currying):

extension Character {

    var _isWholeNumberProp: Bool {
        return self.isWholeNumber
    }

    func _isWholeNumberFn() -> Bool {
        return self.isWholeNumber
    }
}

// To use the unapplied reference, you need to "apply" the function twice:
// once to bind the 'self' value, and then again to actually invoke the closure.

Character._isWholeNumberFn("a")() // false.

// Unapplied property references cannot be used as closures.

let _: (Character)->()->Bool = Character._isWholeNumberFn // works.
let _: (Character)->()->Bool = Character._isWholeNumberProp // ERROR.

// Due to the double application, you can't even use the instance method
// as an argument to 'filter':
let numbers = "abc123def456".filter(Character._isWholeNumberFn) // ERROR.

The only thing that works are static functions. In this case, the type of Character._isWholeNumberStatic is (Character)->Bool, which matches the required closure signature.

extension Character {
    static func _isWholeNumberStatic(_ c: Character) -> Bool {
        return c.isWholeNumber
    }
}

let numbers = "abc123def456".filter(Character._isWholeNumberStatic) // "123456"
1 Like

Nice, that would be sufficient. What's the reason for it not being in yet? It looks like it was accepted in 2017 and the implementation PR closed in July in favour of another. There seem to be a few other PRs related to this. What's the best way to follow along with the progress on this?