December 03, 2019
On Tuesday, 3 December 2019 at 14:45:28 UTC, Ola Fosheim Grøstad wrote:
> Maybe also test with D interfaces? I suppose they have the same behaviour as superclasses?

Yeah, I did that (there's a unittest in the blog source for it, that's what shows under the example section). It works nicely.

I just assumed a null contract in the parent was the same as in(true) and it isn't so ugh.

> Anyway, I think most programmers struggle with covariant/contravariant parameters... I certainly have to stop up and ponder for a while to figure out the semantics when hitting such. So I tend to avoid it... :-P :-)

Maybe, but it is pretty sound once you get to know it.
December 03, 2019
On Tuesday, 3 December 2019 at 14:51:58 UTC, Adam D. Ruppe wrote:
> Maybe, but it is pretty sound once you get to know it.

You mean contracts? Yes. But I was thinking of contravariant/covariant parameters on virtual functions. Doesn't work with overloading though, so D only has it on return types? Probably for the best. Perhaps.

December 03, 2019
On Tuesday, 3 December 2019 at 15:12:46 UTC, Ola Fosheim Grøstad wrote:
> But I was thinking of contravariant/covariant parameters on virtual functions.

those too. I mostly use it with things like a clone method:

interface Cloneable {
    Cloneable clone();
}

class MyClass : Cloneable {
    override MyClass clone() { return new Myclass(); }
}


That is fairly intuitive for return values - MyClass is an instance of the interface so of course you should be able to return it! It just lets you specialize.


On the parameters side it is a little more confusing, but it still makes sense:

abstract class Generic {
    void generic(Generic g);
}

class Specialized : Generic {
    override void generic(Specialized g) {}
}


There you can see the obvious problem:

Generic g = new Specialized();
Generic other = new SomethingElse();
g.generic(other); // uh oh, interface allows it but specialization doesn't



But that's also why you can loosen.

class Specialized : Generic {
    override void generic(Object g) {}
}

since Generic implicitly casts back to Object, it is clear anything from the interface can also go to the child, so you're fine.

And if you do call the `super.generic`, you get the explicit cast requirement which is OK, since it happens in the implementation's body.

And then contracts follow the same rules...


It does make sense if you think about it, just it is weird if you don't.
December 03, 2019
On Tuesday, 3 December 2019 at 15:12:46 UTC, Ola Fosheim Grøstad wrote:
> On Tuesday, 3 December 2019 at 14:51:58 UTC, Adam D. Ruppe wrote:
>> Maybe, but it is pretty sound once you get to know it.
>
> You mean contracts? Yes. But I was thinking of contravariant/covariant parameters on virtual functions. Doesn't work with overloading though, so D only has it on return types? Probably for the best. Perhaps.

Also the use of the term "covariant" here is confusing to me:

https://dlang.org/spec/function.html

My understanding is that covariant means that an enclosing type and the enclosed type have the same typing-relationship when specialized. Whereas contravariant means they are opposite. So you need two types for it to make sense. I.e. the schematic here:

https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)#Inheritance_in_object-oriented_languages

But it the documentation "covariant" is used to describe a simple subtyping-relationship on one type?

Anyway, the wording is confusing. I think it would be hard on newbies, even if it was correctly used.

December 03, 2019
On Tuesday, 3 December 2019 at 15:26:10 UTC, Ola Fosheim Grøstad wrote:
> Also the use of the term "covariant" here is confusing to me:

Yeah, to be honest, I forget what the terms mean, so I tend to avoid them. like my last email just talked about specialization and implicit casts which I think we all understand better anyway :)
December 03, 2019
On Tuesday, 3 December 2019 at 15:25:19 UTC, Adam D. Ruppe wrote:
> On the parameters side it is a little more confusing, but it still makes sense:
>
> abstract class Generic {
>     void generic(Generic g);
> }
>
> class Specialized : Generic {
>     override void generic(Specialized g) {}
> }

But this doesn't work in D.


> There you can see the obvious problem:
>
> Generic g = new Specialized();
> Generic other = new SomethingElse();
> g.generic(other); // uh oh, interface allows it but specialization doesn't

It could have resolved it by calling the Generic.generic(...) method then the Specialized.generic could have tested it... but it gets messy.

Makes more sense for a dynamic language, I guess.

> class Specialized : Generic {
>     override void generic(Object g) {}
> }
>
> since Generic implicitly casts back to Object, it is clear anything from the interface can also go to the child, so you're fine.

Yes, but when do you need to do it? So it is typesafe, but what would the use case be?

> It does make sense if you think about it, just it is weird if you don't.

Sure, but it seems to me that something is missing when it comes to virtual function parameters. Then again, I think overriding the implementation of the subclass is kinda ugly. Some languages (at least one),  force you to call the super class virtual method as a wrapper around the subclass virtual method specialization. So basically the superclass gets to "look at" and act on the parameters before the subclass does.

I've got a feeling that one could do something interesting with such semantics that would make all this variance-stuff cleaner... somehow. But I haven't given it a lot of thought. Just a feeling. :)

December 03, 2019
On Tuesday, 3 December 2019 at 16:03:18 UTC, Ola Fosheim Grøstad wrote:
> comes to virtual function parameters. Then again, I think overriding the implementation of the subclass is kinda ugly.

That made no sense, I meant superclass...

December 03, 2019
On Monday, 2 December 2019 at 20:30:49 UTC, Adam D. Ruppe wrote:
> In short use `in(false)` when you `override` a function to inherit the contract, unless you explicitly want to expand the input - which you shouldn't do when implementing an interface!
>
> Wrote about it in more details here:
>
> http://dpldocs.info/this-week-in-d/Blog.Posted_2019_12_02.html
>
> i think this is a pretty cool little discovery, thanks too for the folks on irc for chatting it through.
>
> destroy if i missed anything lol

I thought this was a defect that was fixed a long time ago, where if the overriding function has no contract, it is implicitly given a "in (true)" contract, causing the contract of the overridden function to not be run. Am I mistaken as to what the defect was, or as to whether it was fixed, or both?
December 03, 2019
On Tuesday, 3 December 2019 at 17:10:04 UTC, Meta wrote:
> On Monday, 2 December 2019 at 20:30:49 UTC, Adam D. Ruppe wrote:
>> In short use `in(false)` when you `override` a function to inherit the contract, unless you explicitly want to expand the input - which you shouldn't do when implementing an interface!
>>
>> Wrote about it in more details here:
>>
>> http://dpldocs.info/this-week-in-d/Blog.Posted_2019_12_02.html
>>
>> i think this is a pretty cool little discovery, thanks too for the folks on irc for chatting it through.
>>
>> destroy if i missed anything lol
>
> I thought this was a defect that was fixed a long time ago, where if the overriding function has no contract, it is implicitly given a "in (true)" contract, causing the contract of the overridden function to not be run. Am I mistaken as to what the defect was, or as to whether it was fixed, or both?

I think this is the defect in question:
https://issues.dlang.org/show_bug.cgi?id=6856

I see a PR in comment 35: https://github.com/dlang/dmd/pull/4200

But it was closed. However, Iain Buclaw created a successor to 4200:
https://github.com/dlang/dmd/pull/7510

Which is still open, but Iain ran into stack corruption issues when compiling with the -m64 flag... and no further progress. So I guess it's just a matter of the bug not being fixed.
December 03, 2019
On Tuesday, 3 December 2019 at 17:10:04 UTC, Meta wrote:
> Am I mistaken as to what the defect was, or as to whether it was fixed, or both?

I don't think they ever changed it - the code in the original link there shows the current behavior.