How to Detect That Parameter Is a Tuple of Two Arbitrary Types

How to detect that parameter is a tuple of two arbitrary types?

You can use Swift's baby introspection methods to get at this:

func isTuple(b: Any) -> Bool {
return reflect(b).disposition == MirrorDisposition.Tuple
}

Note that reflect is largely undocumented and may only be there as support for the playground / debugger, but as far as I know this is the only way to do this.


To achieve this you need to drill down into what reflect() gives you, which is a struct that conforms to MirrorType, which I call a reflection, for lack of a better term. You can subscript the reflection of a tuple to get reflections of the tuples members, and then get the value back out as Any. At that point you can use optional binding to safely rediscover the underlying type:

func process(value: Any) {
println("Any \(value)")
}

func process(value: String) {
println("String \(value)")
}

func processTuple(b: Any) -> Bool {
let isTuple = reflect(b).disposition == MirrorDisposition.Tuple

let r = reflect(b)
for i in 0..<r.count {
println(r[i].0) // string holding tuple part name: ".0", ".1", etc
println(r[i].1.value) // the value of that tuple part: "aa", 1.2

process(r[i].1.value) // calls process(Any)
if let val = r[i].1.value as? String {
process(val) // calls process(String)
}
}

return isTuple
}

let myString = "aa"
let myDouble = 1.2
processTuple((myString, myDouble)) //returns false

Output:

.0
aa
Any aa
String aa
.1
1.2
Any 1.2

Declaring alias types for a tuple of fixed or arbitrary size and element types

You have a typo. It should be NTuple{4, Int64}, with curly braces, not parens. Type parameters normally go inside {}.

Update:
You can do TupleNT = NTuple{N,T} where {N,T} as @MarcMush shows, but I think I prefer this syntax:

const Tuple4D{T} = NTuple{4, T}
const TupleInt{N} = NTuple{N, Int}
const TupleNT{N, T} = NTuple{N, T}

I think this is a bit more readable than NTuple{N, T} where {T, N}:

BackwardsTuple{T, N} = NTuple{N, T}
BackwardsTuple{Float32, 7} # switch parameter order
=> NTuple{7, Float32}

Pattern matching checking for type of tuple

You use : to enfore the type. Below the example. You can refer to any value using a variable and without explicitly declaring type.

Scala REPL

scala> :paste
// Entering paste mode (ctrl-D to finish)

("Java", 1) match {
case (str: String, v) => println(s"value: $v")
case _ => println("something")
}

// Exiting paste mode, now interpreting.

value: 1

How to annotate function that takes a tuple of variable length? (variadic tuple type annotation)

We can annotate variable-length homogeneous tuples using the ... literal (aka Ellipsis) like this:

def process_tuple(t: Tuple[str, ...]):
...

or for Python3.9+

def process_tuple(t: tuple[str, ...]):
...

After that, the errors should go away.

From the docs:

To specify a variable-length tuple of homogeneous type, use literal
ellipsis, e.g. Tuple[int, ...]. A plain Tuple is equivalent to
Tuple[Any, ...], and in turn to tuple.

Compare two sets of types for equality

If types in tuples are unique you could make use of inheritance to answer if all types from the first tuple are involved as a base of the helper struct. E.g. (C++11 approach):

#include <tuple>
#include <type_traits>

template <class T>
struct tag { };

template <class... Ts>
struct type_set_eq_helper: tag<Ts>... { };

template <class, class, class = void>
struct type_set_eq: std::false_type { };

template <bool...>
struct bool_pack { };

template <bool... Bs>
using my_and = std::is_same<bool_pack<Bs..., true>, bool_pack<true, Bs...>>;

template <class... Ts1, class... Ts2>
struct type_set_eq<std::tuple<Ts1...>, std::tuple<Ts2...>, typename std::enable_if< (sizeof...(Ts1) == sizeof...(Ts2)) && my_and< std::is_base_of<tag<Ts2>, type_set_eq_helper<Ts1...>>::value... >::value >::type >:
std::true_type { };

int main() {
using t1 = std::tuple<int, double>;
using t2 = std::tuple<double, int>;
using t3 = std::tuple<int, double, char>;

static_assert(type_set_eq<t1, t1>::value, "err");
static_assert(type_set_eq<t1, t2>::value, "err");
static_assert(!type_set_eq<t1, t3>::value, "err");
}

[Live demo]

Obtain a slice of a Typescript 'Parameters' tuple

