From c34a5c81fc622ba732767ba561961178bca712ee Mon Sep 17 00:00:00 2001 From: Chris Foster Date: Fri, 27 Mar 2020 14:18:03 +1000 Subject: [PATCH 1/2] Allow __ to stand for the argument which accepts a do block --- src/Underscores.jl | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/Underscores.jl b/src/Underscores.jl index c4c3c6f..7d9ae2c 100644 --- a/src/Underscores.jl +++ b/src/Underscores.jl @@ -49,7 +49,7 @@ function add_closures(ex, prefix, pattern) end end if plain_nargs && numbered_nargs > 0 - throw(ArgumentError("Cannot mix plain and numbered `$prefix` placeholders in `$ex`")) + throw(ArgumentError("`@_` expansion: Cannot mix plain and numbered `$prefix` placeholders in `$ex`")) end nargs = max(plain_nargs, numbered_nargs) if nargs == 0 @@ -74,9 +74,33 @@ function lower_underscores(ex) return ex elseif ex.head == :call && length(ex.args) > 1 && ex.args[1] in _pipeline_ops - # Special case for pipelining and composition operators + # Pipelining and composition delimits "outer" expression return Expr(ex.head, ex.args[1], map(lower_underscores, ex.args[2:end])...) + elseif ex.head == :do + # don't expand the RHS - it's already a closure. + rhs = ex.args[2] + # `do` syntax delimits an "outer" expression; expand inside the + # left hand side. + lhs = lower_underscores(ex.args[1]) + if lhs isa Expr && lhs.head == :call + # lhs had no __ placeholders - lower as a normal do block. + Expr(ex.head, lhs, rhs) + else + # lhs has a placeholder and it's now a closure. We could use it + # directly with a do block, but lets avoid one extra closure + # here by lowering to a let block instead. + @assert lhs isa Expr && lhs.head == :-> + lhsargs = lhs.args[1].args + if length(lhsargs) != 1 + throw(ArgumentError("`@_` expansion: multiple `__` placeholders `$((lhsargs...,))` make no sense in `do` block form")) + end + quote + let $(lhsargs[1]) = $rhs + $(lhs.args[2]) + end + end + end elseif ex.head == :. && length(ex.args) == 2 && ex.args[2] isa Expr && ex.args[2].head == :tuple # Broadcast calls treated as normal calls for underscore lowering From 34403ad8aaa3700532a7fdab9436ca8618af5822 Mon Sep 17 00:00:00 2001 From: Chris Foster Date: Mon, 6 Apr 2020 21:30:40 +1000 Subject: [PATCH 2/2] WIP - docs --- docs/src/index.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/src/index.md b/docs/src/index.md index 05e797a..2c71668 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -54,6 +54,21 @@ julia> @_ map("X $_2 $(repeat(_1,_2))", ["a","b","c"], [1,2,3]) "X 3 ccc" ``` +### do blocks and double underscores + +The double underscore placeholder `__` can be used to pass the closure created +by a `do` block to some function argument other than the first. Say we had a +complex binary operator in a map-reduction. With a do block this could look +like: + +```jldoctest +@_ mapreduce(extrema, __, [[1,2],[3,4]]) do (min1,max1), (min2,max2) + +end +``` + +"escapes an extra level", so `@_ f(__)` means + ### Tabular data `@_` is handy for manipulating tabular data. Let's filter a list of named