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
FrankenTuples adhere as closely as makes sense to the API for Tuples and NamedTuples.
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
4Base.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 Symbols.
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 => 4Base.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]
3Base.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 FrankenTuples. 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 Tuples.
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
trueFrankenTuples.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