Skip to content

Commit d4e26c8

Browse files
markmbaumgiordano
andauthored
predicate function negation with ComposedFunction (#44752)
Co-authored-by: Mosè Giordano <[email protected]> Co-authored-by: Mosè Giordano <[email protected]>
1 parent c0c60e8 commit d4e26c8

File tree

4 files changed

+44
-5
lines changed

4 files changed

+44
-5
lines changed

NEWS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ Library changes
4343
as `keys(::Dict)`, `values(::Dict)`, and `Set` is fixed. These methods of `iterate` can
4444
now be called on a dictionary or set shared by arbitrary tasks provided that there are no
4545
tasks mutating the dictionary or set ([#44534]).
46+
* Predicate function negation `!f` now returns a composed function `(!) ∘ f` instead of an anonymous function ([#44752]).
4647

4748

4849
Standard library changes

base/operators.jl

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,16 +1022,41 @@ end
10221022
(f, g, h...) = (f g, h...)
10231023

10241024
function show(io::IO, c::ComposedFunction)
1025-
show(io, c.outer)
1025+
c.outer isa ComposedFunction ? show(io, c.outer) : _showcomposed(io, c.outer)
10261026
print(io, "")
1027-
show(io, c.inner)
1027+
_showcomposed(io, c.inner)
1028+
end
1029+
1030+
#shows !f instead of (!) ∘ f when ! is the outermost function
1031+
function show(io::IO, c::ComposedFunction{typeof(!)})
1032+
print(io, '!')
1033+
_showcomposed(io, c.inner)
1034+
end
1035+
1036+
_showcomposed(io::IO, x) = show(io, x)
1037+
#display operators like + and - inside parens
1038+
_showcomposed(io::IO, f::Function) = isoperator(Symbol(f)) ? (print(io, '('); show(io, f); print(io, ')')) : show(io, f)
1039+
#nesting for chained composition
1040+
_showcomposed(io::IO, f::ComposedFunction) = (print(io, '('); show(io, f); print(io, ')'))
1041+
#no nesting when ! is the outer function in a composition chain
1042+
_showcomposed(io::IO, f::ComposedFunction{typeof(!)}) = show(io, f)
1043+
1044+
function show_function(io::IO, c::ComposedFunction, compact::Bool)
1045+
if compact
1046+
show(io, c)
1047+
else
1048+
print(io, "ComposedFunction(")
1049+
show_function(io, c.outer, false)
1050+
print(io, ", ")
1051+
show_function(io, c.inner, false)
1052+
print(io, ')')
1053+
end
10281054
end
10291055

10301056
"""
10311057
!f::Function
10321058
1033-
Predicate function negation: when the argument of `!` is a function, it returns a
1034-
function which computes the boolean negation of `f`.
1059+
Predicate function negation: when the argument of `!` is a function, it returns a composed function which computes the boolean negation of `f`.
10351060
10361061
See also [`∘`](@ref).
10371062
@@ -1046,8 +1071,12 @@ julia> filter(isletter, str)
10461071
julia> filter(!isletter, str)
10471072
"∀ > 0, ∃ > 0: |-| < ⇒ |()-()| < "
10481073
```
1074+
1075+
!!! compat "Julia 1.9"
1076+
Starting with Julia 1.9, `!f` returns a [`ComposedFunction`](@ref) instead of an anonymous function.
10491077
"""
1050-
!(f::Function) = (x...)->!f(x...)
1078+
!(f::Function) = (!) f
1079+
!(f::ComposedFunction{typeof(!)}) = f.inner #allows !!f === f
10511080

10521081
"""
10531082
Fix1(f, x)

base/show.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,9 +465,12 @@ function show_function(io::IO, f::Function, compact::Bool)
465465
end
466466
end
467467

468+
show_function(io::IO, x, ::Bool) = show(io, x)
469+
468470
show(io::IO, f::Function) = show_function(io, f, get(io, :compact, false)::Bool)
469471
print(io::IO, f::Function) = show_function(io, f, true)
470472

473+
471474
function show(io::IO, f::Core.IntrinsicFunction)
472475
if !(get(io, :compact, false)::Bool)
473476
print(io, "Core.Intrinsics.")

test/operators.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,12 @@ end
179179
str = randstring(20)
180180
@test filter(!isuppercase, str) == replace(str, r"[A-Z]" => "")
181181
@test filter(!islowercase, str) == replace(str, r"[a-z]" => "")
182+
@test !!isnan === isnan
183+
@test repr(!isnan) == "!isnan"
184+
@test repr((-) sin) == "(-) ∘ sin"
185+
@test repr(cos (sin tan)) == "cos ∘ (sin ∘ tan)"
186+
@test repr(!(cos !sin)) == "!(cos ∘ !sin)"
187+
@test repr(cos sin tan) == "cos ∘ sin ∘ tan" == repr((cos sin) tan)
182188
end
183189

184190
# issue #19891

0 commit comments

Comments
 (0)