Jump to page: 1 2
Thread overview
discrimination of constructors with same number of parameters
Dec 30, 2010
spir
Dec 30, 2010
sybrandy
Dec 30, 2010
Lutger Blijdestijn
Dec 30, 2010
sybrandy
Dec 30, 2010
spir
Dec 30, 2010
bearophile
Dec 30, 2010
spir
Dec 30, 2010
Guilherme Vieira
Dec 30, 2010
Lutger Blijdestijn
Dec 30, 2010
Jérôme M. Berger
Dec 30, 2010
spir
Dec 30, 2010
Guilherme Vieira
Dec 30, 2010
spir
Dec 30, 2010
spir
December 30, 2010
Hello,


When 2 constructors (*) accept the same number of parameters, the only remaining discrimination is type. Right? But some language types (or machine types) can have very diverse _human_ semantics, and thus be used for various purposes which should, but cannot, be considered different:
	this (int[] data, string filename) {...}
	this (int[] data, string message) {...}
Aliasing like in
	alias string Name;
does not help since for D Name is still string.

I know about typedef, but it is not even mentionned in TDPL, so I guess it is on the deprecation path. (Am I right?) So, what is the solution for this? (I added a 3rd fake bool parameter in one case)

Things get more complicated with unsigned integers: they can be used as ordinals (index, which one), as cardinals (count, how many), as any of the char types. These are completely different semantics for the "modeller" (the programmer), but for the language (thus for the machine) they are the same semantics.

