and
I regret making
(ex; ex)
a chained expression syntax. Not useful enough to be worth it.
and
Maybe an intermediate solution would be a macro that turns everything of the form
(1, 2; x=2, y=3)
into aFrankenTuple
in a code block
What if we required a trailing comma for that case?
Turns out that (a,; b)
parses, and has for awhile (according to this time machine):
An experiment with FrankenTuple
s:
julia> using FrankenTuples
julia> macro ft(ex) esc(ft!(ex)) end
@ft (macro with 1 method)
julia> function ft!(ex)
# FrankenTuple Construction
if ex isa Expr && ex.head == :tuple && length(ex.args) > 1 &&
ex.args[1] isa Expr && ex.args[1].head == :parameters
t = Expr(:tuple, ex.args[2:end]...)
nt = Expr(:tuple, ex.args[1])
ex.head = :call
ex.args = [:FrankenTuple, t, nt]
end
ex isa Expr && map(ft!, ex.args)
ex
end
ft! (generic function with 1 method)
Can be called like:
julia> @ft x=(1,; a=2)
FrankenTuple((1,), (a = 2,))
Better yet:
julia> pushfirst!(Base.active_repl_backend.ast_transforms, ft!);
julia> x=(1,; a=2)
FrankenTuple((1,), (a = 2,))
Wrap any other expression in @ft
and its behavior is unchanged. For example:
julia> @ft (1; a=2)
2
julia> @ft (1, 2, 3)
(1, 2, 3)
julia> @ft (a=1, b=2)
(a = 1, b = 2)
Of course, @ft
is kinda redundant after adding ft!
to our REPL AST transforms.
More FrankenTuple
fun:
julia> (1, 2; a=3, b=4)
FrankenTuple((1, 2), (a = 3, b = 4))
julia> (1, 2; )
FrankenTuple((1, 2), NamedTuple())
julia> (()...; a=3, b=4)
FrankenTuple((), (a = 3, b = 4))
julia> (()...; )
FrankenTuple()
julia> ((1,2,3)...; (a=4, b=5, c=6)...)
FrankenTuple((1, 2, 3), (a = 4, b = 5, c = 6))
Keep in mind, this is an experiment! I’m just playing around to see what’s possible.
Having to splat an empty tuple to make the FrankenTuple’s tuple an empty tuple is weird While ()
is an empty Tuple
and (;)
is an empty NamedTuple
, an edit is needed for (,;)
to parse so it can be an empty FrankenTuple
.
The FrankenTuple
’s splatting behavior is wrong. Iterating over (x...,)
should splat only the numbered elements from its tuple (right now it splats both numbered and named elements), and for named elements (;x...)
a custom Base.merge(nt::NamedTuple, ft::FrankenTuple) = merge(nt, getfield(ft, :nt))
should be implemented that splats out its named elements. Then you could write f(x...; x...)
and each part would splat out as expected
Like this:
julia> Base.iterate(x::FrankenTuple) = iterate(getfield(x, :t))
julia> Base.iterate(x::FrankenTuple, n) = iterate(getfield(x, :t), n)
julia> Base.merge(nt::NamedTuple, ft::FrankenTuple) = merge(nt, getfield(ft, :nt))
julia> f(args...; kwargs...) = (args, kwargs)
f (generic function with 1 method)
julia> f((1,)...; (a=2,)...)
((1,), Base.Pairs(:a => 2))
julia> f(x...; x...)
((1,), Base.Pairs(:a => 2))
GLHF!