December 30, 2010
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?
> 
> auto obj = new Foo([1, 2, 3], "blah");
> 
> Is "blah" a filename or a message?
> 
--> Error, Foo (int[], string) does not exist.

> 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 ;)
> 
auto obj = new Foo ([1, 2, 3], Filename ("blah"));
auto obj = new Foo ([1, 2, 3], Message ("blah"));

		Jerome
-- 
mailto:jeberger@free.fr
http://jeberger.free.fr
Jabber: jeberger@jabber.fr



December 30, 2010
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 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?
> > 
> --> Error, Foo (int[], string) does not exist.

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.

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

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.

> > The code becomes crystal clear.  Reduce verbosity as you see fit ;)
> > 
> auto obj = new Foo ([1, 2, 3], Filename ("blah"));
> 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 ;-)

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

spir.wikidot.com

December 30, 2010
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  
>> 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?
>> >
>> --> Error, Foo (int[], string) does not exist.
>
> 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  
>> filename
>> > auto obj = Foo.createWithMessage([1,2,3], "blah"); // "blah" is a  
>> message
>
> 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], Filename ("blah"));
>> 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
December 30, 2010
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érôme 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?
>>> >
>>> > auto obj = new Foo([1, 2, 3], "blah");
>>> >
>>> > Is "blah" a filename or a message?
>>> >
>>> --> Error, Foo (int[], string) does not exist.
>>>
>>
>> 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
>>> filename
>>> > auto obj = Foo.createWithMessage([1,2,3], "blah"); // "blah" is a
>>> message
>>>
>>
>> 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], Filename ("blah"));
>>> 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
>

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 use is almost never a good idea:

struct MyObjectWithFileName // this
{ // is
    this(string fname) { obj = MyObject.createWithFilename(fname); } // so
    MyObject obj; // much
} // typing!

// later ...
manager.create!(MyObjectWithFileName)("filename.txt"); // phew..! now repeat
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...

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


December 30, 2010
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?)

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

spir.wikidot.com

December 30, 2010
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
December 30, 2010
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:
> 
> > 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.

Good example, indeed. I must admit I used a load of factory --methods-- plain functions in a parsing lib, just to avoid repetitive "new"'s, imo simply obfuscating the code:
	auto digit = klass("0-9");
	auto natural = oneOrMore(digit);
	auto integer = composition(option(literal("-")), natural);
instead of:
	auto digit = new Klass("0-9");
	auto natural = new OneOrMore(digit);
	auto integer = new Composition(new Option(new Literal("-")), natural);
;-)

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

spir.wikidot.com

1 2
Next ›   Last »