discrimination of constructors with same number of parameters

2010-12-30 Thread spir
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



Re: discrimination of constructors with same number of parameters

2010-12-30 Thread Jonathan M Davis
On Thursday 30 December 2010 02:50:55 spir 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)
 
 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.

This is a common issue in programming languages which allow for function 
overloading. Type is what's used to determine which overload to use. If you 
want 
to have two overloads that use the same types, then you're out of luck. That 
generally means either creating another function or creating a new type (and 
creating a new type is generally overkill). typedef is definitely on the way 
out, 
so that's not a solution, and it would be a pretty fragile one IMHO anyway. So, 
what you would do normally is create another function with another name.

In the case of constructors, you can't do that. So, if you really need it, you 
create static factory methods which return a new value of that type. The 
factory 
methods can have different names.

But function overloading works on type. So, if two overloads would crash, you 
either have to create a new function with a new name, or you have to create a 
new type. It may be annoying sometimes, but it's still a whale of a lot better 
than not having function overloading at all - as is the case with languages 
such 
as C and Go.

- Jonathan M Davis


Re: discrimination of constructors with same number of parameters

2010-12-30 Thread bearophile
Jonathan M Davis:

 typedef is definitely on the way out, so that's not a solution,

typedef is deprecated (because its semantics is not flexible enough and because 
it doesn't play well with object oriented language features), but I have a real 
need for something like it. Andrei has discussed about a Phobos-based typedef 
replacement (based on structs + alias this), but nothing concrete has come out 
yet. I hope to see something to solve problems like spir ones.


 and it would be a pretty fragile one IMHO anyway.

Please, explain better.

Bye,
bearophile


Re: discrimination of constructors with same number of parameters

2010-12-30 Thread Guilherme Vieira
On Thu, Dec 30, 2010 at 9:24 AM, bearophile bearophileh...@lycos.comwrote:

 Jonathan M Davis:

  typedef is definitely on the way out, so that's not a solution,

 typedef is deprecated (because its semantics is not flexible enough and
 because it doesn't play well with object oriented language features), but I
 have a real need for something like it. Andrei has discussed about a
 Phobos-based typedef replacement (based on structs + alias this), but
 nothing concrete has come out yet. I hope to see something to solve problems
 like spir ones.


  and it would be a pretty fragile one IMHO anyway.

 Please, explain better.

 Bye,
 bearophile


As far as I know, typedef was a form of discriminated alias. I don't know
the reasons for its deprecation. It just occurred to me that D's typedefs +
templates could be quite handy in this case.

Consider:

struct semantic_wrapper(T)
{
this(T value) { this.value = value; }

T value;
}

typedef semantic_wrapper!(int) Position;
typedef semantic_wrapper!(size_t) Count;
typedef semantic_wrapper!(string) Filename;
typedef semantic_wrapper!(string) DirPath;

void func(Position pos) { ... }
void func(Count c) { ... }
void func(Filename fname) { ... }
void func(DirPath dir) { ... }

void main()
{
func(Position(1)); // calls first overload
func(Count(5)); // calls second
func(Filename(file.txt)); // third
func(DirPath(/dev/null)); // fourth

func(1); // fails
func(blah); // fails
}


Requires a little more typing, but sometimes it can be better than creating
a new function name (which can get extra-big, non-telling or both) or than
creating factory methods (which I personally dislike, although it's just a
matter of taste most of the time; sometimes you may want to instantiate from
inside a template and classes needing factories would not work, for example,
but one could argue on the validity of this anytime).

Just giving my 2 cents. Dunno if I missed some detail.

-- 
Atenciosamente / Sincerely,
Guilherme (n2liquid) Vieira


Re: discrimination of constructors with same number of parameters

2010-12-30 Thread sybrandy

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


Re: discrimination of constructors with same number of parameters

2010-12-30 Thread bearophile
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


Re: discrimination of constructors with same number of parameters

2010-12-30 Thread spir
On Thu, 30 Dec 2010 03:01:52 -0800
Jonathan M Davis jmdavisp...@gmx.com wrote:

 On Thursday 30 December 2010 02:50:55 spir 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)
  
  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.
 
 This is a common issue in programming languages which allow for function 
 overloading. Type is what's used to determine which overload to use. If you 
 want 
 to have two overloads that use the same types, then you're out of luck. That 
 generally means either creating another function or creating a new type (and 
 creating a new type is generally overkill). typedef is definitely on the way 
 out, 
 so that's not a solution, and it would be a pretty fragile one IMHO anyway. 
 So, 
 what you would do normally is create another function with another name.
 
 In the case of constructors, you can't do that. So, if you really need it, 
 you 
 create static factory methods which return a new value of that type. The 
 factory 
 methods can have different names.
 
 But function overloading works on type. So, if two overloads would crash, you 
 either have to create a new function with a new name, or you have to create a 
 new type. It may be annoying sometimes, but it's still a whale of a lot 
 better 
 than not having function overloading at all - as is the case with languages 
 such 
 as C and Go.

Thank you very much for this clear explanation, jonathan.

Could someone expand on the reason(s) why typedef is definitely on the way 
out? I can see pratical and conceptual advantages of having exclusive type 
defs; I mean a variant of alias for which the aliased type cannot be used where 
the newly type is expected.
1. Practically, this solves the above issue. For instance typedef string Name 
would discriminate two constructors.
2. Conceptually, a Name for instance is nothing like a string in general (and a 
message in particular). Letting the programmer use the proper is a great gain 
in code clarity. (See Pascal like languages for this practice.)
Additionally, this would avoid bugs where an element of a given conceptual type 
is used in place of another (and both happen to be the same machine type, or be 
compatible via casting). I have no idea how common such bugs are; but the 
situation is similar to frequent bugs in dynamic languages: you can pass 
anything to a func, thus if the operations performed there happen to accept 
what is passed, the bug is silently ignored.

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

spir.wikidot.com



Re: discrimination of constructors with same number of parameters

2010-12-30 Thread spir
On Thu, 30 Dec 2010 10:07:58 -0200
Guilherme Vieira n2.nitro...@gmail.com wrote:

 On Thu, Dec 30, 2010 at 9:24 AM, bearophile bearophileh...@lycos.comwrote:
 
  Jonathan M Davis:
 
   typedef is definitely on the way out, so that's not a solution,
 
  typedef is deprecated (because its semantics is not flexible enough and
  because it doesn't play well with object oriented language features), but I
  have a real need for something like it. Andrei has discussed about a
  Phobos-based typedef replacement (based on structs + alias this), but
  nothing concrete has come out yet. I hope to see something to solve problems
  like spir ones.
 
 
   and it would be a pretty fragile one IMHO anyway.
 
  Please, explain better.
 
  Bye,
  bearophile
 
 
 As far as I know, typedef was a form of discriminated alias. I don't know
 the reasons for its deprecation. It just occurred to me that D's typedefs +
 templates could be quite handy in this case.
 
 Consider:
 
 struct semantic_wrapper(T)
 {
 this(T value) { this.value = value; }
 
 T value;
 }
 
 typedef semantic_wrapper!(int) Position;
 typedef semantic_wrapper!(size_t) Count;
 typedef semantic_wrapper!(string) Filename;
 typedef semantic_wrapper!(string) DirPath;
 
 void func(Position pos) { ... }
 void func(Count c) { ... }
 void func(Filename fname) { ... }
 void func(DirPath dir) { ... }
 
 void main()
 {
 func(Position(1)); // calls first overload
 func(Count(5)); // calls second
 func(Filename(file.txt)); // third
 func(DirPath(/dev/null)); // fourth
 
 func(1); // fails
 func(blah); // fails
 }
 
 
 Requires a little more typing, but sometimes it can be better than creating
 a new function name (which can get extra-big, non-telling or both) or than
 creating factory methods (which I personally dislike, although it's just a
 matter of taste most of the time; sometimes you may want to instantiate from
 inside a template and classes needing factories would not work, for example,
 but one could argue on the validity of this anytime).
 
 Just giving my 2 cents. Dunno if I missed some detail.

I would have several needs for wrapper structs or classes like yours.

I like very much your proper use of proper terms (here type names): I myself 
make a difference between machine types and conceptual types. For instance, I 
always define:
// standard type aliases
alias sizediff_t Ordinal;   // index, which one
alias size_t Cardinal;  // count, how many
and use exclusively Ordinal and Cardinal in code. But this would be better with 
a discriminating instruction. the issue, then, is with literals:
* Either such types must be defined by the language, and literals are properly 
cast (like in the case of int vs uint vs dchar for instance).
* Or literal casting works even with discriminating/exclusive types (which 
don't mutually cast); meaning eg a string literal would be accepted where a 
Name is expected even if string's do not cast to Name's.

What do you think of the points I mention in favor of a discriminating alias 
in a // post?

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

spir.wikidot.com



Re: discrimination of constructors with same number of parameters

2010-12-30 Thread Lutger Blijdestijn
Guilherme Vieira wrote:

 On Thu, Dec 30, 2010 at 9:24 AM, bearophile
 bearophileh...@lycos.comwrote:
 
 Jonathan M Davis:

  typedef is definitely on the way out, so that's not a solution,

 typedef is deprecated (because its semantics is not flexible enough and
 because it doesn't play well with object oriented language features), but
 I have a real need for something like it. Andrei has discussed about a
 Phobos-based typedef replacement (based on structs + alias this), but
 nothing concrete has come out yet. I hope to see something to solve
 problems like spir ones.


  and it would be a pretty fragile one IMHO anyway.

 Please, explain better.

 Bye,
 bearophile

 
 As far as I know, typedef was a form of discriminated alias. I don't
 know the reasons for its deprecation. It just occurred to me that D's
 typedefs + templates could be quite handy in this case.
 
 Consider:
 
 struct semantic_wrapper(T)
 {
 this(T value) { this.value = value; }
 
 T value;
 }
 
 typedef semantic_wrapper!(int) Position;
 typedef semantic_wrapper!(size_t) Count;
 typedef semantic_wrapper!(string) Filename;
 typedef semantic_wrapper!(string) DirPath;
 
 void func(Position pos) { ... }
 void func(Count c) { ... }
 void func(Filename fname) { ... }
 void func(DirPath dir) { ... }
 
 void main()
 {
 func(Position(1)); // calls first overload
 func(Count(5)); // calls second
 func(Filename(file.txt)); // third
 func(DirPath(/dev/null)); // fourth
 
 func(1); // fails
 func(blah); // fails
 }
 
 
 Requires a little more typing, but sometimes it can be better than
 creating a new function name (which can get extra-big, non-telling or
 both) or than creating factory methods (which I personally dislike,
 although it's just a matter of taste most of the time; sometimes you may
 want to instantiate from inside a template and classes needing factories
 would not work, for example, but one could argue on the validity of this
 anytime).
 
 Just giving my 2 cents. Dunno if I missed some detail.
 

Here is an attempt to implement it, still needs support for writeln and 
lacks some details:

import std.stdio;

mixin template Newtype(T, string typename)
{
mixin(struct  ~ typename ~  { alias base this;  ~ T.stringof ~
   base;  @disable void opAssign( ~ T.stringof ~ ) {} });
}

mixin Newtype!(int, Position);
mixin Newtype!(size_t, Count);
mixin Newtype!(string, FileName);
mixin Newtype!(string, DirPath);

void func(Position pos) { writeln(position: , pos.base ); }
void func(Count c) { writeln(count:, c.base); }
void func(FileName fname) { writeln(filename:, fname.base); }
void func(DirPath dir) { writeln(dirpath:, dir.base); }

void func2(int pos) { writeln(position: , pos); }

void main()
{
  
func(Position(1)); // calls first overload
func2(Position(1)); // implicit conversion to int with alias this
func(Count(5)); // calls second
func(FileName(file.txt)); // third
func(DirPath(/dev/null)); // fourth
func(1); // fails
func(blah); // fails

auto p = Position(1);
p = 2; // fails
p.base = 4; // ok, explicit
}


Re: discrimination of constructors with same number of parameters

2010-12-30 Thread Lutger Blijdestijn
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. 


Re: discrimination of constructors with same number of parameters

2010-12-30 Thread spir
On Thu, 30 Dec 2010 14:39:02 +0100
Lutger Blijdestijn lutger.blijdest...@gmail.com wrote:

 Guilherme Vieira wrote:
 
  On Thu, Dec 30, 2010 at 9:24 AM, bearophile
  bearophileh...@lycos.comwrote:
  
  Jonathan M Davis:
 
   typedef is definitely on the way out, so that's not a solution,
 
  typedef is deprecated (because its semantics is not flexible enough and
  because it doesn't play well with object oriented language features), but
  I have a real need for something like it. Andrei has discussed about a
  Phobos-based typedef replacement (based on structs + alias this), but
  nothing concrete has come out yet. I hope to see something to solve
  problems like spir ones.
 
 
   and it would be a pretty fragile one IMHO anyway.
 
  Please, explain better.
 
  Bye,
  bearophile
 
  
  As far as I know, typedef was a form of discriminated alias. I don't
  know the reasons for its deprecation. It just occurred to me that D's
  typedefs + templates could be quite handy in this case.
  
  Consider:
  
  struct semantic_wrapper(T)
  {
  this(T value) { this.value = value; }
  
  T value;
  }
  
  typedef semantic_wrapper!(int) Position;
  typedef semantic_wrapper!(size_t) Count;
  typedef semantic_wrapper!(string) Filename;
  typedef semantic_wrapper!(string) DirPath;
  
  void func(Position pos) { ... }
  void func(Count c) { ... }
  void func(Filename fname) { ... }
  void func(DirPath dir) { ... }
  
  void main()
  {
  func(Position(1)); // calls first overload
  func(Count(5)); // calls second
  func(Filename(file.txt)); // third
  func(DirPath(/dev/null)); // fourth
  
  func(1); // fails
  func(blah); // fails
  }
  
  
  Requires a little more typing, but sometimes it can be better than
  creating a new function name (which can get extra-big, non-telling or
  both) or than creating factory methods (which I personally dislike,
  although it's just a matter of taste most of the time; sometimes you may
  want to instantiate from inside a template and classes needing factories
  would not work, for example, but one could argue on the validity of this
  anytime).
  
  Just giving my 2 cents. Dunno if I missed some detail.
  
 
 Here is an attempt to implement it, still needs support for writeln and 
 lacks some details:
 
 import std.stdio;
 
 mixin template Newtype(T, string typename)
 {
 mixin(struct  ~ typename ~  { alias base this;  ~ T.stringof ~
base;  @disable void opAssign( ~ T.stringof ~ ) {} });
 }
 
 mixin Newtype!(int, Position);
 mixin Newtype!(size_t, Count);
 mixin Newtype!(string, FileName);
 mixin Newtype!(string, DirPath);
 
 void func(Position pos) { writeln(position: , pos.base ); }
 void func(Count c) { writeln(count:, c.base); }
 void func(FileName fname) { writeln(filename:, fname.base); }
 void func(DirPath dir) { writeln(dirpath:, dir.base); }
 
 void func2(int pos) { writeln(position: , pos); }
 
 void main()
 {
   
 func(Position(1)); // calls first overload
 func2(Position(1)); // implicit conversion to int with alias this
 func(Count(5)); // calls second
 func(FileName(file.txt)); // third
 func(DirPath(/dev/null)); // fourth
 func(1); // fails
 func(blah); // fails
 
 auto p = Position(1);
 p = 2; // fails
 p.base = 4; // ok, explicit
 }

I like very much the template mixin solution. Would there be any difference in 
inheriting an interface (or even a plain type)?
Also, can one presently rewrite this in D without _string_ mixin inside the 
template? (Else this solution is simply not acceptable for me: I'm allergic to 
code in strings; but don't ask me why ;-)

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

spir.wikidot.com



Re: discrimination of constructors with same number of parameters

2010-12-30 Thread spir
On Thu, 30 Dec 2010 08:15:51 -0500
bearophile bearophileh...@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



Re: discrimination of constructors with same number of parameters

2010-12-30 Thread Guilherme Vieira
On Thu, Dec 30, 2010 at 12:18 PM, spir denis.s...@gmail.com wrote:

 On Thu, 30 Dec 2010 08:15:51 -0500
 bearophile bearophileh...@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


Re: discrimination of constructors with same number of parameters

2010-12-30 Thread sybrandy

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


Re: discrimination of constructors with same number of parameters

2010-12-30 Thread Lutger Blijdestijn
Guilherme Vieira wrote:

 On Thu, Dec 30, 2010 at 12:18 PM, spir denis.s...@gmail.com wrote:
 
 On Thu, 30 Dec 2010 08:15:51 -0500
 bearophile bearophileh...@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;
 


Re: discrimination of constructors with same number of parameters

2010-12-30 Thread Lutger Blijdestijn
spir wrote:

(...)
 
 I like very much the template mixin solution. Would there be any
 difference in inheriting an interface (or even a plain type)? Also, can
 one presently rewrite this in D without _string_ mixin inside the
 template? (Else this solution is simply not acceptable for me: I'm
 allergic to code in strings; but don't ask me why ;-)
 
 Denis
 -- -- -- -- -- -- --
 vit esse estrany ☣
 
 spir.wikidot.com

Compared to inheriting interfaces: 
- works with primitive and struct types
- no dynamic polymorphism
- no performance penalty, likely less code size

You can do it without string mixins by explicitly writing the code the mixin 
automates. This should also work more or less, but I find it less clear:

struct NewType(T, string typename)
{
alias T this;
T base;
@disable void opAssign(T) {}
}

The typename parameter is arbitrary here, just to create a seperate type for 
each possible value. It might as well be an integer. 

alias NewType!(int, Position) Position;
void func(Position position) { ... }

or used directly:

void func(Newtype!(int, Position) position) { ... }



Re: discrimination of constructors with same number of parameters

2010-12-30 Thread Steven Schveighoffer

On Thu, 30 Dec 2010 05:50:55 -0500, spir denis.s...@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


Re: discrimination of constructors with same number of parameters

2010-12-30 Thread Jérôme M. Berger
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:jeber...@free.fr
http://jeberger.free.fr
Jabber: jeber...@jabber.fr



signature.asc
Description: OpenPGP digital signature


Re: discrimination of constructors with same number of parameters

2010-12-30 Thread spir
On Thu, 30 Dec 2010 17:10:00 +0100
Jérôme M. Berger jeber...@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



Re: discrimination of constructors with same number of parameters

2010-12-30 Thread Steven Schveighoffer

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


On Thu, 30 Dec 2010 17:10:00 +0100
Jérôme M. Berger jeber...@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


Re: discrimination of constructors with same number of parameters

2010-12-30 Thread Guilherme Vieira
On Thu, Dec 30, 2010 at 3:19 PM, Steven Schveighoffer
schvei...@yahoo.comwrote:

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

  On Thu, 30 Dec 2010 17:10:00 +0100
 Jérôme M. Berger jeber...@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!


Re: discrimination of constructors with same number of parameters

2010-12-30 Thread spir
On Thu, 30 Dec 2010 16:33:39 -0200
Guilherme Vieira n2.nitro...@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



Re: discrimination of constructors with same number of parameters

2010-12-30 Thread Steven Schveighoffer

On Thu, 30 Dec 2010 14:08:49 -0500, spir denis.s...@gmail.com wrote:


On Thu, 30 Dec 2010 16:33:39 -0200
Guilherme Vieira n2.nitro...@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