From 1800f5a5484b8fe433944d80f95f11f94a825952 Mon Sep 17 00:00:00 2001 From: Peter Deffebach Date: Sun, 14 Mar 2021 13:06:11 -0400 Subject: [PATCH 1/7] add docs --- src/Chain.jl | 42 +++++++++++++++++++++++++++++++----------- test/runtests.jl | 41 ++++++++++++++++++++++++++++++++--------- 2 files changed, 63 insertions(+), 20 deletions(-) diff --git a/src/Chain.jl b/src/Chain.jl index 7cd6139..21554e0 100644 --- a/src/Chain.jl +++ b/src/Chain.jl @@ -13,9 +13,9 @@ function insertionerror(expr) error( """Can't insert a first argument into: $expr. - + First argument insertion works with expressions like these, where [Module.SubModule.] is optional: - + [Module.SubModule.]func [Module.SubModule.]func(args...) [Module.SubModule.]func(args...; kwargs...) @@ -71,7 +71,7 @@ function insert_first_arg(e::Expr, firstarg) # @macro(args...) --> @macro(firstarg, args...) elseif head == :macrocall && - (is_moduled_symbol(args[1]) || args[1] isa Symbol) && + (is_moduled_symbol(args[1]) || args[1] isa Symbol) && args[2] isa LineNumberNode if args[1] == Symbol("@__dot__") @@ -108,10 +108,6 @@ end rewrite(l::LineNumberNode, replacement) = (l, replacement) function rewrite_chain_block(firstpart, block) - if !(block isa Expr && block.head == :block) - error("Second argument of @chain must be a begin / end block") - end - block_expressions = block.args # empty chain returns firstpart @@ -159,11 +155,35 @@ macro chain(initial_value, block::Expr) rewrite_chain_block(initial_value, block) end -function rewrite_chain_block(block) - if !(block isa Expr && block.head == :block) - error("Only argument of single-argument @chain must be a begin / end block") - end +""" + @chain(initial_value, args...) + +Rewrites a series of argments, either expressions or symbols, to feed the result +of each line into the next one. The initial value is given by the first argument. + +In all arguments, underscores are replaced by the argument's result. +If there are no underscores and the argument is a symbol, the symbol is rewritten +to a function call with the previous result as the only argument. +If there are no underscores and the argument is a function call or a macrocall, +the call has the previous result prepended as the first argument. +When using this form of `@chain`, without a `begin` block, you cannot use infix +operators, binary operators, or anonymous functions. For example, `@chain 1 (_ + 2)` +will fail. + +Example: + +``` +x = @chain [1, 2, 3] filter(!=(2), _) sqrt.(_) sum + +x == sum(sqrt.(filter(!=(2), [1, 2, 3]))) +``` +""" +macro chain(initial_value, args...) + rewrite_chain_block(initial_value, Expr(:block, args...)) +end + +function rewrite_chain_block(block) block_expressions = block.args isempty(block_expressions) && error("No expressions found in chain block.") diff --git a/test/runtests.jl b/test/runtests.jl index 7f44162..5c59f5c 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -58,15 +58,38 @@ end @test y == sqrt(sum(x) + 3 - 7) end +@testset "no begin" begin + x = [1, 2, 3] + y = @chain x sum + @test y == 6 + + f() = 1 + y = @chain f() only + @test y == 1 + + y = @chain x sum(_) max(0, _) first + @test y == 6 +end + @testset "invalid invocations" begin - # just one argument + # no begin with infix @test_throws LoadError eval(quote - @chain [1, 2, 3] + @chain 1 +(1) + end) + + # no begin with anonymous function + @test_throws LoadError eval(quote + @chain 1 (t -> t + 1)() end) - # no begin block + # no begin with sum @test_throws LoadError eval(quote - @chain [1, 2, 3] sum + @chain 1 (_ + 1) + end) + + # just one argument + @test_throws LoadError eval(quote + @chain [1, 2, 3] end) # let block @@ -232,7 +255,7 @@ end # issue 13 @testset "broadcasting calls" begin - + xs = [1, 2, 3] ys = @chain xs begin sin.() @@ -275,10 +298,10 @@ module LocalModule macro sin(exp) :(sin($(esc(exp)))) end - + macro broadcastminus(exp1, exp2) :(broadcast(-, $(esc(exp1)), $(esc(exp2)))) - end + end module SubModule function square(xs) @@ -294,10 +317,10 @@ module LocalModule macro sin(exp) :(sin($(esc(exp)))) end - + macro broadcastminus(exp1, exp2) :(broadcast(-, $(esc(exp1)), $(esc(exp2)))) - end + end end end From 1aa48760ac37b90e3b6c5d9c514866729752bf1e Mon Sep 17 00:00:00 2001 From: Peter Deffebach Date: Sun, 14 Mar 2021 13:17:16 -0400 Subject: [PATCH 2/7] only to first --- test/runtests.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/runtests.jl b/test/runtests.jl index 5c59f5c..2907ad8 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -64,7 +64,7 @@ end @test y == 6 f() = 1 - y = @chain f() only + y = @chain f() first @test y == 1 y = @chain x sum(_) max(0, _) first From 14be5afa743ff781d0cf6f92fb3647700d4a3ba0 Mon Sep 17 00:00:00 2001 From: Peter Deffebach Date: Sun, 14 Mar 2021 13:59:26 -0400 Subject: [PATCH 3/7] fix one line --- src/Chain.jl | 3 +++ test/runtests.jl | 31 +++++++++++++++++-------------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/Chain.jl b/src/Chain.jl index 21554e0..5a75b47 100644 --- a/src/Chain.jl +++ b/src/Chain.jl @@ -152,6 +152,9 @@ x == sum(sqrt.(filter(!=(2), [1, 2, 3]))) ``` """ macro chain(initial_value, block::Expr) + if !(block.head == :block) + block = Expr(:block, block) + end rewrite_chain_block(initial_value, block) end diff --git a/test/runtests.jl b/test/runtests.jl index 2907ad8..2bbbfcb 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -69,24 +69,27 @@ end y = @chain x sum(_) max(0, _) first @test y == 6 -end -@testset "invalid invocations" begin - # no begin with infix - @test_throws LoadError eval(quote - @chain 1 +(1) - end) + y = @chain 1 (t -> t + 1)() + @test y == 2 - # no begin with anonymous function - @test_throws LoadError eval(quote - @chain 1 (t -> t + 1)() - end) + y = @chain 1 (t -> t + 1)() first max(0, _) + @test y == 2 - # no begin with sum - @test_throws LoadError eval(quote - @chain 1 (_ + 1) - end) + y = @chain 1 (==(2)) + @test y == false + + y = @chain 1 (==(2)) first (==(false)) + @test y == 2 + + y = @chain 1 (_ + 1) + @test y == 2 + y = @chain 1 (_ + 1) first max(0, _) + @test y == 2 +end + +@testset "invalid invocations" begin # just one argument @test_throws LoadError eval(quote @chain [1, 2, 3] From 2c39913e5d7789ef5c894a30ba0979af2a98e6cf Mon Sep 17 00:00:00 2001 From: Peter Deffebach Date: Sun, 14 Mar 2021 14:04:17 -0400 Subject: [PATCH 4/7] fix test --- test/runtests.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/runtests.jl b/test/runtests.jl index 2bbbfcb..24ae24b 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -80,7 +80,7 @@ end @test y == false y = @chain 1 (==(2)) first (==(false)) - @test y == 2 + @test y == true y = @chain 1 (_ + 1) @test y == 2 From 7f5ea7bb0143911c81f1a4596f02c9ebfeb71157 Mon Sep 17 00:00:00 2001 From: Peter Deffebach Date: Sun, 14 Mar 2021 14:10:23 -0400 Subject: [PATCH 5/7] remove warning in docstring --- src/Chain.jl | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Chain.jl b/src/Chain.jl index 5a75b47..5972a50 100644 --- a/src/Chain.jl +++ b/src/Chain.jl @@ -170,10 +170,6 @@ to a function call with the previous result as the only argument. If there are no underscores and the argument is a function call or a macrocall, the call has the previous result prepended as the first argument. -When using this form of `@chain`, without a `begin` block, you cannot use infix -operators, binary operators, or anonymous functions. For example, `@chain 1 (_ + 2)` -will fail. - Example: ``` From 7d22603542bbfd21f804cad176c40b9a68cde7cb Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Fri, 19 Mar 2021 09:37:54 +0100 Subject: [PATCH 6/7] add double begin block test --- test/runtests.jl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/runtests.jl b/test/runtests.jl index 24ae24b..7db3787 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -87,6 +87,14 @@ end y = @chain 1 (_ + 1) first max(0, _) @test y == 2 + + # the begin block will be different from the normal chain block here + # only the last statement matters + y = @chain x begin + _ .+ 1 + _ .+ 2 + end sum + @test y == sum(x .+ 2) end @testset "invalid invocations" begin From 1daa6640f8f34409871ff50f39391a5fc8aeee6f Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Fri, 19 Mar 2021 09:47:26 +0100 Subject: [PATCH 7/7] add docs --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index ddf1c26..e7c470e 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,15 @@ a_long_result_variable_name = @chain begin end ``` +## One-liner syntax + +You can also use `@chain` as a one-liner, where no begin-end block is necessary. +This works well for short sequences that are still easy to parse visually without being on separate lines. + +```julia +@chain 1:10 filter(isodd, _) sum sqrt +``` + ## The `@aside` macro For debugging, it's often useful to look at values in the middle of a pipeline.