www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - typedef

reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
I tried typedef in two instances, to sadly conclude that it's not useful 
as a true abstraction mechanism. The good news is that it's very close 
to being useful.

Ideally, typedef should provide a completely parallel type with the type 
under the typedef, as if you sat down and wrote it from scratch. 
Unfortunately, today typedef is just about equivariant with the type it 
comes from:

void main()
{
     typedef int IDType;
     IDType id = 5;
     int x = id;
}

This compiles and runs flag-free. An IDType accepts any int without any 
fuss, and furthermore an int accepts an IDType no problem. As such, 
typedef introduces neither a supertype nor a subtype of int.

I believe this behavior is unhelpful as it prevents IDType from being a 
true abstraction mechanism. What should happen is that only this should 
compile:

void main()
{
     typedef int IDType;
     auto id = IDType(5);
     int x = id;
}

So the literals of type IDType have the form IDType(n). That way it's 
visible that we want to deal in IDType objects. I think it's ok that the 
int can be be teased out of an IDType without an explicit conversion. 
Requiring a cast would be a tad too rigid. So with the new rules in 
place we defined a sort of subtype of int.

This works even better with classes. Consider exceptions for instance:

typedef Exception MyException;

void main()
{
     try
     {
         throw new MyException("x");
     }
     catch(MyException e) { writeln("a"); }
     catch (Exception e)  { writeln("b"); }
}

This code doesn't compile with:

Error: catch at ./test.d(19) hides catch at ./test.d(20)

So to the compiler typedefs are today too much the same as the source type.


Andrei
Mar 06 2009
next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Fri, 06 Mar 2009 20:47:40 +0300, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org> wrote:

 I tried typedef in two instances, to sadly conclude that it's not useful  
 as a true abstraction mechanism. The good news is that it's very close  
 to being useful.

 Ideally, typedef should provide a completely parallel type with the type  
 under the typedef, as if you sat down and wrote it from scratch.  
 Unfortunately, today typedef is just about equivariant with the type it  
 comes from:

 void main()
 {
      typedef int IDType;
      IDType id = 5;
      int x = id;
 }

 This compiles and runs flag-free. An IDType accepts any int without any  
 fuss, and furthermore an int accepts an IDType no problem. As such,  
 typedef introduces neither a supertype nor a subtype of int.

 I believe this behavior is unhelpful as it prevents IDType from being a  
 true abstraction mechanism. What should happen is that only this should  
 compile:

 void main()
 {
      typedef int IDType;
      auto id = IDType(5);
      int x = id;
 }

 So the literals of type IDType have the form IDType(n). That way it's  
 visible that we want to deal in IDType objects. I think it's ok that the  
 int can be be teased out of an IDType without an explicit conversion.  
 Requiring a cast would be a tad too rigid. So with the new rules in  
 place we defined a sort of subtype of int.

 This works even better with classes. Consider exceptions for instance:

 typedef Exception MyException;

 void main()
 {
      try
      {
          throw new MyException("x");
      }
      catch(MyException e) { writeln("a"); }
      catch (Exception e)  { writeln("b"); }
 }

 This code doesn't compile with:

 Error: catch at ./test.d(19) hides catch at ./test.d(20)

 So to the compiler typedefs are today too much the same as the source  
 type.


 Andrei

It seems that typedef defined a some kind of sub-type: class Foo {}; typedef Foo Bar; // a //class Bar : Foo {} // b void main() { Bar bar; Foo foo; foo = bar; // fine bar = foo; // error } <OT> Note that I get different error messages in cases a and b: a - Error: cannot implicitly convert expression (foo) of type test.Foo to Bar b - Error: cannot implicitly convert expression (foo) of type test.Foo to test.Bar Is it a bug? Shoudn't the two report the same error (i.e. "test.Bar")? Does it affect mangling? </OT> It works the same for built-in types: alias int Foo; typedef Foo Bar; void main() { Bar bar; Foo foo; foo = bar; // fine bar = foo; // error } Looks sensible and consistent to me.
Mar 06 2009
prev sibling parent reply "Tim M" <a b.com> writes:
On Sat, 07 Mar 2009 06:47:40 +1300, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 I tried typedef in two instances, to sadly conclude that it's not useful  
 as a true abstraction mechanism. The good news is that it's very close  
 to being useful.

 Ideally, typedef should provide a completely parallel type with the type  
 under the typedef, as if you sat down and wrote it from scratch.  
 Unfortunately, today typedef is just about equivariant with the type it  
 comes from:

 void main()
 {
      typedef int IDType;
      IDType id = 5;
      int x = id;
 }

 This compiles and runs flag-free. An IDType accepts any int without any  
 fuss, and furthermore an int accepts an IDType no problem. As such,  
 typedef introduces neither a supertype nor a subtype of int.

 I believe this behavior is unhelpful as it prevents IDType from being a  
 true abstraction mechanism. What should happen is that only this should  
 compile:

 void main()
 {
      typedef int IDType;
      auto id = IDType(5);
      int x = id;
 }

 So the literals of type IDType have the form IDType(n). That way it's  
 visible that we want to deal in IDType objects. I think it's ok that the  
 int can be be teased out of an IDType without an explicit conversion.  
 Requiring a cast would be a tad too rigid. So with the new rules in  
 place we defined a sort of subtype of int.

 This works even better with classes. Consider exceptions for instance:

 typedef Exception MyException;

 void main()
 {
      try
      {
          throw new MyException("x");
      }
      catch(MyException e) { writeln("a"); }
      catch (Exception e)  { writeln("b"); }
 }

 This code doesn't compile with:

 Error: catch at ./test.d(19) hides catch at ./test.d(20)

 So to the compiler typedefs are today too much the same as the source  
 type.


 Andrei

