FrankenTuples.jl
The FrankenTuples package defines a type, FrankenTuple
, which is a creature not unlike Frankenstein's monster. It is comprised of both a Tuple
and a NamedTuple
to facilitate situations in which some but not all elements of a tuple are named, e.g. (1, 2; a=3, b=4)
, and thus acts like a cross between the two.
Type and Constructors
FrankenTuples.FrankenTuple
— TypeFrankenTuple{T<:Tuple, names, NT<:Tuple}
A FrankenTuple
contains a Tuple
of type T
and a NamedTuple
with names names
and types NT
. It acts like a cross between the two, like a partially-named tuple.
The named portion of a FrankenTuple
can be accessed using NamedTuple
, and the unnamed portion can be accessed with Tuple
.
Examples
julia> ft = FrankenTuple((1, 2), (a=1, b=2))
FrankenTuple((1, 2), (a = 1, b = 2))
julia> Tuple(ft)
(1, 2)
julia> NamedTuple(ft)
(a = 1, b = 2)
FrankenTuples.ftuple
— Functionftuple(args...; kwargs...)
Construct a FrankenTuple
from the given positional and keyword arguments.
Examples
julia> ftuple(1, 2)
FrankenTuple((1, 2), NamedTuple())
julia> ftuple(1, 2, a=3, b=4)
FrankenTuple((1, 2), (a = 3, b = 4))
FrankenTuples.@ftuple
— Macro@ftuple (x...; y...)
@ftuple (a, x=t, b, y=u)
Construct a FrankenTuple
from the given tuple expression, which can contain both positional and named elements. The tuple can be "sectioned" in the same manner as a function signature, with positional elements separated from the named elements by a semicolon, or positional and named elements can be intermixed, occurring in any order.
Examples
julia> @ftuple (1, 2; a=3, b=4)
FrankenTuple((1, 2), (a = 3, b = 4))
julia> @ftuple (1, a=3, 2, b=4)
FrankenTuple((1, 2), (a = 3, b = 4))
API
FrankenTuple
s adhere as closely as makes sense to the API for Tuple
s and NamedTuple
s.
Core.Tuple
— TypeTuple(ft::FrankenTuple)
Access the Tuple
part of a FrankenTuple
, i.e. the "plain," unnamed portion.
Core.NamedTuple
— TypeNamedTuple(ft::FrankenTuple)
Access the NamedTuple
part of a FrankenTuple
, i.e. the named portion.
Base.length
— Functionlength(ft::FrankenTuple)
Compute the number of elements in ft
.
Base.isempty
— Functionisempty(ft::FrankenTuple)
Determine whether the given FrankenTuple
is empty, i.e. has no elements.
Base.iterate
— Functioniterate(ft::FrankenTuple[, state])
Iterate over ft
. This yields the values of the unnamed section first, then the values of the named section.
Examples
julia> ft = @ftuple (1, a=3, 2, b=4)
FrankenTuple((1, 2), (a = 3, b = 4))
julia> collect(ft)
4-element Vector{Int64}:
1
2
3
4
Base.keys
— Functionkeys(ft::FrankenTuple)
Get the keys of the given FrankenTuple
, i.e. the set of valid indices into ft
. The unnamed section of ft
has 1-based integer keys and the named section is keyed by name, given as Symbol
s.
Examples
julia> keys(ftuple(1, 2; a=3, b=4))
(1, 2, :a, :b)
Base.values
— Functionvalues(ft::FrankenTuple)
Get the values of the given FrankenTuple
in iteration order. The values for the unnamed section appear before that of the named section.
Examples
julia> values(ftuple(1, 2; a=3, b=4))
(1, 2, 3, 4)
Base.pairs
— Functionpairs(ft::FrankenTuple)
Construct a Pairs
iterator that associates the keys
of ft
with its values
.
Examples
julia> collect(pairs(ftuple(1, 2; a=3, b=4)))
4-element Vector{Pair{Any, Int64}}:
1 => 1
2 => 2
:a => 3
:b => 4
Base.getindex
— Functiongetindex(ft::FrankenTuple, i)
Retrieve the value of ft
at the given index i
. When i::Integer
, this gets the value at index i
in iteration order. When i::Symbol
, this gets the value from the named section with name i
. (getproperty
can also be used for the Symbol
case.)
Examples
julia> ftuple(1, 2; a=3, b=4)[3]
3
julia> ftuple(1, 2; a=3, b=4)[:a]
3
Base.firstindex
— Functionfirstindex(ft::FrankenTuple)
Retrieve the first index of ft
, which is always 1.
Base.lastindex
— Functionlastindex(ft::FrankenTuple)
Retrieve the last index of ft
, which is equivalent to its length
.
Base.first
— Functionfirst(ft::FrankenTuple)
Get the first value in ft
in iteration order. ft
must be non-empty.
Base.tail
— FunctionBase.tail(ft::FrankenTuple)
Return the tail portion of ft
: a new FrankenTuple
with the first element of ft
removed. ft
must be non-empty.
Examples
julia> Base.tail(ftuple(a=4, b=5))
FrankenTuple((), (b = 5,))
Base.empty
— Functionempty(ft::FrankenTuple)
Construct an empty FrankenTuple
.
Base.eltype
— Functioneltype(ft::FrankenTuple)
Determine the element type of ft
. This is the immediate supertype of the elements in ft
if they are not homogeneously typed.
Examples
julia> eltype(ftuple(1, 2; a=3, b=4))
Int64
julia> eltype(ftuple(0x0, 1))
Integer
julia> eltype(ftuple(a=2.0, b=0x1))
Real
julia> eltype(ftuple())
Union{}
Additional Methods
These are some additional ways to use FrankenTuple
s. The most interesting of these is perhaps hasmethod
, which permits looking for methods that have particular keyword arguments. This is not currently possible with the generic method in Base.
Base.hasmethod
— Functionhasmethod(f, ft::Type{<:FrankenTuple})
Determine whether the function f
has a method with positional argument types matching those in the unnamed portion of ft
and with keyword arguments named in accordance with those in the named portion of ft
.
Note that the types in the named portion of ft
do not factor into determining the existence of a matching method because keyword arguments to not participate in dispatch. Similarly, calling hasmethod
with a FrankenTuple
with an empty named portion will still return true
if the positional arguments match, even if f
only has methods that accept keyword arguments. This ensures agreement with the behavior of hasmethod
on Tuple
s.
More generally, the names in the FrankenTuple
must be a subset of the keyword argument names in the matching method, except when the method accepts a variable number of keyword arguments (e.g. kwargs...
). In that case, the names in the method must be a subset of the FrankenTuple
's names.
Examples
julia> f(x::Int; y=3, z=4) = x + y + z;
julia> hasmethod(f, FrankenTuple{Tuple{Int},(:y,)})
true
julia> hasmethod(f, FrankenTuple{Tuple{Int},(:a,)}) # no keyword `a`
false
julia> g(; a, b, kwargs...) = +(a, b, kwargs...);
julia> hasmethod(g, FrankenTuple{Tuple{},(:a,:b,:c,:d)}) # g accepts arbitrarily many kwargs
true
FrankenTuples.ftcall
— Functionftcall(f::Function, ft::FrankenTuple)
Call the function f
using the unnamed portion of ft
as its positional arguments and the named portion of ft
as its keyword arguments.
Examples
julia> ftcall(mapreduce, ftuple(abs2, -, 1:4; init=0))
-30