## digitalmars.D - typedefs are useless

• Steven Schveighoffer (28/28) Dec 03 2007 This may have arisen somewhere before, but...
• Kirk McDonald (7/52) Dec 03 2007 You can typedef things other than integral types.
• Steven Schveighoffer (18/19) Dec 03 2007 OK, so that is one difference between enum and typedef (I forgot about t...
• Matti Niemenmaa (6/14) Dec 03 2007
• BCS (19/23) Dec 03 2007 [...]
• Peter C. Chapin (19/30) Dec 03 2007 FWIW, Ada solves this problem by considering literals in a special type
• John Demme (6/45) Dec 03 2007 Behavior like this would come about as a result of polysemous values bei...
• BLS (9/48) Dec 04 2007 IMO, in this case ADA is slicker than D.
• Peter C. Chapin (10/14) Dec 04 2007 Actually that's
• BLS (5/16) Dec 04 2007 Thanks Peter, that's it.
"Steven Schveighoffer" <schveiguy yahoo.com> writes:
```This may have arisen somewhere before, but...

Let's say I want a way to create a type that's like a long, but is not
implicitly convertable from a long.

I can do:

typedef long mytype;

However, I can't create literals of this type.  so if I want to initialize a
mytype value to 6, I have to do:

mytype x = cast(mytype)6L;

And if I want to scale a mytype value, I can do:

x *= 6;

but if I wanted to create a scaled value of another mytype value, this is
illegal:

x = y * 6;

So in order to get this to work, I have to do:

x = y * cast(mytype)6;

which is really cumersome, when what I want is a integral type that is
semantically different from the integral type it's based on.  But I want
literals of the base type to implicitly convert to my type so I don't have
these ugly casts.

Note that an enum is EXACTLY the same as a typedef, with the added feature
that an enum can have const values of itself accessible as members.  In that
regard, why would anyone use a typedef when it is a less-functional version
of an enum?

in short, I would like the compiler to auto promote a literal to a typedef
that was based on that literal's type.  Not sure if this is feasible, but
without something like this, there is no reason to have a typedef keyword.

-Steve
```
Dec 03 2007
Kirk McDonald <kirklin.mcdonald gmail.com> writes:
```Steven Schveighoffer wrote:
This may have arisen somewhere before, but...

Let's say I want a way to create a type that's like a long, but is not
implicitly convertable from a long.

I can do:

typedef long mytype;

However, I can't create literals of this type.  so if I want to initialize a
mytype value to 6, I have to do:

mytype x = cast(mytype)6L;

And if I want to scale a mytype value, I can do:

x *= 6;

but if I wanted to create a scaled value of another mytype value, this is
illegal:

x = y * 6;

So in order to get this to work, I have to do:

x = y * cast(mytype)6;

which is really cumersome, when what I want is a integral type that is
semantically different from the integral type it's based on.  But I want
literals of the base type to implicitly convert to my type so I don't have
these ugly casts.

Note that an enum is EXACTLY the same as a typedef, with the added feature
that an enum can have const values of itself accessible as members.  In that
regard, why would anyone use a typedef when it is a less-functional version
of an enum?

in short, I would like the compiler to auto promote a literal to a typedef
that was based on that literal's type.  Not sure if this is feasible, but
without something like this, there is no reason to have a typedef keyword.

-Steve

You can typedef things other than integral types.

--
Kirk McDonald
http://kirkmcdonald.blogspot.com
Pyd: Connecting D and Python
http://pyd.dsource.org
```
Dec 03 2007
"Steven Schveighoffer" <schveiguy yahoo.com> writes:
```"Kirk McDonald" wrote
You can typedef things other than integral types.

OK, so that is one difference between enum and typedef (I forgot about that,
because I was so narrowly focused on my problem at hand).

but the same thing can be said for non-integral types:

typedef mytype float;

mytype x = 1.0 // does not work.

This typedef is just as useless as an integral typedef.  The fact that enum
does not exist for a floating point type does not justify the existance of
typedef.  You can use alias to make a more useful type, but you then do not
have the restrictions I am looking for.

I look at a typedef as a useful way to create a derived type from a builtin
type such that it is implicitly convertable to the base type, but not in
reverse (similar to deriving from a base class).  However, without the
ability to specify literals, or even extend the syntax to be able to specify
them (i.e. some way to make 1mt mean cast(mytype)1), the type is only useful
as an enumeration, as any mathematical manipulation will require lots of
casting statements, just like an enum.

-Steve
```
Dec 03 2007
```Steven Schveighoffer wrote:
I can do:

typedef long mytype;

However, I can't create literals of this type.  so if I want to initialize a
mytype value to 6, I have to do:

mytype x = cast(mytype)6L;

<snip>

Complained about in Issue 1335 and earlier. This is why I use aliases instead of
typedefs in all but the shortest of programs.

--
E-mail address: matti.niemenmaa+news, domain is iki (DOT) fi
```
Dec 03 2007
```Steven Schveighoffer wrote:
This may have arisen somewhere before, but...

Let's say I want a way to create a type that's like a long, but is not
implicitly convertable from a long.

[...]

this is getting really close to something I have wanted for some time:

typedef real MyReal
{
// stuff
}

it would be like a struct that inherits from a primitive type. The this
would be of the base type and you wouldn't be allowed to add any
members. However this would allow you to do things like overload the
operators. The one things in particular I would like to do would be to
overload just the typing of the operators. This would result in the
implementation of + (for instance) being the same as + on the underlying
type, but the type of the result would be redefined. The point of this
would be to allow a program to restrict the type that could be used. A
concrete use case would be a SIUinits type that would, at compile time,
verify unit correctness, but in the end would result in exactly the same
code as if bare FP values were used.

Thoughts?
```
Dec 03 2007
Bill Baxter <dnewsgroup billbaxter.com> writes:
```BCS wrote:
Steven Schveighoffer wrote:
This may have arisen somewhere before, but...

Let's say I want a way to create a type that's like a long, but is not
implicitly convertable from a long.

[...]

this is getting really close to something I have wanted for some time:

typedef real MyReal
{
// stuff
}

it would be like a struct that inherits from a primitive type. The this
would be of the base type and you wouldn't be allowed to add any
members. However this would allow you to do things like overload the
operators. The one things in particular I would like to do would be to
overload just the typing of the operators. This would result in the
implementation of + (for instance) being the same as + on the underlying
type, but the type of the result would be redefined. The point of this
would be to allow a program to restrict the type that could be used. A
concrete use case would be a SIUinits type that would, at compile time,
verify unit correctness, but in the end would result in exactly the same
code as if bare FP values were used.

Thoughts?

Why not just make that behavior triggered when you do

struct MyReal : real
{
...
}

?
```
Dec 03 2007
"Steven Schveighoffer" <schveiguy yahoo.com> writes:
```"Bill Baxter" wrote
BCS wrote:
Steven Schveighoffer wrote:
This may have arisen somewhere before, but...

Let's say I want a way to create a type that's like a long, but is not
implicitly convertable from a long.

[...]

this is getting really close to something I have wanted for some time:

typedef real MyReal
{
// stuff
}

it would be like a struct that inherits from a primitive type. The this
would be of the base type and you wouldn't be allowed to add any members.
However this would allow you to do things like overload the operators.
The one things in particular I would like to do would be to overload just
the typing of the operators. This would result in the implementation of +
(for instance) being the same as + on the underlying type, but the type
of the result would be redefined. The point of this would be to allow a
program to restrict the type that could be used. A concrete use case
would be a SIUinits type that would, at compile time, verify unit
correctness, but in the end would result in exactly the same code as if
bare FP values were used.

Thoughts?

Why not just make that behavior triggered when you do

struct MyReal : real
{
...
}

?

FWIW, you can do stuff like this today by using a struct with a single
member of the base type you want, but you must define all the operators, but
there is no implicit cast back to the base type.  A struct is POD, so it
occupies the same space as the single member, so you get no loss of
efficiency there.

The other problem is that operators can't be used in constant expressions
operators to be used.

I want to say I really like BCS's idea as it really allows you to redefine
all aspects of a typedef.  This would be perfect for what I am trying to do.

-Steve
```
Dec 04 2007
Simen Kjaeraas <simen.kjaras NOSPAM.gmail.com> writes:
```Steven Schveighoffer Wrote:
FWIW, you can do stuff like this today by using a struct with a single
member of the base type you want, but you must define all the operators, but
there is no implicit cast back to the base type.  A struct is POD, so it
occupies the same space as the single member, so you get no loss of
efficiency there.

The other problem is that operators can't be used in constant expressions
operators to be used.

I want to say I really like BCS's idea as it really allows you to redefine
all aspects of a typedef.  This would be perfect for what I am trying to do.

-Steve

On Tue, 04 Dec 2007 15:38:40 +0100, Steven Schveighoffer <schveiguy yahoo.com>
wrote:
FWIW, you can do stuff like this today by using a struct with a single
member of the base type you want, but you must define all the operators,
but
there is no implicit cast back to the base type.  A struct is POD, so it
occupies the same space as the single member, so you get no loss of
efficiency there.

The other problem is that operators can't be used in constant expressions
allow
operators to be used.

I want to say I really like BCS's idea as it really allows you to
redefine
all aspects of a typedef.  This would be perfect for what I am trying to
do.

-Steve

Have a template, it's on the house.

// start code
template TypeDef(T)
{
T value;

typeof(this) opNeg()
{
typeof(this) tmp;
tmp.value = -this.value;
return tmp;
}

typeof(this) opPostInc()
{
typeof(this) tmp;
tmp.value = value++;
return tmp;
}

typeof(this) opPostDec()
{
typeof(this) tmp;
tmp.value = value--;
return tmp;
}

T opCast()
{
return value;
}

static typeof(this) opCall(T rhs)
{
typeof(this) tmp;
tmp.value = rhs;
return tmp;
}

T opCall() // ugly hack to simulate implicit cast
{
return value;
}

{
typeof(this) tmp;
tmp.value = value + rhs;
return tmp;
}

typeof(this) opSub(T rhs)
{
typeof(this) tmp;
tmp.value = value + rhs;
return tmp;
}

typeof(this) opMul(T rhs)
{
typeof(this) tmp;
tmp.value = value * rhs;
return tmp;
}

typeof(this) opDiv(T rhs)
{
typeof(this) tmp;
tmp.value = value / rhs;
return tmp;
}

typeof(this) opMod(T rhs)
{
typeof(this) tmp;
tmp.value = value % rhs;
return tmp;
}

typeof(this) opAssign(T rhs)
{
value = rhs;
return this;
}

{
value += rhs;
return this;
}

typeof(this) opSubAssign(T rhs)
{
value -= rhs;
return this;
}

typeof(this) opMulAssign(T rhs)
{
value *= rhs;
return this;
}

typeof(this) opDivAssign(T rhs)
{
value /= rhs;
return this;
}

typeof(this) opModAssign(T rhs)
{
value %= rhs;
return this;
}

{
typeof(this) tmp;
tmp.value = value + rhs.value;
return tmp;
}

typeof(this) opSub(typeof(this) rhs)
{
typeof(this) tmp;
tmp.value = value + rhs.value;
return tmp;
}

typeof(this) opMul(typeof(this) rhs)
{
typeof(this) tmp;
tmp.value = value * rhs.value;
return tmp;
}

typeof(this) opDiv(typeof(this) rhs)
{
typeof(this) tmp;
tmp.value = value / rhs.value;
return tmp;
}

typeof(this) opMod(typeof(this) rhs)
{
typeof(this) tmp;
tmp.value = value % rhs.value;
return tmp;
}

{
value += rhs.value;
return this;
}

typeof(this) opSubAssign(typeof(this) rhs)
{
value -= rhs.value;
return this;
}

typeof(this) opMulAssign(typeof(this) rhs)
{
value *= rhs.value;
return this;
}

typeof(this) opDivAssign(typeof(this) rhs)
{
value /= rhs.value;
return this;
}

typeof(this) opModAssign(typeof(this) rhs)
{
value %= rhs.value;
return this;
}

string toString()
{
return std.stdio.toString(value);
}
}
// end code

Used it for a quick 'n dirty test like this:

// start code
struct ImaginaryNumber
{
mixin TypeDef!(int);

typeof(this) opSubAssign(int rhs){return this;}

int opMulAssign(typeof(*this) rhs)
{
int result = value * rhs.value;
value = 0;
return -result;
}

typeof(this) opMulAssign(int rhs)
{
value *= rhs;
return this;
}

int opMul(typeof(*this) rhs)
{
return -value * rhs.value;
}

typeof(this) opMul(int rhs)
{
typeof(this) tmp;
tmp.value = value * rhs;
return tmp;
}
}
// end code

I'd like to have a T opImplicitCastTo() {return value;} in there as well, but
that will have to wait.
```
Dec 04 2007
"Steven Schveighoffer" <schveiguy yahoo.com> writes:
```"Simen Kjaeraas" wrote
Have a template, it's on the house.

Thank you for the code.

However, there are still 2 problems with it:

1. Every time I define a typedef of the same base type, the operators are
reinstantiated.  This makes for tremendous code bloat.
2. I cannot create const versions with operators.

i.e. how do I do this:

typedef long myType;

const myType x1 = cast(myType)5;

const myType x2 = x1 * cast(myType)5;

-Steve
```
Dec 04 2007
"Peter C. Chapin" <pchapin sover.net> writes:
```Steven Schveighoffer wrote:

Let's say I want a way to create a type that's like a long, but is not
implicitly convertable from a long.

I can do:

typedef long mytype;

However, I can't create literals of this type.  so if I want to initialize a
mytype value to 6, I have to do:

mytype x = cast(mytype)6L;

FWIW, Ada solves this problem by considering literals in a special type
called "universal integer." It's special because you can't actually
declare any variables of that type. However, universal integers can be
implicitly converted to other types derived from Integer. So, in Ada it
looks like this

type My_Type is range 0..10    -- Or whatever range you need.

X : My_Type;
Y : Integer;

...

X := Y;    -- Type mismatch. Compile error.
X := 1;    -- Fine. Universal integer converts to My_Type.

This sounds like what you want for D. Note, by the way, that the range
constraint on a type definition in Ada must be static. Thus the compiler
can always tell if the value of the universal integer (which can only be
a literal) is in the right range.

Ada also has a concept of universal float to deal with float point
literals in a similar way.

Peter
```
Dec 03 2007
John Demme <me teqdruid.com> writes:
```Peter C. Chapin wrote:

Steven Schveighoffer wrote:

Let's say I want a way to create a type that's like a long, but is not
implicitly convertable from a long.

I can do:

typedef long mytype;

However, I can't create literals of this type.  so if I want to
initialize a mytype value to 6, I have to do:

mytype x = cast(mytype)6L;

FWIW, Ada solves this problem by considering literals in a special type
called "universal integer." It's special because you can't actually
declare any variables of that type. However, universal integers can be
implicitly converted to other types derived from Integer. So, in Ada it
looks like this

type My_Type is range 0..10    -- Or whatever range you need.

X : My_Type;
Y : Integer;

...

X := Y;    -- Type mismatch. Compile error.
X := 1;    -- Fine. Universal integer converts to My_Type.

This sounds like what you want for D. Note, by the way, that the range
constraint on a type definition in Ada must be static. Thus the compiler
can always tell if the value of the universal integer (which can only be
a literal) is in the right range.

Ada also has a concept of universal float to deal with float point
literals in a similar way.

Peter

Behavior like this would come about as a result of polysemous values being

--
~John Demme
me teqdruid.com
```
Dec 03 2007
```IMO, in this case ADA is slicker than D.
I remember that f.i.
type Natural is range 0..255
is quit often in use. (long time ago that I learned a bit ADA)

Having quality assurance and *Reliable Software in mind, then the ADA
way much is smarter  than using D's assert() or DBC instead.

Hope we'll see this feature in D, maybe it is worth a Feature Request.
Bjoern

Peter C. Chapin schrieb:
Steven Schveighoffer wrote:

Let's say I want a way to create a type that's like a long, but is not
implicitly convertable from a long.

I can do:

typedef long mytype;

However, I can't create literals of this type.  so if I want to initialize a
mytype value to 6, I have to do:

mytype x = cast(mytype)6L;

FWIW, Ada solves this problem by considering literals in a special type
called "universal integer." It's special because you can't actually
declare any variables of that type. However, universal integers can be
implicitly converted to other types derived from Integer. So, in Ada it
looks like this

type My_Type is range 0..10    -- Or whatever range you need.

X : My_Type;
Y : Integer;

...

X := Y;    -- Type mismatch. Compile error.
X := 1;    -- Fine. Universal integer converts to My_Type.

This sounds like what you want for D. Note, by the way, that the range
constraint on a type definition in Ada must be static. Thus the compiler
can always tell if the value of the universal integer (which can only be
a literal) is in the right range.

Ada also has a concept of universal float to deal with float point
literals in a similar way.

Peter

```
Dec 04 2007
"Peter C. Chapin" <pchapin sover.net> writes:
```BLS wrote:

IMO, in this case ADA is slicker than D.
I remember that f.i.
type Natural is range 0..255
is quit often in use.

Actually that's

subtype Natural is Integer range 0 .. Integer'Last;

The values in the type are all the non-negative integers. Also it's a
subtype so it can be freely mixed with its parent type (Integer).
However, a run time check will be added when necessary to verify the
range constraint.

Sorry about the off topic post. I just felt the need to clarify that
point. :-)

Peter
```
Dec 04 2007
```Peter C. Chapin schrieb:
BLS wrote:

IMO, in this case ADA is slicker than D.
I remember that f.i.
type Natural is range 0..255
is quit often in use.

Actually that's

subtype Natural is Integer range 0 .. Integer'Last;
Peter

Thanks Peter, that's it.
Too long ago that I've used ADA. :-(  However, having something similar
in D would be great.
Bjoern
```
Dec 04 2007