## digitalmars.D - Walter's second axiom

• Janice Caron (32/32) Dec 08 2007 As discussed elsewhere, Walter's second axiom states: for any type T,
• Bill Baxter (21/65) Dec 08 2007 There are various other ones that have to do with return of lvalues (or
• Walter Bright (14/17) Dec 08 2007 That axiom didn't really exist until Andrei started pointing out to me
• Janice Caron (10/15) Dec 08 2007 Oh dear - now you've gone and renumbered them! How can we refer to them ...
• Sean Kelly (3/12) Dec 09 2007 I guess mixins are an obvious exception to 4?
• Walter Bright (3/15) Dec 09 2007 Not really, because mixins are *defined* to have meaning only in the
• Jason House (16/19) Dec 11 2007 Can you please share with us all the advantages of "Any type T can be
• Bill Baxter (14/38) Dec 11 2007 I don't think the idea is to restrict it to the original set of methods....
• Walter Bright (3/11) Dec 11 2007 You might, for example, want to create an associative array that behaved...
• Jason House (4/16) Dec 11 2007 ... so even without the const issues, this type of functionality does no...
• Derek Parnell (49/71) Dec 11 2007 I don't think that Walter was suggesting that one actually does this ...
• Jason House (6/30) Dec 12 2007 That still leaves me wondering what he's suggesting that we do, and how ...
• Janice Caron (4/11) Dec 11 2007 It's the difference between CAN and MUST. The idea that it is
```As discussed elsewhere, Walter's second axiom states: for any type T,
it should be possible to construct a struct S which behaves exactly
like a T, by wrapping T inside a struct and adding extra member
functions, like so:

struct S
{
T t;
/* functions */
}

However, there is at least one violation of the axiom: inheritance. As follows:

class C {}
struct S { C c; }

I can do

class D : C {}

but I can't do

class D : S {}

thus, S cannot be made to behave exactly like C, and so the axiom does not hold.

The solution is to provide structs with some mechanism that allows
classes to inherit from them - such as, for example, opInherit()

struct S
{
C c;
C opInherit() { return c; }
}

A class could then inherit from either a class, or from a struct which
supplies opInherit(). opInherit() itself must, of course, return
either a class, or a struct which supplies opInherit().

This is not a feature request as such. I'm just mentioning - from a
purely theoretical standpoint - a violation of Walter's second axiom,
and a possible remedy.

(And just to stir things up further, I think that interfaces violate
the second axiom also) :-)
```
Dec 08 2007
Bill Baxter <dnewsgroup billbaxter.com> writes:
```Janice Caron wrote:
As discussed elsewhere, Walter's second axiom states: for any type T,
it should be possible to construct a struct S which behaves exactly
like a T, by wrapping T inside a struct and adding extra member
functions, like so:

struct S
{
T t;
/* functions */
}

However, there is at least one violation of the axiom: inheritance. As follows:

class C {}
struct S { C c; }

I can do

class D : C {}

but I can't do

class D : S {}

thus, S cannot be made to behave exactly like C, and so the axiom does not
hold.

There are various other ones that have to do with return of lvalues (or
lack thereof).
For instance, if T is int[], you can do t[0]++ but you can't do that for
a struct that wraps an int[].

Also the thing about default initializers for struct wrappers.  Perhaps
that's just a bug, though.  Walter hasn't commented whether he intends
to make that work or not.  The example is wrap a numeric type and give
it all the bells it needs to initialize it with a regular number, like
MyInt x = 5.  Now try to use it as a paramter with default:
void func(MyInt x = 5) // error

But I think axiom 2 is great and I'd love to see a solution for all of
the holes that prevent making a wrapper behave exactly like another type.

The solution is to provide structs with some mechanism that allows
classes to inherit from them - such as, for example, opInherit()

struct S
{
C c;
C opInherit() { return c; }
}

A class could then inherit from either a class, or from a struct which
supplies opInherit(). opInherit() itself must, of course, return
either a class, or a struct which supplies opInherit().

This is not a feature request as such. I'm just mentioning - from a
purely theoretical standpoint - a violation of Walter's second axiom,
and a possible remedy.

(And just to stir things up further, I think that interfaces violate
the second axiom also) :-)

I think maybe that's going to far.  There are some places where you
cannot and should not have a wrapper act like the type it wraps.  The
main one that comes to mind is an 'is' check.   is(MyWrapper ==
RealClass).  Obviously that should fail.   But it stands to reason that
there are perhaps some other operations that are so closely tied to the
actual type that they shouldn't be expected to work with a wrapper.   I
think inheritance is maybe one of those.

--bb
```
Dec 08 2007
Walter Bright <newshound1 digitalmars.com> writes:
```Janice Caron wrote:
This is not a feature request as such. I'm just mentioning - from a
purely theoretical standpoint - a violation of Walter's second axiom,
and a possible remedy.

That axiom didn't really exist until Andrei started pointing out to me
all the advantages of being able to wrap types in a struct. So we can
expect some historical problems with it.

But it is a very useful one going forward with changes to the language,
as it keeps us from making things worse. I suspect that the closer we
can adhere to that axiom, the better generic programming will work.

Other axioms in D have been there from the beginning, and have helped
keep the language on track. For example:

1) D can be lexed with no syntactic information
2) D can be parsed with no semantic information
3) Modules can be imported in any order without affecting their meaning
4) The meaning of imported declarations is not affected by the context
of where they are imported
```
Dec 08 2007
```On 12/8/07, Walter Bright <newshound1 digitalmars.com> wrote:
1) D can be lexed with no syntactic information
2) D can be parsed with no semantic information
3) Modules can be imported in any order without affecting their meaning
4) The meaning of imported declarations is not affected by the context
of where they are imported

Oh dear - now you've gone and renumbered them! How can we refer to them now? :-)

It occurs to me that the second axiom (...that's using the old
numbering...) could in time render the keyword "typedef" irrelevant.
That is,

struct S : T {}

could replace

typedef T S;

Of course, I'm being extremely optimistic here in assuming the struct
syntax above will ever be part of D! :-)
```
Dec 08 2007
Jeff Nowakowski <jeff dilacero.org> writes:
```Janice Caron wrote:

Oh dear - now you've gone and renumbered them! How can we refer to them now?
:-)

enumerate them with descriptive names?

-Jeff
```
Dec 08 2007
Sean Kelly <sean f4.ca> writes:
```Walter Bright wrote:

Other axioms in D have been there from the beginning, and have helped
keep the language on track. For example:

1) D can be lexed with no syntactic information
2) D can be parsed with no semantic information
3) Modules can be imported in any order without affecting their meaning
4) The meaning of imported declarations is not affected by the context
of where they are imported

I guess mixins are an obvious exception to 4?

Sean
```
Dec 09 2007
Walter Bright <newshound1 digitalmars.com> writes:
```Sean Kelly wrote:
Walter Bright wrote:
Other axioms in D have been there from the beginning, and have helped
keep the language on track. For example:

1) D can be lexed with no syntactic information
2) D can be parsed with no semantic information
3) Modules can be imported in any order without affecting their meaning
4) The meaning of imported declarations is not affected by the context
of where they are imported

I guess mixins are an obvious exception to 4?

Not really, because mixins are *defined* to have meaning only in the
instantiation context. How/where you do the import doesn't affect this.
```
Dec 09 2007
Jason House <jason.james.house gmail.com> writes:
```Walter Bright wrote:
That axiom didn't really exist until Andrei started pointing out to me
all the advantages of being able to wrap types in a struct. So we can
expect some historical problems with it.

Can you please share with us all the advantages of "Any type T can be
wrapped inside a struct S, and that S can be made to behave as a typedef
for type T."

Under what conditions can it behave exactly like a typedef?  If it behaves
*exactly* as a typedef under all circumstances, then it can't have any
additional data members or member functions and must behave exactly like
the original.  If that's true, why wrap it in a struct at all?

I assume there has to be some way to bend that.  Candidates in my mind are
extra data members and extra member functions.  Extra data members seems
less likely because a cast from S to T and back to S would lose data
(something that typedef's don't have happen).  Of course, this may fit into
the grand scheme of opImplicitCast.

I also wonder if extending a class may be an alternative to wrapping it in a
struct.  Maybe wrapping in a struct could be restricted to value types?  I
just don't understand this enough yet to give any credible suggestions.
```
Dec 11 2007
Bill Baxter <dnewsgroup billbaxter.com> writes:
```Jason House wrote:
Walter Bright wrote:
That axiom didn't really exist until Andrei started pointing out to me
all the advantages of being able to wrap types in a struct. So we can
expect some historical problems with it.

Can you please share with us all the advantages of "Any type T can be
wrapped inside a struct S, and that S can be made to behave as a typedef
for type T."

Under what conditions can it behave exactly like a typedef?  If it behaves
*exactly* as a typedef under all circumstances, then it can't have any
additional data members or member functions and must behave exactly like
the original.  If that's true, why wrap it in a struct at all?

I assume there has to be some way to bend that.  Candidates in my mind are
extra data members and extra member functions.  Extra data members seems
less likely because a cast from S to T and back to S would lose data
(something that typedef's don't have happen).  Of course, this may fit into
the grand scheme of opImplicitCast.

I also wonder if extending a class may be an alternative to wrapping it in a
struct.  Maybe wrapping in a struct could be restricted to value types?  I
just don't understand this enough yet to give any credible suggestions.

I don't think the idea is to restrict it to the original set of methods.
Adding or removing methods is ok.  For instance if you want to make
something that's like an array but can't have its length changed then
then just leave out the .length setter property, etc.

Even if you keep the exact same interface as the original type it's
still useful to be able to intercept methods to provide notifications.
For instance in a GUI program you could create a float wrapper that acts
just like a regular float, except whenever you set or change its value
it automatically sends out notifications to anyone who's interested in
the value.  Or maybe you're writing some distributed processing thing
and setting the value is going to cause packets to be sent out to
another process over the net that is mirroring this one's state.

--bb
```
Dec 11 2007
Walter Bright <newshound1 digitalmars.com> writes:
```Jason House wrote:
Walter Bright wrote:
That axiom didn't really exist until Andrei started pointing out to me
all the advantages of being able to wrap types in a struct. So we can
expect some historical problems with it.

Can you please share with us all the advantages of "Any type T can be
wrapped inside a struct S, and that S can be made to behave as a typedef
for type T."

You might, for example, want to create an associative array that behaved
slightly differently, but use it just like an AA.
```
Dec 11 2007
Jason House <jason.james.house gmail.com> writes:
```Walter Bright wrote:

Jason House wrote:
Walter Bright wrote:
That axiom didn't really exist until Andrei started pointing out to me
all the advantages of being able to wrap types in a struct. So we can
expect some historical problems with it.

Can you please share with us all the advantages of "Any type T can be
wrapped inside a struct S, and that S can be made to behave as a typedef
for type T."

You might, for example, want to create an associative array that behaved
slightly differently, but use it just like an AA.

... so even without the const issues, this type of functionality does not
yet work in D?  As far as I know, AA's are the only thing that support the
in operator.
```
Dec 11 2007
Daniel Keep <daniel.keep.lists gmail.com> writes:
```Jason House wrote:
Walter Bright wrote:

Jason House wrote:
Walter Bright wrote:
That axiom didn't really exist until Andrei started pointing out to me
all the advantages of being able to wrap types in a struct. So we can
expect some historical problems with it.

Can you please share with us all the advantages of "Any type T can be
wrapped inside a struct S, and that S can be made to behave as a typedef
for type T."

You might, for example, want to create an associative array that behaved
slightly differently, but use it just like an AA.

.... so even without the const issues, this type of functionality does not
yet work in D?  As far as I know, AA's are the only thing that support the
in operator.

opIn_r

-- Daniel
```
Dec 11 2007
Derek Parnell <derek nomail.afraid.org> writes:
```On Tue, 11 Dec 2007 18:57:17 -0500, Jason House wrote:

Walter Bright wrote:
That axiom didn't really exist until Andrei started pointing out to me
all the advantages of being able to wrap types in a struct. So we can
expect some historical problems with it.

Can you please share with us all the advantages of "Any type T can be
wrapped inside a struct S, and that S can be made to behave as a typedef
for type T."

Under what conditions can it behave exactly like a typedef?  If it behaves
*exactly* as a typedef under all circumstances, then it can't have any
additional data members or member functions and must behave exactly like
the original.  If that's true, why wrap it in a struct at all?

I don't think that Walter was suggesting that one actually does this ...

struct LONG { long x; }
LONG foo;

because that would be a bit pointless, and because it would also show that
you need to use different syntax when using structs rather than typedefs
...

struct longS { long x; }
typedef long longT ;

void main()
{

longS foo;
longT bar;

foo.x = 1;   // One can't yet do "foo = cast(longS)1;"
bar = cast(longT)2;
}

I assume there has to be some way to bend that.  Candidates in my mind are
extra data members and extra member functions.  Extra data members seems
less likely because a cast from S to T and back to S would lose data
(something that typedef's don't have happen).  Of course, this may fit into
the grand scheme of opImplicitCast.

I also wonder if extending a class may be an alternative to wrapping it in a
struct.  Maybe wrapping in a struct could be restricted to value types?  I
just don't understand this enough yet to give any credible suggestions.

I needed a Currency data type. I chose to use a struct to wrap a long plus
some methods to get it. So my Currency is really a smart long but it
behaves 'like' a value type except for the special syntax required because
it *is* a struct.

Of course I'd like to see a truly smart value type functionality added to
D, in which one could use the same syntax used for built-in value types.

Currency actual_balance,
available_balance,
credit_limit,
overlimit_amount
;
Date     overlimit_date,
current_processing_date
;
int      overlimit_days;
. . .

if ( actual_balance <= credit_limit )
overlimit_amount = 0;
else
overlimit_amount = credit_limit - actual_balance;

if (available_balance <= credit_limit)
{
overlimit_date = null;
overlimit_days = 0;
}
else
overlimit_days = current_processing_date - overlimit_date;

--
Derek
(skype: derek.j.parnell)
Melbourne, Australia
12/12/2007 12:59:14 PM
```
Dec 11 2007
Jason House <jason.james.house gmail.com> writes:
```Derek Parnell Wrote:

On Tue, 11 Dec 2007 18:57:17 -0500, Jason House wrote:

Walter Bright wrote:
That axiom didn't really exist until Andrei started pointing out to me
all the advantages of being able to wrap types in a struct. So we can
expect some historical problems with it.

Can you please share with us all the advantages of "Any type T can be
wrapped inside a struct S, and that S can be made to behave as a typedef
for type T."

Under what conditions can it behave exactly like a typedef?  If it behaves
*exactly* as a typedef under all circumstances, then it can't have any
additional data members or member functions and must behave exactly like
the original.  If that's true, why wrap it in a struct at all?

I don't think that Walter was suggesting that one actually does this ...

struct LONG { long x; }
LONG foo;

because that would be a bit pointless, and because it would also show that
you need to use different syntax when using structs rather than typedefs
...

That still leaves me wondering what he's suggesting that we do, and how it fits
into expanding generic programming support.  It seems like this mailing list
has a lot of trouble convincing Walter of our viewpoints.  Somehow, Andrei has
succeeded and I have to assume that there were a number of good arguments and
motivating examples.

I'd also expect axioms to be well worded and iron-clad concepts.  They are
after-all, an acid test for addition of features.  If the goal is really an
ability to define a new type that can be used in a similar way to another type
(including application of const), I think that's a lot different than wrapping
in a struct and a lot different than defining a typedef.

As I understand the const/tail-const problems, I think that char[], const(char)
[], and const char[] are three distinct types.  Using a reference struct of
struct foo{ char[] x}, the middle array type (tail const) is impossible to
achieve with a simple application of const to the type...  foo and const foo
are well defined, but tail const makes little sense.

I'd like to provide better feedback in the const debate, but I need to really
understand what the real goal is.

For example, arrays and associative arrays tend to pose a unique problem with
axiom #2.  I've been trying to think of arrays and associative arrays as
nothing special... array!(T) and AA!(T) instead of specialized syntax.  Looking
at normal arrays, the three const types would translate to array!(char),
array!(const(char)), const array!(char).  In that form, tail const really has a
different feel...  If we have templated structs in a similar way, it may be a
```
Dec 12 2007
```On Dec 11, 2007 11:57 PM, Jason House <jason.james.house gmail.com> wrote:
Can you please share with us all the advantages of "Any type T can be
wrapped inside a struct S, and that S can be made to behave as a typedef
for type T."

Under what conditions can it behave exactly like a typedef?  If it behaves
*exactly* as a typedef under all circumstances, then it can't have any
additional data members or member functions and must behave exactly like
the original.  If that's true, why wrap it in a struct at all?

It's the difference between CAN and MUST. The idea that it is
/possible/ to make a type which emulates another type. But no one ever
said it was compulsory.
```
Dec 11 2007