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