I’m happy to announce Underscores.jl
, a small but general package for _
placeholder syntax which makes it easier to pass anonymous functions to other functions.
The package provides a single @_
macro which is helpful in writing anonymous functions succinctly without naming the arguments. Some examples:
Expression | Meaning |
---|---|
@_ map(_+1, a) |
map(x->x+1, a) |
@_ map(_+2^_, a) |
map((x)->x+2^x, a) (NOTE) |
@_ map(_2/_1, a, b) |
map((x,y)->y/x, a, b) |
@_ func(a,__,b) |
x->func(a,x,b) |
`@_ data | > map(_.f,__)` |
(Edit to add (NOTE): this changed from the original meaning of (x,y)->x+2^y
as discussed below.)
A lot of packages have done similar things before, but Underscores takes a slightly different approach: the @_
is attached to the function receiving the closure rather than delimiting the start of the closure itself.
It turns out this outer placement is really convenient because
- It reduces the need for extra parentheses:
@_ map(_+1, a)
rather thanmap(@_(_+1), a)
- It is fairly conceptually consistent to allow it to act on a whole data pipeline, which makes it rather handy for tabular data manipulation.
This package was stimulated by the excellent discussion over at https://github.com/JuliaLang/julia/pull/24990, and elsewhere.
As a more concrete example, this provides natural syntax sugar for the functional approach to data manipulation taken in SplitApplyCombine.jl
(@andyferris you may find this fun):
julia> using TypedTables, Underscores, SplitApplyCombine
julia> t = Table(name = ["Alice", "Bob", "Charlie", "Eve"],
age = [25, 42, 37, 45],
sex = ["female", "male", "male", "female"])
Table with 3 columns and 4 rows:
name age sex
┌─────────────────────
1 │ Alice 25 female
2 │ Bob 42 male
3 │ Charlie 37 male
4 │ Eve 45 female
julia> @_ t |>
filter(_.age > 27, __) |>
group(_.sex, __) |>
map(length, __)
2-element Dictionaries.HashDictionary{String,Int64}
"male" │ 2
"female" │ 1