WARNING This package is all about syntax pun and interactive convenience. It is highly recommended to avoid using it inside any serious packages and scripts.
UnderscoreOh.jl is a tool for building anonymous callables. There
are several differences to the native anonymous functions and closures
created with syntax like x -> ...
and function (x) ... end
:
-
The callables created with UnderscoreOh.jl are identical when generated with the same expression and the same context. It allows
julia
to re-use JIT-compiled anonymous callables. -
Simple callables are easier to build with UnderscoreOh.jl API.
-
Variables are captured by value (i.e., no infamous
Box
ing).
UnderscoreOh.jl exports _o
. Accessing properties and keys generate
a function that does that:
julia> using UnderscoreOh
julia> f = _o.key
_o -> _o.key (generic function with 1 method)
julia> f((key = 1, value = 2))
1
julia> map(_o.key, [(key = 1,), (key = 2,), (key = 3,)])
3-element Array{Int64,1}:
1
2
3
julia> f = _o.x[1]
_o -> _o.x[1] (generic function with 1 method)
julia> f((x = (:a,), y = (:b, :c)))
:a
Broadcasting expression around this object becomes a unary function:
julia> f = _o.x .== _o.y
_o -> _o.x == _o.y (generic function with 1 method)
julia> f((x = 2, y = 2))
true
julia> f = _o .* 2 .+ 1
_o -> (_o * 2) + 1 (generic function with 1 method)
julia> f(1)
3
julia> g(args...; kwargs...) = (args, (; kwargs...))
g (generic function with 1 method)
julia> f = g.(_o; x = _o .+ 1)
_o -> g(_o; x=_o + 1) (generic function with 1 method)
julia> f(0)
((0,), (x = 1,))
julia> filter(_o.k .== 1, [(k = 1, v = :a), (k = 2, v = :b), (k = 1, v = :c)])
2-element Array{NamedTuple{(:k, :v),Tuple{Int64,Symbol}},1}:
(k = 1, v = :a)
(k = 1, v = :c)
The type of the function generated by _o
overloads the ~
operator
(in such a way that it is nothing to do with bitwise not):
julia> ~_o.x
_o -> ~(_o.x) (generic function with 1 method)
julia> _o ~ _o.x # equivalent
_o -> ~(_o.x) (generic function with 1 method)
If f
is a function generated by o_
, ~(f)
is also a function.
When invoked, this function acts like the one wrapped in ~
.
julia> _o.x((x = 1,))
1
julia> (~_o.x)((x = 1,))
1
However, it has the normal broadcasting rule so that it can be used to
mix dot-_o
syntax with actual broadcasting
julia> sum.(~_o.x, [[(x=1,), (x=2,)], [(x=3,), (x=4,), (x=5,)]])
2-element Array{Int64,1}:
3
12
Note that ~ _o .* 2
does not work since it is parsed as (~ _o) .* 2
.
It can be worked around by using the binary version _o ~ _o .* 2
where _o
on the left side of ~
is just a marker for declaring the
anonymous function boundary.
(At this point, ordinary anonymous function x -> 2x
is much simpler!
However, this form is included so that the expression can be used
without re-typing it.)
UnderscoreOh.jl also exports _nt
. This function simply converts
keyword arguments to a named tuple for normal arguments:
julia> _nt(X = 1)
(X = 1,)
When the input contains _o
, it creates a function that returns a
named tuple:
julia> _nt(a = _o.x .+ 1, b = 2)
_o -> _nt(; a=_o.x + 1, b=2) (generic function with 1 method)
julia> _nt(a = _o.x .+ 1, b = 2)((x = 1,))
(a = 2, b = 2)