www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - discrimination of constructors with same number of parameters

reply spir <denis.spir gmail.com> writes:
Hello,


When 2 constructors (*) accept the same number of parameters, the only rema=
ining discrimination is type. Right? But some language types (or machine ty=
pes) 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 ord=
inals (index, which one), as cardinals (count, how many), as any of the cha=
r types. These are completely different semantics for the "modeller" (the p=
rogrammer), but for the language (thus for the machine) they are the same s=
emantics.

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 =3D S!string(1);
    auto s1 =3D S!int(1);
}
=3D=3D>
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 c=
onstructors, because they have good reasons to accept various parameter set=
s.
-- -- -- -- -- -- --
vit esse estrany =E2=98=A3

spir.wikidot.com
Dec 30 2010
next sibling parent reply sybrandy <sybrandy gmail.com> writes:
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
Dec 30 2010
parent reply Lutger Blijdestijn <lutger.blijdestijn gmail.com> writes:
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.
Dec 30 2010
parent sybrandy <sybrandy gmail.com> writes:
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
Dec 30 2010
prev sibling next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
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
Dec 30 2010
parent Lutger Blijdestijn <lutger.blijdestijn gmail.com> writes:
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


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

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;
 

Dec 30 2010
prev sibling next sibling parent spir <denis.spir gmail.com> writes:
On Thu, 30 Dec 2010 08:04:29 -0500
sybrandy <sybrandy gmail.com> wrote:

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

That's what I did, precisely, and yes the fake param is bool. (thought I ha= d mentioned this in the OP, maybe forgot, sorry). Denis -- -- -- -- -- -- -- vit esse estrany =E2=98=A3 spir.wikidot.com
Dec 30 2010
prev sibling next sibling parent spir <denis.spir gmail.com> writes:
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=


, be considered different: =20
=20
 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 requir= es the 'alias this' hack, I guess, or forwarding every operation to the act= ual, but wrapped, element. What do you think Denis -- -- -- -- -- -- -- vit esse estrany =E2=98=A3 spir.wikidot.com
Dec 30 2010
prev sibling next sibling parent Guilherme Vieira <n2.nitrogen gmail.com> writes:
--00163628430a5c7f710498a187f5
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable

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



 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 us=

 everywhere performance is not critical. But a side issue is that it requi=

 the 'alias this' hack, I guess, or forwarding every operation to the actu=

 but wrapped, element. What do you think

 Denis
 -- -- -- -- -- -- --
 vit esse estrany =E2=98=A3

 spir.wikidot.com

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 =3D pos; // implicit conversion from Position to int instead of int b =3D pos.base; --=20 Atenciosamente / Sincerely, Guilherme ("n2liquid") Vieira --00163628430a5c7f710498a187f5 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable <div class=3D"gmail_quote">On Thu, Dec 30, 2010 at 12:18 PM, spir <span dir= =3D"ltr">&lt;<a href=3D"mailto:denis.spir gmail.com">denis.spir gmail.com</= a>&gt;</span> wrote:<br><blockquote class=3D"gmail_quote" style=3D"margin:0= 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;"> On Thu, 30 Dec 2010 08:15:51 -0500<br> <div class=3D"im">bearophile &lt;<a href=3D"mailto:bearophileHUGS lycos.com= ">bearophileHUGS lycos.com</a>&gt; wrote:<br> <br> </div><div><div></div><div class=3D"h5">&gt; &gt; 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:<br=

