digitalmars.D.learn - Comparing two AliasSeq

Yuxuan Shui <yshuiv7 gmail.com> writes:
```In this example:

import std.range;
template expandRange(alias R) if (isInputRange!(typeof(R))) {
static if (R.empty)
alias expandRange = AliasSeq!();
else
alias expandRange = AliasSeq!(R.front(),
expandRange!(R.drop(1)));
}

///
unittest {
import std.range;
static assert (is(expandRange!(iota(0,5)):
AliasSeq!(0,1,2,3,4)));
}

The static assert fails, why?
```
Mar 24 2017
"H. S. Teoh via Digitalmars-d-learn" <digitalmars-d-learn puremagic.com> writes:
```On Sat, Mar 25, 2017 at 03:25:27AM +0000, Yuxuan Shui via Digitalmars-d-learn
wrote:
In this example:

import std.range;
template expandRange(alias R) if (isInputRange!(typeof(R))) {
static if (R.empty)
alias expandRange = AliasSeq!();
else
alias expandRange = AliasSeq!(R.front(), expandRange!(R.drop(1)));
}

///
unittest {
import std.range;
static assert (is(expandRange!(iota(0,5)): AliasSeq!(0,1,2,3,4)));
}

The static assert fails, why?

Firstly, is(T : U) expects T and U to be types, and expandRange!(...) is
not a type (or a list of types), it is a list of values.

Second, is(T : U) means "does the type T implicitly convert to the type
U?". It checks for implicit convertibility between types, it does not
compare values.

T

--
The right half of the brain controls the left half of the body. This means that
only left-handed people are in their right mind. -- Manoj Srivastava
```
Mar 24 2017
Jonathan M Davis via Digitalmars-d-learn writes:
```On Saturday, March 25, 2017 03:25:27 Yuxuan Shui via Digitalmars-d-learn
wrote:
In this example:

import std.range;
template expandRange(alias R) if (isInputRange!(typeof(R))) {
static if (R.empty)
alias expandRange = AliasSeq!();
else
alias expandRange = AliasSeq!(R.front(),
expandRange!(R.drop(1)));
}

///
unittest {
import std.range;
static assert (is(expandRange!(iota(0,5)):
AliasSeq!(0,1,2,3,4)));
}

The static assert fails, why?

Well, is expressions normally compare types, not values, and
AliasSeq!(0, 1, 2, 3, 4), isn't a type and doesn't contain types.

static assert(is(AliasSeq!int == AliasSeq!int));

passes, whereas

static assert(is(AliasSeq!0 == AliasSeq!0));

does not. So, I expect that the issue is that you're dealing with values
rather than types. You're also using : instead of ==, and : _definitely_ is
for types (since it checks for implicit conversion, not equality), so it
wouldn't have entirely surprised me if == worked when : didn't, but ==
doesn't either.

What you proobably should do is either convert the AliasSeq's to dynamic
arrays or ranges - e.g. [AliasSeq!(0, 1, 2, 3, 4)] or
only(AliasSeq!(0, 1, 2, 3, 4)) - though in both cases, that really only
makes sense when you already have an AliasSeq, since [] and only will take
the values directly.

- Jonathan M Davis
```
Mar 24 2017
Yuxuan Shui <yshuiv7 gmail.com> writes:
```On Saturday, 25 March 2017 at 04:23:31 UTC, Jonathan M Davis
wrote:
On Saturday, March 25, 2017 03:25:27 Yuxuan Shui via
Digitalmars-d-learn wrote:
In this example:

import std.range;
template expandRange(alias R) if
(isInputRange!(typeof(R))) {
static if (R.empty)
alias expandRange = AliasSeq!();
else
alias expandRange = AliasSeq!(R.front(),
expandRange!(R.drop(1)));
}

///
unittest {
import std.range;
static assert (is(expandRange!(iota(0,5)):
AliasSeq!(0,1,2,3,4)));
}

The static assert fails, why?

Well, is expressions normally compare types, not values, and
AliasSeq!(0, 1, 2, 3, 4), isn't a type and doesn't contain
types.

static assert(is(AliasSeq!int == AliasSeq!int));

passes, whereas

static assert(is(AliasSeq!0 == AliasSeq!0));

does not. So, I expect that the issue is that you're dealing
with values rather than types. You're also using : instead of
==, and : _definitely_ is for types (since it checks for
implicit conversion, not equality), so it wouldn't have
entirely surprised me if == worked when : didn't, but ==
doesn't either.

What you proobably should do is either convert the AliasSeq's
to dynamic
arrays or ranges - e.g. [AliasSeq!(0, 1, 2, 3, 4)] or
only(AliasSeq!(0, 1, 2, 3, 4)) - though in both cases, that
really only
makes sense when you already have an AliasSeq, since [] and
only will take
the values directly.

- Jonathan M Davis

I see. I always thought tuple() is a type...

So a tuple of types is a type, but a tuple of mixed types and
values is not a type. Doesn't seem very consistent.

Here is the solution I will go with:

struct test(T...) { }
import std.range;
static assert (is(test!(expandRange!(iota(0,5))) == test!(0, 1,
2, 3, 4)));
```
Mar 24 2017
Jonathan M Davis via Digitalmars-d-learn writes:
```On Saturday, March 25, 2017 04:57:26 Yuxuan Shui via Digitalmars-d-learn
wrote:
I see. I always thought tuple() is a type...

So a tuple of types is a type, but a tuple of mixed types and
values is not a type. Doesn't seem very consistent.

An AliasSeq isn't really ever a type. AliasSeq!(int, float) is a list of
types, not a type itself, and is expressions supports comparing those in at
least some instances, because is expressions operate on types, and having
them support a list of types is useful. Calling AliasSeq!(int, float) a type
would be like claiming that the (int, float) in foo!(int, float) a type.
It's a list - a list of template arguments in this case - but it's still a
list and not itself a type.

An AliaSeq (or tuple as the compiler incorrectly calls it) is essentially a
list of symbols or expressions - or aliases - which is why TypeTuple got
renamed to AliasSeq; it's not really a tuple (since unlike a tuple, it
always expands and is not nestable), and it doesn't hold just types. Also,
it's used for template parameters, template arguments, function parameters,
and function arguments, so you can't really call it a list of parameters or
arguments. So, so it's sort of a list or sequence of aliases, that's what it
got changed to - AliasSeq. But that's still not a great name for it. It can
just hold too many different things and be used in too many different ways.
Essentially though, an AliasSeq is a list of template/function
parameters/arguments that happens to be separated from any templates or
functions at the moment.

In any case, since is expressions are entirely meant for operating on types,
having them not work with an AliaSeq that includes values makes sense. In
general, mixing types and values in a single AliasSeq isn't a great idea -
(though sometimes that's exactly what's required for template arguments).
So, code that mixes them is not the norm.

Here is the solution I will go with:

struct test(T...) { }
import std.range;
static assert (is(test!(expandRange!(iota(0,5))) == test!(0, 1,
2, 3, 4)));

Why are you using is expressons at all? Why are you creating a type? You're
dealing purely with values here. As such, why aren't you just using arrays
or ranges? At least with your example here, using an AliasSeq is just
complicating life. Dynamic arrays and ranges can be used at compile time via
CTFE and are a lot more straightforward to use.

- Jonathan M Davis
```
Mar 24 2017
Yuxuan Shui <yshuiv7 gmail.com> writes:
```On Saturday, 25 March 2017 at 05:20:44 UTC, Jonathan M Davis
wrote:
On Saturday, March 25, 2017 04:57:26 Yuxuan Shui via
Digitalmars-d-learn wrote:
[...]

An AliasSeq isn't really ever a type. AliasSeq!(int, float) is
a list of types, not a type itself, and is expressions supports
comparing those in at least some instances, because is
expressions operate on types, and having them support a list of
types is useful. Calling AliasSeq!(int, float) a type would be
like claiming that the (int, float) in foo!(int, float) a type.
It's a list - a list of template arguments in this case - but
it's still a list and not itself a type.

[...]

Because I want to make use of the "static foreach unrolling"
feature (I don't know what's the official name).

[...]

```
Mar 24 2017