Thread overview
Question about template argument matching with alias this
Jul 29, 2018
Johannes Loher
Jul 29, 2018
Alex
Jul 29, 2018
Johannes Loher
Jul 30, 2018
Alex
July 29, 2018
I have a question about template argument matching in combination with implicit conversion and alias this. Consider the following code:


interface SomeInterface
{
}

class SomeClass : SomeInterface
{
}

struct SomeStruct
{
    SomeClass someClass;
    alias someClass this;
}

template isSuperType(T, S : T)
{
    enum isSuperType = is(SomeStruct : SomeInterface);
}

void main()
{
    static assert(is(SomeStruct : SomeInterface));
    static assert(isSuperType!(SomeInterface, SomeStruct)); // why does the template not match?
}


The question is, why does the template declaration not match? Thanks for your help!
July 29, 2018
On Sunday, 29 July 2018 at 16:43:08 UTC, Johannes Loher wrote:
> I have a question about template argument matching in combination with implicit conversion and alias this. Consider the following code:
>
>
> interface SomeInterface
> {
> }
>
> class SomeClass : SomeInterface
> {
> }
>
> struct SomeStruct
> {
>     SomeClass someClass;
>     alias someClass this;
> }
>
> template isSuperType(T, S : T)
> {
>     enum isSuperType = is(SomeStruct : SomeInterface);
> }
>
> void main()
> {
>     static assert(is(SomeStruct : SomeInterface));
>     static assert(isSuperType!(SomeInterface, SomeStruct)); // why does the template not match?
> }
>
>
> The question is, why does the template declaration not match? Thanks for your help!

Do you mean something like this?

´´´
interface SomeInterface {}

class SomeClass : SomeInterface {}

struct SomeStruct
{
    SomeClass someClass;
    alias someClass this;
}

template isSuperType(T, S) if(is(S : T))
{
    enum isSuperType = is(S : T);
}

static assert(is(SomeStruct : SomeInterface));
static assert(isSuperType!(SomeInterface, SomeStruct));
void main(){}
´´´
July 29, 2018
On Sunday, 29 July 2018 at 20:51:45 UTC, Alex wrote:
> Do you mean something like this?
> [...]

Yeah, I know that it possible to implement the template like this, but that is not the point here. I would like to know why it does not work the way I described it. To me it seems very strange, that `S : T` has different semantics in `is` expressions and as template parameters.

My actual problem is the following: I would like to use the dependency injection framework poodinis [1] in conjuction with the mocking capabilities from unit-threaded [2]. My code would look something like the following:

```
import poodinis : DependencyContainer;
import unit_threaded.mock : mock;

interface SomeInterface
{
}

unittest
{
    auto myMock = mock!SomeInterface;
    alias MockedType = typeof(myMock)
    auto container = new shared DependencyContainer;

    container.register!(SomeInterface, MockedType)().existingInstance(myMock);
    /* ... */
}
```

The problem with this is that register has the signature described above, i.e. register(T, S : T)() and that the mock template from unit-threaded is actually implemented by a struct which is "alias this"ed (how do you call that...? :D) to a class which is derived from the mocked interface. This means I run exactly into the problem I described in the first post.

Now I could ask the author of poodinis to remove the restriction on the template parameters for register, but it actually perfectly makes sense to have that restriction, because we are registering a concrete type as an abstract type.

I also had a quick look at the implementation of mock, which seems to be quite complicated already. So I fear that changing this implementation to using a derived class directly is unlikely to happen.

So I am back to my question: Why do we have this strange behavior? All compiler version on run.dlang.io behave like that, so I suppose there is some reason for this...?

[0] https://github.com/mbierlee/poodinis
[1] https://github.com/atilaneves/unit-threaded
July 30, 2018
On Sunday, 29 July 2018 at 23:03:27 UTC, Johannes Loher wrote:
> Yeah, I know that it possible to implement the template like this, but that is not the point here. I would like to know why it does not work the way I described it. To me it seems very strange, that `S : T` has different semantics in `is` expressions and as template parameters.
>

Yes... I see your point now. It is like "is" itself adds some semantics to the colon operator. So, while colon specifies, that only derivatives are matched, "is" matches all things, where the interface can be extracted...

I think, this is intended:
https://dlang.org/spec/template.html#argument_deduction    p. 5
and
https://dlang.org/spec/expression.html#is_expression    p. 2

But... maybe some native D speaker could comment on this...

> Now I could ask the author of poodinis to remove the restriction on the template parameters for register, but it actually perfectly makes sense to have that restriction, because we are registering a concrete type as an abstract type.

I would say, for practical reasons not to remove the restriction, but to formulate it in a more abstract way. This seems to be with ´if(is(S : T))´ in this case... As otherwise the restriction is to use classes only... (?)

> So I am back to my question: Why do we have this strange behavior? All compiler version on run.dlang.io behave like that, so I suppose there is some reason for this...?