From d923984e66bb019bcbe2f6cbbd57c88ce283d699 Mon Sep 17 00:00:00 2001 From: Avik Pal Date: Fri, 30 Jun 2023 18:48:46 -0400 Subject: [PATCH 1/4] First attempt at @$ macro --- Project.toml | 6 ++- README.md | 2 + src/PartialFunctions.jl | 3 +- src/generalizedpartialfunctions.jl | 74 ++++++++++++++++++++++++++++++ 4 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 src/generalizedpartialfunctions.jl diff --git a/Project.toml b/Project.toml index 07dd829..420ebe5 100644 --- a/Project.toml +++ b/Project.toml @@ -1,9 +1,13 @@ name = "PartialFunctions" uuid = "570af359-4316-4cb7-8c74-252c00c2016b" authors = ["Thomas Marks and contributors"] -version = "1.1.1" +version = "1.2.0" + +[deps] +MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" [compat] +MacroTools = "0.5" julia = "1.5" [extras] diff --git a/README.md b/README.md index 8ddbaba..615626b 100644 --- a/README.md +++ b/README.md @@ -121,3 +121,5 @@ isequal(1, 2, ...) julia> isequal $ (1, 2) <| () # equivalent to a() or isequal(1, 2) false ``` + +## Generalized Partial Functions using `@$` diff --git a/src/PartialFunctions.jl b/src/PartialFunctions.jl index 7f354c4..560da87 100644 --- a/src/PartialFunctions.jl +++ b/src/PartialFunctions.jl @@ -1,8 +1,9 @@ module PartialFunctions -export $ +export $, @$ export <| include("reversedfunctions.jl") +include("generalizedpartialfunctions.jl") name = (string ∘ Symbol) diff --git a/src/generalizedpartialfunctions.jl b/src/generalizedpartialfunctions.jl new file mode 100644 index 0000000..8474591 --- /dev/null +++ b/src/generalizedpartialfunctions.jl @@ -0,0 +1,74 @@ +using MacroTools + +struct GeneralizedPartialFunction{L, F<:Function, T<:NamedTuple, N<:NamedTuple} <: Function + expr_string::String + func::F + args::T + kwargs::N +end + +Base.show(io::IO, pf::GeneralizedPartialFunction) = print(io, pf.expr_string) +Base.show(io::IO, ::MIME"text/plain", pf::GeneralizedPartialFunction) = print(io, pf.expr_string) + +function GeneralizedPartialFunction(expr_string::String, f::F, loc::Tuple, args::T, + kwargs::N) where {F<:Function, T<:NamedTuple, + N<:NamedTuple} + return GeneralizedPartialFunction{loc, typeof(f), T, N}(expr_string, f, args, kwargs) +end + +@generated function (pf::GeneralizedPartialFunction{loc, F, T})(args...; kwargs...) where {loc, F, T} + L = length(args) + kloc = fieldnames(T) + if L == length(loc) + # Execute the function + final_args = [] + total_args = length(loc) + length(kloc) + j, k = 1, 1 + for i in 1:total_args + if i ∈ loc + push!(final_args, :(args[$j])) + j += 1 + else + push!(final_args, :(pf.args[$k])) + k += 1 + end + end + return quote + @show pf.func + @show typeof.(($(final_args...),)) + pf.func($(final_args...); pf.kwargs..., kwargs...) + end + else + # Construct another GeneralizedPartialFunction + end +end + +macro ($)(expr::Expr) + if !@capture(expr, f_(args__; kwargs__) | f_(args__)) + throw(ArgumentError("Only function calls are supported!")) + end + + kwargs = kwargs === nothing ? [] : kwargs + + underscore_args_pos = Tuple(findall(x -> x == :_, args)) + if length(args) == 0 || length(underscore_args_pos) == 0 + return :($(esc(expr))) + end + + kwargs_keys = Symbol[] + kwargs_values = Any[] + for kwarg in kwargs + @assert kwarg.head == :kw "Malformed keyword argument!" + push!(kwargs_keys, kwarg.args[1]) + push!(kwargs_values, kwarg.args[2]) + end + final_kwargs = NamedTuple{Tuple(kwargs_keys)}(kwargs_values) + + non_underscore_args_pos = Tuple(setdiff(1:length(args), underscore_args_pos)) + non_underscore_args = map(Base.Fix1(getindex, args), non_underscore_args_pos) + stored_args = NamedTuple{Symbol.(non_underscore_args_pos)}(non_underscore_args) + + return :(GeneralizedPartialFunction($(esc(string(expr))), $(esc(f)), + $(esc(underscore_args_pos)), $(esc(args)), + $(esc(final_kwargs)))) +end From 3b962b61d2d6476ac696085d2946d5f32dbfbf79 Mon Sep 17 00:00:00 2001 From: Avik Pal Date: Sat, 1 Jul 2023 16:54:52 -0400 Subject: [PATCH 2/4] Make things work + tests --- README.md | 17 ++++++++++++- docs/src/index.md | 17 +++++++++++++ src/generalizedpartialfunctions.jl | 11 ++++----- test/runtests.jl | 38 +++++++++++++++++++++++++++++- 4 files changed, 75 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 615626b..df14900 100644 --- a/README.md +++ b/README.md @@ -122,4 +122,19 @@ julia> isequal $ (1, 2) <| () # equivalent to a() or isequal(1, 2) false ``` -## Generalized Partial Functions using `@$` +## The `@$` Macro + +`@$` allows users to create general partial functions by replacing the currently known +arguments with `_`. For example, we can implement matrix multiplication as: + +```julia +julia> matmul(A, X, B) = A * X .+ B + +julia> A = randn(2, 2); B = rand(2, 2); X = randn(2, 2); + +julia> pf = @$ matmul(_, X, _) +matmul(_, X, _) + +julia> pf(A, B) ≈ matmul(A, X, B) +true +``` diff --git a/docs/src/index.md b/docs/src/index.md index 2c5ce00..2ff3ece 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -132,3 +132,20 @@ julia> isequal $ (1, 2) <| () # equivalent to a() or isequal(1, 2) false ``` +## The `@$` Macro + +`@$` allows users to create general partial functions by replacing the currently known +arguments with `_`. For example, we can implement matrix multiplication as: + +```jldoctest +julia> matmul(A, X, B) = A * X .+ B +matmul (generic function with 1 method) + +julia> A = randn(2, 2); B = rand(2, 2); X = randn(2, 2); + +julia> pf = @$ matmul(_, X, _) +matmul(_, X, _) + +julia> pf(A, B) ≈ matmul(A, X, B) +true +``` diff --git a/src/generalizedpartialfunctions.jl b/src/generalizedpartialfunctions.jl index 8474591..d6978ef 100644 --- a/src/generalizedpartialfunctions.jl +++ b/src/generalizedpartialfunctions.jl @@ -34,12 +34,10 @@ end end end return quote - @show pf.func - @show typeof.(($(final_args...),)) pf.func($(final_args...); pf.kwargs..., kwargs...) end else - # Construct another GeneralizedPartialFunction + return :(pf $ (args..., (; kwargs...))) end end @@ -62,13 +60,14 @@ macro ($)(expr::Expr) push!(kwargs_keys, kwarg.args[1]) push!(kwargs_values, kwarg.args[2]) end - final_kwargs = NamedTuple{Tuple(kwargs_keys)}(kwargs_values) + kwargs_keys = Tuple(kwargs_keys) non_underscore_args_pos = Tuple(setdiff(1:length(args), underscore_args_pos)) non_underscore_args = map(Base.Fix1(getindex, args), non_underscore_args_pos) stored_args = NamedTuple{Symbol.(non_underscore_args_pos)}(non_underscore_args) return :(GeneralizedPartialFunction($(esc(string(expr))), $(esc(f)), - $(esc(underscore_args_pos)), $(esc(args)), - $(esc(final_kwargs)))) + $(esc(underscore_args_pos)), + NamedTuple{$(esc(Symbol.(non_underscore_args_pos)))}(tuple($(esc.(non_underscore_args)...))), + NamedTuple{$(esc(kwargs_keys))}(tuple($(esc.(kwargs_values)...))))) end diff --git a/test/runtests.jl b/test/runtests.jl index a4a5053..714b1d0 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -49,4 +49,40 @@ end @test sort_a_by_length == sort_a_by_length_2 @test repr(sort_a_by_length) == "sort([[1, 2, 3], [1, 2]], ...; by = length, ...)" -end \ No newline at end of file +end + +@testset "Generalized Partial Functions" begin + @test map(@$(+(2, _)), [1,2,3]) == [3, 4, 5] + @test map(@$(+(_, 2)), [1,2,3]) == [3, 4, 5] + @test repr(@$(map(a, _))) == "map(a, _)" + @test (@$(map(a, _)))([1, 2, 3]) == [1, 4, 9] + + @test greet("Hello", "Bob", "!") == "Hello, Bob!" + sayhello = @$ greet("Hello", _, _) + @test repr(sayhello) == "greet(\"Hello\", _, _)" + @test repr("text/plain", sayhello) == repr(sayhello) + + @test sayhello("Bob", "!") == "Hello, Bob!" + + sayhellobob = sayhello("Bob") + @test repr(sayhellobob) == "greet(\"Hello\", _, _)(\"Bob\", ...)" + @test sayhellobob("!") == "Hello, Bob!" + + hi_bob = @$(@$(greet("Hi", _, _))("Bob", _)) + @test @$(hi_bob("!")) == "Hi, Bob!" + @test hi_bob isa PartialFunctions.GeneralizedPartialFunction + @test sayhello <| ("Jimmy", "?")... == "Hello, Jimmy?" + + @test hi_bob <| "!" == "Hi, Bob!" + + @testset "Keyword Arguments" begin + a = [[1,2,3], [1,2]] + sort_by_length = @$(sort(_; by = length)) + @test sort(a, by = length) == sort_by_length(a) + + sorted_a_by_length = @$(sort(a; by = length)) + @test sort(a, by = length) == sorted_a_by_length + + @test repr(sort_by_length) == "sort(_; by = length)" + end +end From 5e0b3e5e5d3c3b7aaf8849810bed63bcacbd97c8 Mon Sep 17 00:00:00 2001 From: Avik Pal Date: Tue, 4 Jul 2023 12:31:37 -0400 Subject: [PATCH 3/4] Compact code to just use PartialFunction struct --- README.md | 2 +- docs/src/index.md | 2 +- src/PartialFunctions.jl | 108 ++++++++++++++++++++-- src/generalizedpartialfunctions.jl | 73 --------------- test/runtests.jl | 140 +++++++++++++++-------------- 5 files changed, 172 insertions(+), 153 deletions(-) delete mode 100644 src/generalizedpartialfunctions.jl diff --git a/README.md b/README.md index df14900..a2df36c 100644 --- a/README.md +++ b/README.md @@ -124,7 +124,7 @@ false ## The `@$` Macro -`@$` allows users to create general partial functions by replacing the currently known +`@$` allows users to create general partial functions by replacing the currently unknown arguments with `_`. For example, we can implement matrix multiplication as: ```julia diff --git a/docs/src/index.md b/docs/src/index.md index 2ff3ece..bb57e51 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -134,7 +134,7 @@ false ## The `@$` Macro -`@$` allows users to create general partial functions by replacing the currently known +`@$` allows users to create general partial functions by replacing the currently unknown arguments with `_`. For example, we can implement matrix multiplication as: ```jldoctest diff --git a/src/PartialFunctions.jl b/src/PartialFunctions.jl index 560da87..68ea5ba 100644 --- a/src/PartialFunctions.jl +++ b/src/PartialFunctions.jl @@ -1,19 +1,26 @@ module PartialFunctions +using MacroTools + export $, @$ export <| include("reversedfunctions.jl") -include("generalizedpartialfunctions.jl") name = (string ∘ Symbol) -struct PartialFunction{F<:Function, T<:Tuple, N<:NamedTuple} <: Function +struct PartialFunction{KL, UL, F<:Function, T<:Tuple, N<:NamedTuple} <: Function + expr_string::String func::F args::T kwargs::N - PartialFunction(f, args::T, kwargs::N) where {T<:Tuple, N<:NamedTuple} = let - new{typeof(f), T, N}(f, args, kwargs) - end +end + +function PartialFunction(f, args::T, kwargs::N) where {T<:Tuple, N<:NamedTuple} + return PartialFunction{nothing, nothing, typeof(f), typeof(args), typeof(kwargs)}("", f, args, kwargs) +end + +function PartialFunction(known_args_locations, unknown_args_locations, expr_string::String, f, args::T, kwargs::N) where {T<:Tuple, N<:NamedTuple} + return PartialFunction{known_args_locations, unknown_args_locations, typeof(f), T, N}(expr_string, f, args, kwargs) end function PartialFunction(f::PartialFunction, newargs::Tuple, newkwargs::NamedTuple) @@ -22,13 +29,13 @@ function PartialFunction(f::PartialFunction, newargs::Tuple, newkwargs::NamedTup PartialFunction(f.func, allargs, allkwargs) end -(p::PartialFunction)(newargs...; newkwargs...) = p.func(p.args..., newargs...; p.kwargs..., newkwargs...) +(p::PartialFunction{nothing})(newargs...; newkwargs...) = p.func(p.args..., newargs...; p.kwargs..., newkwargs...) """ (\$)(f::Function, args...) Partially apply the given arguments to f. Typically used as infix `f \$ args` -The returned function is of type [`PartialFunctions.PartialFunction{typeof(f), typeof(args)}`](@ref) +The returned function is of type [`PartialFunctions.PartialFunction{nothing, nothing, typeof(f), typeof(args)}`](@ref) # Examples @@ -53,6 +60,87 @@ end end ($)(f::DataType, args) = ($)(identity∘f, args) + +@generated function (pf::PartialFunction{kloc, uloc})(args...; kwargs...) where {kloc, uloc} + L = length(args) + if L == length(uloc) + # Execute the function + final_args = [] + total_args = length(uloc) + length(kloc) + j, k = 1, 1 + for i in 1:total_args + if i ∈ uloc + push!(final_args, :(args[$j])) + j += 1 + else + push!(final_args, :(pf.args[$k])) + k += 1 + end + end + return quote + pf.func($(final_args...); pf.kwargs..., kwargs...) + end + else + return :(pf $ (args..., (; kwargs...))) + end +end + + +""" + @\$ f(args...; kwargs...) + +Partially apply the given arguments to `f`. Unknown arguments are represented by `_`. + +!!! note + If no `_` is present, the function is executed immediately. + +# Examples + +```jldoctest +julia> matmul(A, X, B; C = 1) = A * X .+ B .* C +matmul (generic function with 1 method) + +julia> A = randn(2, 2); B = rand(2, 2); X = randn(2, 2); + +julia> pf = @\$ matmul(_, X, _; C = 2) +matmul(_, X, _; C = 2) + +julia> pf(A, B) ≈ matmul(A, X, B; C = 2) +true +``` +""" +macro ($)(expr::Expr) + if !@capture(expr, f_(args__; kwargs__) | f_(args__)) + throw(ArgumentError("Only function calls are supported!")) + end + + kwargs = kwargs === nothing ? [] : kwargs + + underscore_args_pos = Tuple(findall(x -> x == :_, args)) + if length(args) == 0 || length(underscore_args_pos) == 0 + return :($(esc(expr))) + end + + kwargs_keys = Symbol[] + kwargs_values = Any[] + for kwarg in kwargs + @assert kwarg.head == :kw "Malformed keyword argument!" + push!(kwargs_keys, kwarg.args[1]) + push!(kwargs_values, kwarg.args[2]) + end + kwargs_keys = Tuple(kwargs_keys) + + non_underscore_args_pos = Tuple(setdiff(1:length(args), underscore_args_pos)) + non_underscore_args = map(Base.Fix1(getindex, args), non_underscore_args_pos) + stored_args = NamedTuple{Symbol.(non_underscore_args_pos)}(non_underscore_args) + + return :(PartialFunction($(esc(non_underscore_args_pos)), + $(esc(underscore_args_pos)), + $(esc(string(expr))), $(esc(f)), + $(esc(tuple))($(esc.(non_underscore_args)...)), + $(NamedTuple{kwargs_keys})(tuple($(esc.(kwargs_values)...))))) +end + """ <|(f, args) @@ -91,7 +179,9 @@ function Base.Symbol(pf::PartialFunction) Base.Symbol("$(func_name)$(argstring)") end -Base.show(io::IO, pf::PartialFunction) = print(io, name(pf)) -Base.show(io::IO, ::MIME"text/plain", pf::PartialFunction) = show(io, pf) +Base.show(io::IO, pf::PartialFunction{nothing}) = print(io, name(pf)) +Base.show(io::IO, ::MIME"text/plain", pf::PartialFunction{nothing}) = show(io, pf) +Base.show(io::IO, pf::PartialFunction) = print(io, pf.expr_string) +Base.show(io::IO, ::MIME"text/plain", pf::PartialFunction) = print(io, pf.expr_string) end diff --git a/src/generalizedpartialfunctions.jl b/src/generalizedpartialfunctions.jl deleted file mode 100644 index d6978ef..0000000 --- a/src/generalizedpartialfunctions.jl +++ /dev/null @@ -1,73 +0,0 @@ -using MacroTools - -struct GeneralizedPartialFunction{L, F<:Function, T<:NamedTuple, N<:NamedTuple} <: Function - expr_string::String - func::F - args::T - kwargs::N -end - -Base.show(io::IO, pf::GeneralizedPartialFunction) = print(io, pf.expr_string) -Base.show(io::IO, ::MIME"text/plain", pf::GeneralizedPartialFunction) = print(io, pf.expr_string) - -function GeneralizedPartialFunction(expr_string::String, f::F, loc::Tuple, args::T, - kwargs::N) where {F<:Function, T<:NamedTuple, - N<:NamedTuple} - return GeneralizedPartialFunction{loc, typeof(f), T, N}(expr_string, f, args, kwargs) -end - -@generated function (pf::GeneralizedPartialFunction{loc, F, T})(args...; kwargs...) where {loc, F, T} - L = length(args) - kloc = fieldnames(T) - if L == length(loc) - # Execute the function - final_args = [] - total_args = length(loc) + length(kloc) - j, k = 1, 1 - for i in 1:total_args - if i ∈ loc - push!(final_args, :(args[$j])) - j += 1 - else - push!(final_args, :(pf.args[$k])) - k += 1 - end - end - return quote - pf.func($(final_args...); pf.kwargs..., kwargs...) - end - else - return :(pf $ (args..., (; kwargs...))) - end -end - -macro ($)(expr::Expr) - if !@capture(expr, f_(args__; kwargs__) | f_(args__)) - throw(ArgumentError("Only function calls are supported!")) - end - - kwargs = kwargs === nothing ? [] : kwargs - - underscore_args_pos = Tuple(findall(x -> x == :_, args)) - if length(args) == 0 || length(underscore_args_pos) == 0 - return :($(esc(expr))) - end - - kwargs_keys = Symbol[] - kwargs_values = Any[] - for kwarg in kwargs - @assert kwarg.head == :kw "Malformed keyword argument!" - push!(kwargs_keys, kwarg.args[1]) - push!(kwargs_values, kwarg.args[2]) - end - kwargs_keys = Tuple(kwargs_keys) - - non_underscore_args_pos = Tuple(setdiff(1:length(args), underscore_args_pos)) - non_underscore_args = map(Base.Fix1(getindex, args), non_underscore_args_pos) - stored_args = NamedTuple{Symbol.(non_underscore_args_pos)}(non_underscore_args) - - return :(GeneralizedPartialFunction($(esc(string(expr))), $(esc(f)), - $(esc(underscore_args_pos)), - NamedTuple{$(esc(Symbol.(non_underscore_args_pos)))}(tuple($(esc.(non_underscore_args)...))), - NamedTuple{$(esc(kwargs_keys))}(tuple($(esc.(kwargs_values)...))))) -end diff --git a/test/runtests.jl b/test/runtests.jl index 714b1d0..9157d27 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -4,85 +4,87 @@ using Test a(x) = x^2 greet(greeting, name, punctuation) = "$(greeting), $(name)$(punctuation)" -@testset "Partial functions" begin - @test map((+)$2, [1,2,3]) == [3, 4, 5] - @test repr(map $ a) == "map(a, ...)" - @test (map $ a)([1, 2, 3]) == [1, 4, 9] - - @test greet("Hello", "Bob", "!") == "Hello, Bob!" - sayhello = greet $ "Hello" - @test repr(sayhello) == "greet(\"Hello\", ...)" - @test repr("text/plain", sayhello) == repr(sayhello) - - @test sayhello("Bob", "!") == "Hello, Bob!" - hi_bob = greet $ "Hi" $ "Bob" $ "!" - @test hi_bob isa PartialFunctions.PartialFunction{typeof(greet), Tuple{String, String, String}, NamedTuple{(), Tuple{}}} - @test hi_bob <| () == "Hi, Bob!" - @test sayhello <| ("Jimmy", "?")... == "Hello, Jimmy?" - - @test greet $ ("Hi", "Bob") <| "!" == "Hi, Bob!" -end - -@testset "Reversed functions" begin - revmap = flip(map) - @test flip(revmap) == map - @test revmap([1,2,3], sin) == map(sin, [1,2,3]) - - func(x, y) = x - y - func(x, y, z) = x - y - z - @test func(1, 2) == -1 - @test func(1, 3, 6) == -8 - flipped = flip(func) - @test flipped(2, 1) == -1 - @test flipped(3, 2, 1) == -4 -end +@testset "PartialFunctions.jl" begin + @testset "Partial functions" begin + @test map((+)$2, [1,2,3]) == [3, 4, 5] + @test repr(map $ a) == "map(a, ...)" + @test (map $ a)([1, 2, 3]) == [1, 4, 9] + + @test greet("Hello", "Bob", "!") == "Hello, Bob!" + sayhello = greet $ "Hello" + @test repr(sayhello) == "greet(\"Hello\", ...)" + @test repr("text/plain", sayhello) == repr(sayhello) + + @test sayhello("Bob", "!") == "Hello, Bob!" + hi_bob = greet $ "Hi" $ "Bob" $ "!" + @test hi_bob isa PartialFunctions.PartialFunction{nothing, nothing, typeof(greet), Tuple{String, String, String}, NamedTuple{(), Tuple{}}} + @test hi_bob <| () == "Hi, Bob!" + @test sayhello <| ("Jimmy", "?")... == "Hello, Jimmy?" + + @test greet $ ("Hi", "Bob") <| "!" == "Hi, Bob!" + end + + @testset "Reversed functions" begin + revmap = flip(map) + @test flip(revmap) == map + @test revmap([1,2,3], sin) == map(sin, [1,2,3]) + + func(x, y) = x - y + func(x, y, z) = x - y - z + @test func(1, 2) == -1 + @test func(1, 3, 6) == -8 + flipped = flip(func) + @test flipped(2, 1) == -1 + @test flipped(3, 2, 1) == -4 + end -@testset "Keyword Arguments" begin - a = [[1,2,3], [1,2]] - sort_by_length = sort $ (; by = length) - @test sort(a, by = length) == sort_by_length(a) + @testset "Keyword Arguments" begin + a = [[1,2,3], [1,2]] + sort_by_length = sort $ (; by = length) + @test sort(a, by = length) == sort_by_length(a) - sort_a_by_length = sort $ (a, (;by = length)) - @test sort(a, by = length) == sort_a_by_length() + sort_a_by_length = sort $ (a, (;by = length)) + @test sort(a, by = length) == sort_a_by_length() - sort_a_by_length_2 = sort $ ((a,), (;by = length)) - @test sort_a_by_length == sort_a_by_length_2 + sort_a_by_length_2 = sort $ ((a,), (;by = length)) + @test sort_a_by_length == sort_a_by_length_2 - @test repr(sort_a_by_length) == "sort([[1, 2, 3], [1, 2]], ...; by = length, ...)" -end + @test repr(sort_a_by_length) == "sort([[1, 2, 3], [1, 2]], ...; by = length, ...)" + end -@testset "Generalized Partial Functions" begin - @test map(@$(+(2, _)), [1,2,3]) == [3, 4, 5] - @test map(@$(+(_, 2)), [1,2,3]) == [3, 4, 5] - @test repr(@$(map(a, _))) == "map(a, _)" - @test (@$(map(a, _)))([1, 2, 3]) == [1, 4, 9] - - @test greet("Hello", "Bob", "!") == "Hello, Bob!" - sayhello = @$ greet("Hello", _, _) - @test repr(sayhello) == "greet(\"Hello\", _, _)" - @test repr("text/plain", sayhello) == repr(sayhello) + @testset "Generalized Partial Functions" begin + @test map(@$(+(2, _)), [1,2,3]) == [3, 4, 5] + @test map(@$(+(_, 2)), [1,2,3]) == [3, 4, 5] + @test repr(@$(map(a, _))) == "map(a, _)" + @test (@$(map(a, _)))([1, 2, 3]) == [1, 4, 9] + + @test greet("Hello", "Bob", "!") == "Hello, Bob!" + sayhello = @$ greet("Hello", _, _) + @test repr(sayhello) == "greet(\"Hello\", _, _)" + @test repr("text/plain", sayhello) == repr(sayhello) - @test sayhello("Bob", "!") == "Hello, Bob!" + @test sayhello("Bob", "!") == "Hello, Bob!" - sayhellobob = sayhello("Bob") - @test repr(sayhellobob) == "greet(\"Hello\", _, _)(\"Bob\", ...)" - @test sayhellobob("!") == "Hello, Bob!" + sayhellobob = sayhello("Bob") + @test repr(sayhellobob) == "greet(\"Hello\", \"Bob\", ...)" + @test sayhellobob("!") == "Hello, Bob!" - hi_bob = @$(@$(greet("Hi", _, _))("Bob", _)) - @test @$(hi_bob("!")) == "Hi, Bob!" - @test hi_bob isa PartialFunctions.GeneralizedPartialFunction - @test sayhello <| ("Jimmy", "?")... == "Hello, Jimmy?" + hi_bob = @$(@$(greet("Hi", _, _))("Bob", _)) + @test @$(hi_bob("!")) == "Hi, Bob!" + @test hi_bob isa PartialFunctions.PartialFunction + @test sayhello <| ("Jimmy", "?")... == "Hello, Jimmy?" - @test hi_bob <| "!" == "Hi, Bob!" + @test hi_bob <| "!" == "Hi, Bob!" - @testset "Keyword Arguments" begin - a = [[1,2,3], [1,2]] - sort_by_length = @$(sort(_; by = length)) - @test sort(a, by = length) == sort_by_length(a) + @testset "Keyword Arguments" begin + a = [[1,2,3], [1,2]] + sort_by_length = @$(sort(_; by = length)) + @test sort(a, by = length) == sort_by_length(a) - sorted_a_by_length = @$(sort(a; by = length)) - @test sort(a, by = length) == sorted_a_by_length + sorted_a_by_length = @$(sort(a; by = length)) + @test sort(a, by = length) == sorted_a_by_length - @test repr(sort_by_length) == "sort(_; by = length)" - end + @test repr(sort_by_length) == "sort(_; by = length)" + end + end end From 353bc47346a14001e292b654cef34f0f7604d7b1 Mon Sep 17 00:00:00 2001 From: Thomas Marks Date: Wed, 5 Jul 2023 10:40:19 -0400 Subject: [PATCH 4/4] Update Project.toml