Yes, you can use conditional type inference on the function type, in a way very similar to how the Parameters utility type is implemented:

type ParametersExceptFirst<F> = 
F extends (arg0: any, ...rest: infer R) => any ? R : never;

compare to

// from lib.es5.d.ts
type Parameters<T extends (...args: any) => any> =
T extends (...args: infer P) => any ? P : never;

and verify that it works:

declare function foo(x: string, y: number, z: boolean): Date;
type FooParamsExceptFirst = ParametersExceptFirst<typeof foo>;
// type FooParamsExceptFirst = [y: number, z: boolean]
declare function foo(x: string, y: number, z: boolean): Date;

Playground link to code


UPDATE: arbitrary slicing of tuples with numeric literals is possible, but not pretty and has caveats. First let's write TupleSplit<T, N> which takes a tuple T and a numeric literal type N, and splits the tuple T at index N, returning two pieces: the first piece is the first N elements of T, and the second piece is everything after that. (If N is more than the length of T then the first piece is all of T and the second piece is empty):

type TupleSplit<T, N extends number, O extends readonly any[] = readonly []> =
O['length'] extends N ? [O, T] : T extends readonly [infer F, ...infer R] ?
TupleSplit<readonly [...R], N, readonly [...O, F]> : [O, T]

This works via recursive conditional types on variadic tuples and is therefore more computationally intensive than the relatively simple ParametersExceptFirst implementation above. If you try this on long tuples (lengths more than 25 or so) you can expect to see recursion errors. If you try this on ill-behaved types like non-fixed-length tuples or unions of things, you might get weird results. It's fragile; be careful with it.

Let's verify that it works:

type Test = TupleSplit<readonly ["a", "b", "c", "d", "e"], 3>
// type Test = [readonly ["a", "b", "c"], readonly ["d", "e"]]

Looks good.


Now we can use TupleSplit<T, N> to implement TakeFirst<T, N>, returning just the first N elements of T, and SkipFirst<T, N>, which skips the first N elements of T:

type TakeFirst<T extends readonly any[], N extends number> =
TupleSplit<T, N>[0];

type SkipFirst<T extends readonly any[], N extends number> =
TupleSplit<T, N>[1];

And finally TupleSlice<T, S, E> produces the slice of tuple T from start position S to end position E (remember, slices are inclusive of the start index, and exclusive of the end index) by taking the first E elements of T and skipping the first S elements of the result:

type TupleSlice<T extends readonly any[], S extends number, E extends number> =
SkipFirst<TakeFirst<T, E>, S>

To demonstrate that this more or less represents what array slice() does, let's write a function and test it:

function slice<T extends readonly any[], S extends number, E extends number>(
arr: readonly [...T], start: S, end: E
) {
return arr.slice(start, end) as readonly any[] as TupleSlice<T, S, E>;
}

const tuple = ["a", "b", "c", "d", "e"] as const
// const tuple: readonly ["a", "b", "c", "d", "e"]

const ret0 = slice(tuple, 2, 4);
// const ret0: readonly ["c", "d"]
console.log(ret0); // ["c", "d"]

const ret1 = slice(tuple, 0, 9);
// const ret1: readonly ["a", "b", "c", "d", "e"]
console.log(ret1); // ["a", "b", "c", "d", "e"];

const ret2 = slice(tuple, 5, 3);
// const ret2: readonly []
console.log(ret2); // [];

This looks good; the returned arrays from slice() have types that accurately represent their values.

Of course, there are many caveats; if you pass negative or non-whole numbers to slice() for S and E, then TupleSlice<T, S, E> is very likely not to correspond to what actually happens with array slices: negative "from end" behavior is possibly implementable but it would be even uglier; non-whole numbers or even just number have not been tested but I expect recursion warnings and other things that go bump in the night. Be warned!

Playground link to code

Number and type of elements in System.ValueTuple

Yes you can iterate through the Items via the following for loop:

var tuple = ("First", 2, 3.ToString());
ITuple indexableTuple = (ITuple)tuple;
for (var itemIdx = 0; itemIdx < indexableTuple.Length; itemIdx++)
{
Console.WriteLine(indexableTuple[itemIdx]);
}
  • The trick is that you need to explicitly convert your ValueTuple to ITuple
  • That interface exposes Length and index operator

ITuple resides inside the System.Runtime.CompilerServices namespace.



Related Topics



Leave a reply



Submit