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.FrankenTupleType
FrankenTuple{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)
source
FrankenTuples.ftupleFunction
ftuple(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))
source
FrankenTuples.@ftupleMacro
@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))
source

API

FrankenTuples adhere as closely as makes sense to the API for Tuples and NamedTuples.

Core.TupleType
Tuple(ft::FrankenTuple)

Access the Tuple part of a FrankenTuple, i.e. the "plain," unnamed portion.

source
Core.NamedTupleType
NamedTuple(ft::FrankenTuple)

Access the NamedTuple part of a FrankenTuple, i.e. the named portion.

source
Base.lengthFunction
length(ft::FrankenTuple)

Compute the number of elements in ft.

source
Base.isemptyFunction
isempty(ft::FrankenTuple)

Determine whether the given FrankenTuple is empty, i.e. has no elements.

source
Base.iterateFunction
iterate(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
source
Base.keysFunction
keys(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)
source
Base.valuesFunction
values(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)
source
Base.pairsFunction
pairs(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
source
Base.getindexFunction
getindex(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
source
Base.firstindexFunction
firstindex(ft::FrankenTuple)

Retrieve the first index of ft, which is always 1.

source
Base.lastindexFunction
lastindex(ft::FrankenTuple)

Retrieve the last index of ft, which is equivalent to its length.

source
Base.firstFunction
first(ft::FrankenTuple)

Get the first value in ft in iteration order. ft must be non-empty.

source
Base.tailFunction
Base.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,))
source
Base.emptyFunction
empty(ft::FrankenTuple)

Construct an empty FrankenTuple.

source
Base.eltypeFunction
eltype(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{}
source

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.hasmethodFunction
hasmethod(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
true
source
FrankenTuples.ftcallFunction
ftcall(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
source