Things get worse with template parameterisation, a case I lately met:
    Struct S (Element) {
	this (int[] data, string message) {...}
	this (int[] data, Element element) {...}
What happens when Element is string? Below an example:

struct S(Typ) {
    this(int) {writeln("int");}
    this(Typ) {writeln("Typ");}
}
unittest {
    auto s1 = S!string(1);
    auto s1 = S!int(1);
}
==>
rdmd -w -debug -unittest -L--export-dynamic --build-only -of"__trials__" "__trials__.d"

__trials__.d(42): Error: constructor __trials__.S!(int).S.this called with argument types:
	((int))
matches both:
	__trials__.S!(int).S.this(int _param_0)
and:
	__trials__.S!(int).S.this(int _param_0)

Compilation failed.

How do you cope with such cases?

Denis

(*) or any other func, in fact, but the issue shows up more frequently on constructors, because they have good reasons to accept various parameter sets.
-- -- -- -- -- -- --
vit esse estrany ☣

spir.wikidot.com

December 30, 2010
Why not have something like this:

this (int[] data, string text, bool isMessage = false) {...}

Then, if you just pass in two parameters you treat it as a filename and if you pass in a "true" for the third parameter, it's a message.  It's not quite what you're looking for, but it's simple and pretty clean.

Casey
December 30, 2010
spir:

> But some language types (or machine types) can have very diverse _human_ semantics, and thus be used for various purposes which should, but cannot, be considered different:

You may wrap your data in a struct.

Bye,
bearophile
December 30, 2010
sybrandy wrote:

> Why not have something like this:
> 
> this (int[] data, string text, bool isMessage = false) {...}
> 
> Then, if you just pass in two parameters you treat it as a filename and if you pass in a "true" for the third parameter, it's a message.  It's not quite what you're looking for, but it's simple and pretty clean.
> 
> Casey

If you opt for this solution, an enum is slightly more verbose but much clearer:

enum IsMessage
{
    Yes,
    No
}

this (int[] data, string text, IsMessage isMessage = IsMessage.No) {...}


auto s = new S(data, text, IsMessage.Yes);

vs

auto s = new S(data, text, true);


I would still prefer a factory method or a struct wrapper though.
December 30, 2010
On Thu, 30 Dec 2010 08:04:29 -0500
sybrandy <sybrandy@gmail.com> wrote:

> Why not have something like this:
> 
> this (int[] data, string text, bool isMessage = false) {...}
> 
> Then, if you just pass in two parameters you treat it as a filename and if you pass in a "true" for the third parameter, it's a message.  It's not quite what you're looking for, but it's simple and pretty clean.
> 
> Casey

That's what I did, precisely, and yes the fake param is bool. (thought I had mentioned this in the OP, maybe forgot, sorry).

Denis
-- -- -- -- -- -- --
vit esse estrany ☣

spir.wikidot.com

December 30, 2010
On Thu, 30 Dec 2010 08:15:51 -0500
bearophile <bearophileHUGS@lycos.com> wrote:

> > But some language types (or machine types) can have very diverse _human_ semantics, and thus be used for various purposes which should, but cannot, be considered different:
> 
> You may wrap your data in a struct.

Yes, thank you for this hint. A kind of proxy struct? It can indeed be used everywhere performance is not critical. But a side issue is that it requires the 'alias this' hack, I guess, or forwarding every operation to the actual, but wrapped, element. What do you think

Denis
-- -- -- -- -- -- --
vit esse estrany ☣

spir.wikidot.com

December 30, 2010
On Thu, Dec 30, 2010 at 12:18 PM, spir <denis.spir@gmail.com> wrote:

> On Thu, 30 Dec 2010 08:15:51 -0500
> bearophile <bearophileHUGS@lycos.com> wrote:
>
> > > But some language types (or machine types) can have very diverse
> _human_ semantics, and thus be used for various purposes which should, but cannot, be considered different:
> >
> > You may wrap your data in a struct.
>
> Yes, thank you for this hint. A kind of proxy struct? It can indeed be used everywhere performance is not critical. But a side issue is that it requires the 'alias this' hack, I guess, or forwarding every operation to the actual, but wrapped, element. What do you think
>
> Denis
> -- -- -- -- -- -- --
> vit esse estrany ☣
>
> spir.wikidot.com
>
>
Why is performance harmed by the use of a struct? Wouldn't it be zero-overhead like C++'s std::auto_ptr?

Also, the alias this and the forward might be a real good solution. And a mixin like Luger's might be jackpot, really. I just dislike the use in:

func2(Position(1)); // implicit conversion to int with alias this


I guess that can be actually a bug, not a feature :) Maybe one day the function signature changes slightly and the problem is further disguised because "you're obviously passing the right Position here"... when it's actually an "int count" thing. The "alias this" thing is a good shorthand when assigning, though:

int a = pos; // implicit conversion from Position to int instead of int b = pos.base;


-- 
Atenciosamente / Sincerely,
Guilherme ("n2liquid") Vieira


December 30, 2010
On 12/30/2010 08:46 AM, Lutger Blijdestijn wrote:
> sybrandy wrote:
>
>> Why not have something like this:
>>
>> this (int[] data, string text, bool isMessage = false) {...}
>>
>> Then, if you just pass in two parameters you treat it as a filename and
>> if you pass in a "true" for the third parameter, it's a message.  It's
>> not quite what you're looking for, but it's simple and pretty clean.
>>
>> Casey
>
> If you opt for this solution, an enum is slightly more verbose but much
> clearer:
>
> enum IsMessage
> {
>      Yes,
>      No
> }
>
> this (int[] data, string text, IsMessage isMessage = IsMessage.No) {...}
>
>
> auto s = new S(data, text, IsMessage.Yes);
>
> vs
>
> auto s = new S(data, text, true);

I will agree that is clearer.  I just had to do stuff like this for different reasons and it worked very nicely.
>
>
> I would still prefer a factory method or a struct wrapper though.

True, I just don't know how without it being complex.  I think this may be the case where improvements to the type system would be useful.  For me, this situation doesn't come up very often, so I'm not all that concerned, but I do see where this can be useful.  I'm just not a fan of having to write a lot of code to do something that the language can turn into something simple.