&gt;<br> &gt; You may wrap your data in a struct.<br> <br> </div></div>Yes, thank you for this hint. A kind of proxy struct? It can in= deed be used everywhere performance is not critical. But a side issue is th= at it requires the &#39;alias this&#39; hack, I guess, or forwarding every = operation to the actual, but wrapped, element. What do you think<br> <div><div></div><div class=3D"h5"><br> Denis<br> -- -- -- -- -- -- --<br> vit esse estrany =E2=98=A3<br> <br> <a href=3D"http://spir.wikidot.com" target=3D"_blank">spir.wikidot.com</a><= br> <br> </div></div></blockquote></div><br>Why is performance harmed by the use of = a struct? Wouldn&#39;t it be zero-overhead like C++&#39;s std::auto_ptr?<di= v><br><div>Also, the alias this and the forward might be a real good soluti= on. And a mixin like Luger&#39;s might be jackpot, really. I just dislike t= he use in:</div> </div><blockquote class=3D"webkit-indent-blockquote" style=3D"margin: 0 0 0= 40px; border: none; padding: 0px;"><div><div><span class=3D"Apple-style-sp= an" style=3D"font-family: arial, sans-serif; font-size: 13px; border-collap= se: collapse; color: rgb(80, 0, 80); ">func2(Position(1)); // implicit conv= ersion to int with alias this</span></div> </div></blockquote><font class=3D"Apple-style-span" face=3D"arial, sans-ser= if"><span class=3D"Apple-style-span" style=3D"border-collapse: collapse; ">= <div><font class=3D"Apple-style-span" face=3D"arial, sans-serif"><span clas= s=3D"Apple-style-span" style=3D"border-collapse: collapse; "><br> </span></font></div>I guess that can be actually a bug, not a feature :) Ma= ybe one day the function signature changes slightly and the problem is furt= her disguised because &quot;you&#39;re obviously passing the right Position= here&quot;... when it&#39;s actually an &quot;int count&quot; thing. The &= quot;alias this&quot; thing is a good shorthand when assigning, though:</sp= an></font><div> <blockquote class=3D"webkit-indent-blockquote" style=3D"margin: 0 0 0 40px;= border: none; padding: 0px;"><div><font class=3D"Apple-style-span" face=3D= "arial, sans-serif"><span class=3D"Apple-style-span" style=3D"border-collap= se: collapse; ">int a =3D pos; // implicit conversion from Position to int = instead of</span></font></div> <div><font class=3D"Apple-style-span" face=3D"arial, sans-serif"><span clas= s=3D"Apple-style-span" style=3D"border-collapse: collapse; ">int b =3D pos.= base;</span></font></div></blockquote><div><div><div><br>-- <br>Atenciosame= nte / Sincerely,<br> Guilherme (&quot;n2liquid&quot;) Vieira<br> </div></div></div></div> --00163628430a5c7f710498a187f5--
Dec 30 2010
prev sibling next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
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
Dec 30 2010
parent =?UTF-8?B?IkrDqXLDtG1lIE0uIEJlcmdlciI=?= <jeberger free.fr> writes:
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable

Steven Schveighoffer wrote:
 What I would suggest is static factory methods.  The issue with any kin=

 of typedef (be it with the soon-to-be-deprecated typedef keyword or wit=

 a proxy struct), is that what does this mean?
=20
 auto obj =3D new Foo([1, 2, 3], "blah");
=20
 Is "blah" a filename or a message?
=20

 Whereas, if you use factory methods:
=20
 auto obj =3D Foo.createWithFilename([1,2,3], "blah"); // "blah" is a fi=

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

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

auto obj =3D new Foo ([1, 2, 3], Message ("blah")); Jerome --=20 mailto:jeberger free.fr http://jeberger.free.fr Jabber: jeberger jabber.fr
Dec 30 2010
prev sibling next sibling parent spir <denis.spir gmail.com> writes:
On Thu, 30 Dec 2010 17:10:00 +0100
"J=C3=A9r=C3=B4me M. Berger" <jeberger free.fr> wrote:

 Steven Schveighoffer wrote:
 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?
=20
 auto obj =3D new Foo([1, 2, 3], "blah");
=20
 Is "blah" a filename or a message?
=20


Yes, you are right. Typedef-like solutions need core support by the languag= e with a kind of hint to the compiler... playing the role of type in J=C3= =A9r=C3=B4me's sample below.
 Whereas, if you use factory methods:
=20
 auto obj =3D Foo.createWithFilename([1,2,3], "blah"); // "blah" is a fi=


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


Factory methods are definitely convenient. The single objection is rather c= onceptual: it defeats the purpose of a major language feature, namely const= ructor; which happens to have a clear meaning from the modelling point of v= iew.
 The code becomes crystal clear.  Reduce verbosity as you see fit ;)
=20

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

