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.