I've never really found typedefs of any use. You say it makes it clearer that you are dealing with IDType objects. But they are not objects. If you had used objects you could have defined the constuctor to get the syntax you are looking for because taking away this syntax: IDType id = 5; Should also remove the following syntaxes to keep D simple: *= += -+ %= etc.. So you end up with a type that stores int data but cant be used for arithmetic. What good would that be keeping in mind that D already has enums?
Mar 06 2009
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Tim M wrote:
 On Sat, 07 Mar 2009 06:47:40 +1300, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:
 
 I tried typedef in two instances, to sadly conclude that it's not 
 useful as a true abstraction mechanism. The good news is that it's 
 very close to being useful.

I've never really found typedefs of any use. You say it makes it clearer that you are dealing with IDType objects. But they are not objects. If you had used objects you could have defined the constuctor to get the syntax you are looking for because taking away this syntax: IDType id = 5; Should also remove the following syntaxes to keep D simple: *= += -+ %= etc.. So you end up with a type that stores int data but cant be used for arithmetic. What good would that be keeping in mind that D already has enums?

Well I was wondering whether others have success stories about typedef to share. Here's an example that would have made it very handy for me to use a good typedef. I have a machine translation app that deals with sentences in two languages. Furthermore, in the target language there are several hypotheses per sentence. I undergo intensive manipulation of these things, and found bugs because I confuse e.g. the IDs of sentences in different languages. So I index in a hash for one language with an ID from another language, which is a bug. So I though, alright, let me typedef IDs so they never mix up: typedef uint SourceSntID; typedef uint TargetSntID; typedef uint HypothesisID; Things did improve considerably but I found it annoying that these IDs, which are supposed to be essentially independent types, convert to and from uint at the drop of a hat. I could define structs for them but that's onerous - like dozens of lines instead of one line. Andrei
Mar 06 2009
prev sibling parent "Tim M" <a b.com> writes:
On Sat, 07 Mar 2009 17:04:56 +1300, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 Tim M wrote:
 On Sat, 07 Mar 2009 06:47:40 +1300, Andrei Alexandrescu  
 <SeeWebsiteForEmail erdani.org> wrote:

 I tried typedef in two instances, to sadly conclude that it's not  
 useful as a true abstraction mechanism. The good news is that it's  
 very close to being useful.

clearer that you are dealing with IDType objects. But they are not objects. If you had used objects you could have defined the constuctor to get the syntax you are looking for because taking away this syntax: IDType id = 5; Should also remove the following syntaxes to keep D simple: *= += -+ %= etc.. So you end up with a type that stores int data but cant be used for arithmetic. What good would that be keeping in mind that D already has enums?

Well I was wondering whether others have success stories about typedef to share. Here's an example that would have made it very handy for me to use a good typedef. I have a machine translation app that deals with sentences in two languages. Furthermore, in the target language there are several hypotheses per sentence. I undergo intensive manipulation of these things, and found bugs because I confuse e.g. the IDs of sentences in different languages. So I index in a hash for one language with an ID from another language, which is a bug. So I though, alright, let me typedef IDs so they never mix up: typedef uint SourceSntID; typedef uint TargetSntID; typedef uint HypothesisID; Things did improve considerably but I found it annoying that these IDs, which are supposed to be essentially independent types, convert to and from uint at the drop of a hat. I could define structs for them but that's onerous - like dozens of lines instead of one line. Andrei

Derelict uses typedef for functions and alias for others by convention: "The declarations of the function pointers should be typedefed and should have the following syntax:" http://svn.dsource.org/projects/derelict/trunk/docs/derelictify.html It seems to be in strong use for where an alias could do the job just fine: http://www.google.com/search?rls=en&rls=en&q=site:dsource.org+typedef&sourceid=opera&ie=utf-8&oe=utf-8
Mar 06 2009