Conceptually, I would prefere this -- at the use place. But if requires obf= uscating the code at the definition point (with eg wrapper structs), is it = worth it? If we could write eg: typedef string Message; auto obj =3D new Foo ([1, 2, 3], Message ("blah")); then I would be happy, I guess ;-) Denis -- -- -- -- -- -- -- vit esse estrany =E2=98=A3 spir.wikidot.com
Dec 30 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 30 Dec 2010 12:08:56 -0500, spir <denis.spir gmail.com> wrote:

 On Thu, 30 Dec 2010 17:10:00 +0100
 "Jérôme M. Berger" <jeberger free.fr> wrote:

 Steven Schveighoffer wrote:
 What I would suggest is static factory methods.  The issue with any  

 of typedef (be it with the soon-to-be-deprecated typedef keyword or  

 a proxy struct), is that what does this mean?

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

 Is "blah" a filename or a message?


Yes, you are right. Typedef-like solutions need core support by the language with a kind of hint to the compiler... playing the role of type in Jérôme's sample below.

I expected a definition like this: typedef string filename; this(int[] x, string message); this(int[] x, filename file); Which would be more ambiguous in usage. So your version (with two typedefs) is better.
 Whereas, if you use factory methods:

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

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


Factory methods are definitely convenient. The single objection is rather conceptual: it defeats the purpose of a major language feature, namely constructor; which happens to have a clear meaning from the modelling point of view.

This doesn't mean much to me. I don't see the benefit of using 'new' vs. using a static factory method. What is the "clear meaning" that constructors have that factory methods do not?
 The code becomes crystal clear.  Reduce verbosity as you see fit ;)

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

Conceptually, I would prefere this -- at the use place. But if requires obfuscating the code at the definition point (with eg wrapper structs), is it worth it? If we could write eg: typedef string Message; auto obj = new Foo ([1, 2, 3], Message ("blah")); then I would be happy, I guess ;-)

Wait, this isn't any different than using a wrapper struct... struct Message { string value; } struct Filename { string value; } class Foo { string message; string filename; int[] arr; this(int[] arr, Message m) {this.arr = arr; this.message = m.value;} this(int[] arr, Filename f) {this.arr = arr; this.filename = f.value;} } How is that "obfuscation"? I still prefer the factory method solution, as it doesn't add unecessary types. -Steve
Dec 30 2010
prev sibling next sibling parent Guilherme Vieira <n2.nitrogen gmail.com> writes:
--00163628430aa7618a0498a4ec9f
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable

On Thu, Dec 30, 2010 at 3:19 PM, Steven Schveighoffer
<schveiguy yahoo.com>wrote:

 On Thu, 30 Dec 2010 12:08:56 -0500, spir <denis.spir gmail.com> wrote:

  On Thu, 30 Dec 2010 17:10:00 +0100
 "J=E9r=F4me M. Berger" <jeberger free.fr> wrote:

  Steven Schveighoffer wrote:
 What I would suggest is static factory methods.  The issue with any

 of typedef (be it with the soon-to-be-deprecated typedef keyword or

 a proxy struct), is that what does this mean?

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

 Is "blah" a filename or a message?


Yes, you are right. Typedef-like solutions need core support by the language with a kind of hint to the compiler... playing the role of type=


 J=E9r=F4me's sample below.

I expected a definition like this: typedef string filename; this(int[] x, string message); this(int[] x, filename file); Which would be more ambiguous in usage. So your version (with two typedefs) is better.
  > Whereas, if you use factory methods:
 auto obj =3D Foo.createWithFilename([1,2,3], "blah"); // "blah" is a

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


Factory methods are definitely convenient. The single objection is rathe=


 conceptual: it defeats the purpose of a major language feature, namely
 constructor; which happens to have a clear meaning from the modelling po=


 of view.

This doesn't mean much to me. I don't see the benefit of using 'new' vs. using a static factory method. What is the "clear meaning" that constructors have that factory methods do not?
  > The code becomes crystal clear.  Reduce verbosity as you see fit ;)

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

Conceptually, I would prefere this -- at the use place. But if requires obfuscating the code at the definition point (with eg wrapper structs), =


 it worth it?

 If we could write eg:
        typedef string Message;
        auto obj =3D new Foo ([1, 2, 3], Message ("blah"));
 then I would be happy, I guess ;-)