However, another possible solution that just occurred to me is something like this (please forgive any typos, I haven't done inheritance in D yet):

enum TextType { Filename, Message }

class Text
{
    string text;
    TextType type;

    bool isMessage() { return TextType.Message == this.type; }
}

class Filename : Text
{
    this(int[] data, string txt)
    {
        this.type = TextType.Filename;
        this.text = txt;
        // Do something with data...
    }
}

class Message : Text
{
    this(int[] data, string txt)
    {
        this.type = TextType.Message;
        this.text = txt;
        // Do something with data...
    }
}

Then, you can do something like this:

Text foo = new Filename(data, filename);
Text bar = new Message(data, message);

Not sure if it's any better than using an enum, but it still has the clarity that you're looking for.

Casey
December 30, 2010
Guilherme Vieira wrote:

> On Thu, Dec 30, 2010 at 12:18 PM, spir <denis.spir@gmail.com> wrote:
> 
>> On Thu, 30 Dec 2010 08:15:51 -0500
>> bearophile <bearophileHUGS@lycos.com> wrote:
>>
>> > > But some language types (or machine types) can have very diverse
>> _human_ semantics, and thus be used for various purposes which should, but cannot, be considered different:
>> >
>> > You may wrap your data in a struct.
>>
>> Yes, thank you for this hint. A kind of proxy struct? It can indeed be used everywhere performance is not critical. But a side issue is that it requires the 'alias this' hack, I guess, or forwarding every operation to the actual, but wrapped, element. What do you think
>>
>> Denis
>> -- -- -- -- -- -- --
>> vit esse estrany ☣
>>
>> spir.wikidot.com
>>
>>
> Why is performance harmed by the use of a struct? Wouldn't it be zero-overhead like C++'s std::auto_ptr?
> 
> Also, the alias this and the forward might be a real good solution. And a mixin like Luger's might be jackpot, really. I just dislike the use in:
> 
> func2(Position(1)); // implicit conversion to int with alias this

This is deliberate, in this case I think of Position as a subtype of int so
it is entirely reasonable to implicitly convert it. With opDispatch and
operator overloading you could achieve the semantics you are after though.

> I guess that can be actually a bug, not a feature :) Maybe one day the function signature changes slightly and the problem is further disguised because "you're obviously passing the right Position here"... when it's actually an "int count" thing. The "alias this" thing is a good shorthand when assigning, though:
> 
> int a = pos; // implicit conversion from Position to int instead of int b = pos.base;
> 
December 30, 2010
On Thu, 30 Dec 2010 05:50:55 -0500, spir <denis.spir@gmail.com> wrote:

> Hello,
>
>
> When 2 constructors (*) accept the same number of parameters, the only remaining discrimination is type. Right? But some language types (or machine types) can have very diverse _human_ semantics, and thus be used for various purposes which should, but cannot, be considered different:
> 	this (int[] data, string filename) {...}
> 	this (int[] data, string message) {...}
> Aliasing like in
> 	alias string Name;
> does not help since for D Name is still string.
>
> I know about typedef, but it is not even mentionned in TDPL, so I guess it is on the deprecation path. (Am I right?) So, what is the solution for this? (I added a 3rd fake bool parameter in one case)

What I would suggest is static factory methods.  The issue with any kind of typedef (be it with the soon-to-be-deprecated typedef keyword or with a proxy struct), is that what does this mean?

auto obj = new Foo([1, 2, 3], "blah");

Is "blah" a filename or a message?

Whereas, if you use factory methods:

auto obj = Foo.createWithFilename([1,2,3], "blah"); // "blah" is a filename
auto obj = Foo.createWithMessage([1,2,3], "blah"); // "blah" is a message

The code becomes crystal clear.  Reduce verbosity as you see fit ;)

I've used this kind of method with creating exceptions in C#, where I want to generate a message based on the data instead of having to redundantly specify both the message and the data.

-Steve
« First   ‹ Prev
1 2