Overloads not returning appropriate info. [Field reflunkory]
Adam D. Ruppe
destructionator at gmail.com
Wed Apr 10 14:06:53 UTC 2019
On Wednesday, 10 April 2019 at 10:18:35 UTC, H. S. Teoh wrote:
> The functionality rocks, but the syntax is a horrendous
> hairball of inconsistencies and design-by-chance.
There's a little bit weird about it, but it makes sense.
I used to find it hard to even remember how it works, but then I
had to describe it for the book and it all came together.
Consider this.
Declaring a normal local variable:
---
int foo = 0;
---
We can generalize that to
---
Type_Name Local_Name Operator Initial_Value
---
Well, now, compare to the is expression:
---
is(typeof(func) Params == __parameters)
---
It is a bit of a stretch, but the general shape is the same. We
can define the is expression generally to be:
is(Type_Name Local_Name Deconstruction_Pattern)
And then both Local_Name and Deconstruction_Pattern are optional.
This gives us all the forms from the website
https://dlang.org/spec/expression.html#IsExpression (from point
#5 there)
1: is(Type). This has the type, but left out the local name and
deconstruction pattern. All it cares about is if the type exists.
2: is(Type : something). This deconstruction pattern, using :,
means "can implicitly convert to something"
The deconstruction pattern mimics common ways of writing such
types; class A : B {} defines a type A that can implicitly cast
to B, so is(A : B) would pass and return true.
3: is(Type == something). This deconstruction pattern, using ==,
just needs an exact match. It is probably the easiest one for
people to remember.
4: is(Type Identifier). This skips the deconstruction pattern,
meaning it is the most basic "this type must exist" check, but
includes the local name.
Like with `int a;`, the name comes after the type.
5: is(Type Identifier : something). This is just #2 again, but
including the optional local name.
6: is(Type Identifier == something). Just #3 including the
optional local name.
At this point, the documentation also includes other
deconstruction patterns, like the `class` keyword, etc. These are
mostly very simple, but it is slightly magical in places because
it can initialize Identifier to other thing.. but is that really
weird?
int a = 10;
The value of a is dependent on the right side, and so is the
Identifier here.
It looks totally wrong because it is using == here rather than =,
so we intuitively think
is(A B == C)
is comparing B and C... but just rewrite in your brain that this
is actually declaring a variable with a funky deconstruction
initializer and it will make sense again.
So the whole `== C` part is the deconstruction pattern, and B is
the variable being set to it. And being a deconstruction pattern,
it is trying to pull layers off, so stuff like
is(MyClass Parents == super),
the ==super pattern is deconstructing its super keyword; its base
classes.
Think of it not as comparison, but as matching a deconstruction
pattern and it will make more sense.
I know its weird, but we can work with it. Let's move on:
7: This one has a lot of examples, but it really just expands on
the deconstruction pattern. For example:
static if(is(int[10] == T[N], T, size_t N))
What's the pattern here?
== means use exact comparison, not implicitly converting
comparison.
T[N] writes out a model of what the declaration is expected to
look like.
Then commas separate the definitions of each placeholder
variable, just as if they were template argument definitions.
Same syntax, different location.
The one tricky thing is the compiler will not tell you when you
malformed the pattern (for the most part), just nothing will
happen to match it. So match it on some known quantity to test
via static assert or something.
To use a complex pattern with the optional name:
static if(is(int[10] OriginalType == T[N], T, size_t N))
that's all the pieces together. Not so bad when you know how it
breaks down.
More information about the Digitalmars-d-learn
mailing list