struct Message { string value; } struct Filename { string value; } class Foo { string message; string filename; int[] arr; this(int[] arr, Message m) {this.arr =3D arr; this.message =3D m.value;=

   this(int[] arr, Filename f) {this.arr =3D arr; this.filename =3D f.valu=

 }

 How is that "obfuscation"?

 I still prefer the factory method solution, as it doesn't add unecessary
 types.

 -Steve

There's an idiom I'm quite fond of. There are some classes you shouldn't be instantiating yourself. Take for example a SoundSource class, which represents a source of sound in a 2D or 3D environment. It's obvious that it requires the SoundSystem to be initialized when it's created, unless it used lazy initialization of the sound system (which I dislike, since everytime you create an object it'll have to check whether the system is initialized or not). As such, it makes sense that the architecture guide client developers to only instantiate after initializing the system. If you normally simply *new*SoundSources yourself, it's not hard to forget the sound system initialization. So I prefer to make the SoundSystem class a factory of SoundSources (Ogre3D does such things a lot), and it's particularly damn great to create template methods such as these: class SoundSystem { Unique!(TSoundSource) createSource(TSoundSource, CtorArgs...)(CtorArgs ctorArgs) { // reserves first argument for mandatory parameters, but leaves the rest client-defined return new TSoundSource(this, ctorArgs); } } // later ... sndSystem.createSource!(MySoundSource)(my, custom, parameters); In this case, constructing the SoundSource required a SoundSystem as a parameter, so yeah, you would need the thing to be able to instantiate alright. But it surely gives margin to misuses: if you, as the library developer, noticed that *any* SoundSource implementation should get the SoundSystem upon construction from the caller (and not try to tell which system to use by e.g. picking it from a singleton of the likes), then this idiom is useful. I find this kind of usage extremely expressive (in fact, I'd like to take the moment the ask what the gurus think about it; I really have never seen people doing this). It shows precisely how the library is meant to be used. The least wrong things you can do, the better, so getting rid of the possibility of instantiating things at the wrong times is certainly good. And static factories succeed in making such things harder. Yes, you could wrap classes in structs that would construct them using one factory or another, but making useful idioms more and more cumbersome to us= e is almost never a good idea: struct MyObjectWithFileName // this { // is this(string fname) { obj =3D MyObject.createWithFilename(fname); } // s= o MyObject obj; // much } // typing! // later ... manager.create!(MyObjectWithFileName)("filename.txt"); // phew..! now repea= t for every other overload When I create factory methods like those proposed in this thread, I feel like I'm hijacking a core aspect of the language. Goddamn it, it's the constructor! IMHO, everybody expects to construct things.. using constructors (unless there's a restriction like not being allowed to construct stuff anytime, but only under certain conditions, which was the case with SoundSources). Factory methods should, again just in my opinion, be used to exploit polymorphism or enhance the system design, not to solve problems of the language dealing with method overloading. It's just.. creepy. Is it just me= ? I feel like I'm giving too much and barely getting one thirth in return... --=20 Atenciosamente / Sincerely, Guilherme ("n2liquid") Vieira --00163628430aa7618a0498a4ec9f Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable <div class=3D"gmail_quote">On Thu, Dec 30, 2010 at 3:19 PM, Steven Schveigh= offer <span dir=3D"ltr">&lt;<a href=3D"mailto:schveiguy yahoo.com">schveigu= y yahoo.com</a>&gt;</span> wrote:<br><blockquote class=3D"gmail_quote" styl= e=3D"margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;"> <div class=3D"im">On Thu, 30 Dec 2010 12:08:56 -0500, spir &lt;<a href=3D"m= ailto:denis.spir gmail.com" target=3D"_blank">denis.spir gmail.com</a>&gt; = wrote:<br> <br> </div><blockquote class=3D"gmail_quote" style=3D"margin:0 0 0 .8ex;border-l= eft:1px #ccc solid;padding-left:1ex"><div class=3D"im"> On Thu, 30 Dec 2010 17:10:00 +0100<br> &quot;J=E9r=F4me M. Berger&quot; &lt;<a href=3D"mailto:jeberger free.fr" ta= rget=3D"_blank">jeberger free.fr</a>&gt; wrote:<br> <br> </div><div class=3D"im"><blockquote class=3D"gmail_quote" style=3D"margin:0= 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"> Steven Schveighoffer wrote:<br> &gt; What I would suggest is static factory methods. =A0The issue with any = kind<br> &gt; of typedef (be it with the soon-to-be-deprecated typedef keyword or wi= th<br> &gt; a proxy struct), is that what does this mean?<br> &gt;<br> &gt; auto obj =3D new Foo([1, 2, 3], &quot;blah&quot;);<br> &gt;<br> &gt; Is &quot;blah&quot; a filename or a message?<br> &gt;<br> --&gt; Error, Foo (int[], string) does not exist.<br> </blockquote> <br></div><div class=3D"im"> Yes, you are right. Typedef-like solutions need core support by the languag= e with a kind of hint to the compiler... playing the role of type in J=E9r= =F4me&#39;s sample below.<br> </div></blockquote> <br> I expected a definition like this:<br> <br> typedef string filename;<br> <br> this(int[] x, string message);<br> this(int[] x, filename file);<br> <br> Which would be more ambiguous in usage. =A0So your version (with two typede= fs) is better.<br> <br> <blockquote class=3D"gmail_quote" style=3D"margin:0 0 0 .8ex;border-left:1p= x #ccc solid;padding-left:1ex"><div class=3D"im"> <br> <blockquote class=3D"gmail_quote" style=3D"margin:0 0 0 .8ex;border-left:1p= x #ccc solid;padding-left:1ex"> &gt; Whereas, if you use factory methods:<br> &gt;<br> &gt; auto obj =3D Foo.createWithFilename([1,2,3], &quot;blah&quot;); // &qu= ot;blah&quot; is a filename<br> &gt; auto obj =3D Foo.createWithMessage([1,2,3], &quot;blah&quot;); // &quo= t;blah&quot; is a message<br> </blockquote> <br></div><div class=3D"im"> Factory methods are definitely convenient. The single objection is rather c= onceptual: it defeats the purpose of a major language feature, namely const= ructor; which happens to have a clear meaning from the modelling point of v= iew.<br> </div></blockquote> <br> This doesn&#39;t mean much to me. =A0I don&#39;t see the benefit of using &= #39;new&#39; vs. using a static factory method. =A0What is the &quot;clear = meaning&quot; that constructors have that factory methods do not?<br> <br> <blockquote class=3D"gmail_quote" style=3D"margin:0 0 0 .8ex;border-left:1p= x #ccc solid;padding-left:1ex"><div class=3D"im"> <br> <blockquote class=3D"gmail_quote" style=3D"margin:0 0 0 .8ex;border-left:1p= x #ccc solid;padding-left:1ex"> &gt; The code becomes crystal clear. =A0Reduce verbosity as you see fit ;)<= br> &gt;<br> auto obj =3D new Foo ([1, 2, 3], Filename (&quot;blah&quot;));<br> auto obj =3D new Foo ([1, 2, 3], Message (&quot;blah&quot;));<br> </blockquote> <br></div><div class=3D"im"> Conceptually, I would prefere this -- at the use place. But if requires obf= uscating the code at the definition point (with eg wrapper structs), is it = worth it?<br> <br> If we could write eg:<br> =A0 =A0 =A0 =A0typedef string Message;<br></div><div class=3D"im"> =A0 =A0 =A0 =A0auto obj =3D new Foo ([1, 2, 3], Message (&quot;blah&quot;)= );<br></div><div class=3D"im"> then I would be happy, I guess ;-)<br> <br> </div></blockquote> <br> Wait, this isn&#39;t any different than using a wrapper struct...<br> <br> struct Message<br> {<br> =A0 string value;<br> }<br> <br> struct Filename<br> {<br> =A0 string value;<br> }<br> <br> class Foo<br> {<br> =A0 string message;<br> =A0 string filename;<br> =A0 int[] arr;<br> =A0 this(int[] arr, Message m) {this.arr =3D arr; this.message =3D m.value= ;}<br> =A0 this(int[] arr, Filename f) {this.arr =3D arr; this.filename =3D f.val= ue;}<br> }<br> <br> How is that &quot;obfuscation&quot;?<br> <br> I still prefer the factory method solution, as it doesn&#39;t add unecessar= y types.<br> <br> -Steve<br> </blockquote></div><br>There&#39;s an idiom I&#39;m quite fond of. There ar= e some classes you shouldn&#39;t be instantiating yourself.<div><br></div><= div>Take for example a SoundSource class, which represents a source of soun= d in a 2D or 3D environment. It&#39;s obvious that it requires the SoundSys= tem to be initialized when it&#39;s created, unless it used lazy initializa= tion of the sound system (which I dislike, since everytime you create an ob= ject it&#39;ll have to check whether the system is initialized or not).<div=

evelopers to only instantiate after initializing the system. If you normall= y simply <b>new</b> SoundSources yourself, it&#39;s not hard to forget the = sound system initialization. So I prefer to make the SoundSystem class a fa= ctory of SoundSources (Ogre3D does such things a lot), and it&#39;s particu= larly damn great to create template methods such as these:</div> </div><div><br></div><blockquote class=3D"webkit-indent-blockquote" style= =3D"margin: 0 0 0 40px; border: none; padding: 0px;"><div><div><font class= =3D"Apple-style-span" face=3D"&#39;courier new&#39;, monospace">class Sound= System</font></div> <div><font class=3D"Apple-style-span" face=3D"&#39;courier new&#39;, monosp= ace">{</font></div></div><div><font class=3D"Apple-style-span" face=3D"&#39= ;courier new&#39;, monospace">=A0=A0 =A0Unique!(TSoundSource) createSource(= TSoundSource, CtorArgs...)(CtorArgs ctorArgs)</font></div> <div><font class=3D"Apple-style-span" face=3D"&#39;courier new&#39;, monosp= ace">=A0=A0 =A0{</font></div><div><font class=3D"Apple-style-span" face=3D"= &#39;courier new&#39;, monospace">=A0=A0 =A0 =A0 =A0// reserves first argum= ent for mandatory parameters, but leaves the rest client-defined</font></di= v> <div><font class=3D"Apple-style-span" face=3D"&#39;courier new&#39;, monosp= ace">=A0=A0 =A0 =A0 =A0return new TSoundSource(this, ctorArgs);</font></div=
<div><font class=3D"Apple-style-span" face=3D"&#39;courier new&#39;, monos=

<div><font class=3D"Apple-style-span" face=3D"&#39;courier new&#39;, monosp= ace">}</font></div><div><font class=3D"Apple-style-span" face=3D"&#39;couri= er new&#39;, monospace"><br></font></div><div><font class=3D"Apple-style-sp= an" face=3D"&#39;courier new&#39;, monospace">// later ...</font></div> <div><font class=3D"Apple-style-span" face=3D"&#39;courier new&#39;, monosp= ace">sndSystem.createSource!(MySoundSource)(my, custom, parameters);</font>= </div></blockquote><div style=3D"font-family: &#39;courier new&#39;, monosp= ace; "> <br></div><div><font class=3D"Apple-style-span" face=3D"arial, helvetica, s= ans-serif">In this case, constructing the SoundSource required a SoundSyste= m as a parameter, so yeah, you would need the thing to be able to instantia= te alright. But it surely gives margin to misuses: if you, as the library d= eveloper, noticed that *any* SoundSource implementation should get the Soun= dSystem upon construction from the caller (and not try to tell which system= to use by e.g. picking it from a singleton of the likes), then this idiom = is useful.</font></div> <div><font class=3D"Apple-style-span" face=3D"arial, helvetica, sans-serif"=
<br></font></div><div><font class=3D"Apple-style-span" face=3D"arial, helv=

I&#39;d like to take the moment the ask what the gurus think about it; I r= eally have never seen people doing this). It shows precisely how the librar= y is meant to be used.</font></div> <div><font class=3D"Apple-style-span" face=3D"arial, helvetica, sans-serif"=
<br></font></div><div><font class=3D"Apple-style-span" face=3D"arial, helv=

g rid of the possibility of instantiating things at the wrong times is cert= ainly good. And static factories succeed in making such things harder.</fon= t></div> <div><font class=3D"Apple-style-span" face=3D"arial, helvetica, sans-serif"=
<br></font></div><div><font class=3D"Apple-style-span" face=3D"arial, helv=

uct them using one factory or another, but making useful idioms more and mo= re cumbersome to use is almost never a good idea:</font></div> <div><font class=3D"Apple-style-span" face=3D"arial, helvetica, sans-serif"=
<br></font></div><blockquote class=3D"webkit-indent-blockquote" style=3D"m=

tyle-span" face=3D"&#39;courier new&#39;, monospace">struct MyObjectWithFil= eName // this</font></div> <div><font class=3D"Apple-style-span" face=3D"&#39;courier new&#39;, monosp= ace">{ // is</font></div><div><font class=3D"Apple-style-span" face=3D"&#39= ;courier new&#39;, monospace">=A0=A0 =A0this(string fname) { obj =3D MyObje= ct.createWithFilename(fname); } // so</font></div> <div><font class=3D"Apple-style-span" face=3D"&#39;courier new&#39;, monosp= ace">=A0=A0 =A0MyObject obj; // much</font></div><div><font class=3D"Apple-= style-span" face=3D"&#39;courier new&#39;, monospace">} // typing!</font></= div><div><font class=3D"Apple-style-span" face=3D"&#39;courier new&#39;, mo= nospace"><br> </font></div><div><font class=3D"Apple-style-span" face=3D"&#39;courier new= &#39;, monospace">// later ...</font></div><div><font class=3D"Apple-style-= span" face=3D"&#39;courier new&#39;, monospace">manager.create!(MyObjectWit= hFileName)(&quot;filename.txt&quot;); // phew..! now repeat for every other= overload</font></div> </blockquote><div><font class=3D"Apple-style-span" face=3D"arial, helvetica= , sans-serif"><br></font></div><div><font class=3D"Apple-style-span" face= =3D"arial, helvetica, sans-serif">When I create factory methods like those = proposed in this thread, I feel like I&#39;m hijacking a core aspect of the= language. Goddamn it, it&#39;s the constructor! IMHO, everybody expects to= construct things.. using constructors (unless there&#39;s a restriction li= ke not being allowed to construct stuff anytime, but only under certain con= ditions, which was the case with SoundSources).</font></div> <div><font class=3D"Apple-style-span" face=3D"arial, helvetica, sans-serif"=
<br></font></div><div><font class=3D"Apple-style-span" face=3D"arial, helv=

d to exploit polymorphism or enhance the system design, not to solve proble= ms of the language dealing with method overloading. It&#39;s just.. creepy.= Is it just me? I feel like I&#39;m giving too much and barely getting one = thirth in return...</font></div> <div><div><div><div><br>-- <br>Atenciosamente / Sincerely,<br>Guilherme (&q= uot;n2liquid&quot;) Vieira<br> </div></div></div></div> --00163628430aa7618a0498a4ec9f--
Dec 30 2010
prev sibling next sibling parent spir <denis.spir gmail.com> writes:
On Thu, 30 Dec 2010 16:33:39 -0200
Guilherme Vieira <n2.nitrogen gmail.com> wrote:

 When I create factory methods like those proposed in this thread, I feel
 like I'm hijacking a core aspect of the language. Goddamn it, it's the
 constructor! IMHO, everybody expects to construct things.. using
 constructors (unless there's a restriction like not being allowed to
 construct stuff anytime, but only under certain conditions, which was the
 case with SoundSources).
=20
 Factory methods should, again just in my opinion, be used to exploit
 polymorphism or enhance the system design, not to solve problems of the
 language dealing with method overloading. It's just.. creepy. Is it just =

 I feel like I'm giving too much and barely getting one thirth in return...

Steven: This is great answer to the question you asked me (and I did not a= nswer) about the "clear meaning" of constructors in a PL. (Else, let's just= get rid of them alltogether, and of the notion too, and just use object fa= ctories?) Denis -- -- -- -- -- -- -- vit esse estrany =E2=98=A3 spir.wikidot.com
Dec 30 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 30 Dec 2010 14:08:49 -0500, spir <denis.spir gmail.com> wrote:

 On Thu, 30 Dec 2010 16:33:39 -0200
 Guilherme Vieira <n2.nitrogen gmail.com> wrote:

 When I create factory methods like those proposed in this thread, I feel
 like I'm hijacking a core aspect of the language. Goddamn it, it's the
 constructor! IMHO, everybody expects to construct things.. using
 constructors (unless there's a restriction like not being allowed to
 construct stuff anytime, but only under certain conditions, which was  
 the
 case with SoundSources).

 Factory methods should, again just in my opinion, be used to exploit
 polymorphism or enhance the system design, not to solve problems of the
 language dealing with method overloading. It's just.. creepy. Is it  
 just me?
 I feel like I'm giving too much and barely getting one thirth in  
 return...

Steven: This is great answer to the question you asked me (and I did not answer) about the "clear meaning" of constructors in a PL. (Else, let's just get rid of them alltogether, and of the notion too, and just use object factories?)

A factory method *forwards to* a constructor. You are not losing the construction aspect (or its special qualities), in fact, it can sit beside a constructor. There are many cases where a constructor is not as clear as a factory method. Take for std.array.Appender. The constructor that takes an initial array as input: int[1024] buf; auto app = Appender!(int[])(buf); vs. auto app = appender(buf); The factory method looks clearer to me. I think it's really subjective depending on the situation. All I'm saying is that there is nothing IMO that rules out factory methods as construction means on principal. -Steve
Dec 30 2010
prev sibling parent spir <denis.spir gmail.com> writes:
On Thu, 30 Dec 2010 14:26:21 -0500
"Steven Schveighoffer" <schveiguy yahoo.com> wrote:

 On Thu, 30 Dec 2010 14:08:49 -0500, spir <denis.spir gmail.com> wrote:
=20
 On Thu, 30 Dec 2010 16:33:39 -0200
 Guilherme Vieira <n2.nitrogen gmail.com> wrote:

 When I create factory methods like those proposed in this thread, I fe=



 like I'm hijacking a core aspect of the language. Goddamn it, it's the
 constructor! IMHO, everybody expects to construct things.. using
 constructors (unless there's a restriction like not being allowed to
 construct stuff anytime, but only under certain conditions, which was =



 the
 case with SoundSources).

 Factory methods should, again just in my opinion, be used to exploit
 polymorphism or enhance the system design, not to solve problems of the
 language dealing with method overloading. It's just.. creepy. Is it =20
 just me?
 I feel like I'm giving too much and barely getting one thirth in =20
 return...

Steven: This is great answer to the question you asked me (and I did =


 not answer) about the "clear meaning" of constructors in a PL. (Else, =


 let's just get rid of them alltogether, and of the notion too, and just=


 use object factories?)

A factory method *forwards to* a constructor. You are not losing the =20 construction aspect (or its special qualities), in fact, it can sit besid=

 a constructor.
=20
 There are many cases where a constructor is not as clear as a factory =20
 method.  Take for std.array.Appender.  The constructor that takes an =20
 initial array as input:
=20
 int[1024] buf;
=20
 auto app =3D Appender!(int[])(buf);
=20
 vs.
=20
 auto app =3D appender(buf);
=20
 The factory method looks clearer to me.
=20
 I think it's really subjective depending on the situation.  All I'm sayin=

 is that there is nothing IMO that rules out factory methods as =20
 construction means on principal.

Good example, indeed. I must admit I used a load of factory --methods-- pla= in functions in a parsing lib, just to avoid repetitive "new"'s, imo simply= obfuscating the code: auto digit =3D klass("0-9"); auto natural =3D oneOrMore(digit); auto integer =3D composition(option(literal("-")), natural); instead of: auto digit =3D new Klass("0-9"); auto natural =3D new OneOrMore(digit); auto integer =3D new Composition(new Option(new Literal("-")), natural); ;-) Denis -- -- -- -- -- -- -- vit esse estrany =E2=98=A3 spir.wikidot.com
Dec 30 2010