Base including notnan, notnothing, ...?

has there been discussion about including a notnan function and its analogs like notnothing in the Base library? I frequently find myself doing things like

filter(x -> !isnan(x), Y)

where it would be nice to simply

filter(notnan, Y)

I always consider putting the definitions in a startup file but then I would have to remember that if I share code.

you should be able to just use

filter(!isnan, y)
9 Likes

Yes, to see:

julia> !isnan
#84 (generic function with 1 method)

so writing !isnan returns a function already. This is documented behaviour of !:

help?> !

(...)

  !f::Function

  Predicate function negation: when the argument of ! is a function, it returns a function which computes the boolean negation of f.
3 Likes

Oh, that settles that. I was unaware that ! could be used cleanly as a predicate like that.

the magic of multiple dispatch :smile:

julia> methods(!)
# 3 methods for generic function "!":
[1] !(f::Function) in Base at operators.jl:1117
[2] !(x::Bool) in Base at bool.jl:35
[3] !(::Missing) in Base at missing.jl:101
3 Likes

There’s just one caveat when broadcasting though:

v = [1, nothing]

isnothing.(v)    # Ok

!isnothing.(v)  # Error

due to the fact that !isnothing.(v) is parsed as !(isnothing.(v)).
To make it work:

(!isnothing).(v) # Ok

# or

.!isnothing.(v)  # Ok
2 Likes

Curious why !(f::Function) is defined as !(f::Function) = (x...)->!f(x...) instead !(f::Function) = (!) ∘ f in Base? The latter would allow dispatching on these negated functions.

3 Likes

Seems like just an oversight — the !f operation was defined in #17155 (in 2017 for Julia 0.6) long before was changed to return a special ComposedFunction type in #37517 (in 2020 for Julia 1.6).

Changing ! to use this would make a nice PR. (You could then overload show to print !foo as !foo as well, and make !!foo return foo.) I filed an issue: !foo should use (!) ∘ foo · Issue #44748 · JuliaLang/julia · GitHub

6 Likes

Looks like the show for ComposedFunction is broken (missing parens) for operators too:

julia> (!) ∘ isnan
! ∘ isnan

julia> ! ∘ isnan
ERROR: syntax: "∘" is not a unary operator
Stacktrace:
 [1] top-level scope
   @ none:1

Too bad one can’t broadcast negated functions like

!foo.(x)

Is that ‘unfixable’?

1 Like