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 ...
- Jeff Nowakowski (3/5) Dec 08 2007 enumerate them with descriptive names?
- 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...
- Daniel Keep (4/20) Dec 11 2007 opIn_r
- 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
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
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 importedOh 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
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
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 importedI guess mixins are an obvious exception to 4? Sean
Dec 09 2007
Sean Kelly wrote:Walter Bright wrote:Not really, because mixins are *defined* to have meaning only in the instantiation context. How/where you do the import doesn't affect this.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 importedI guess mixins are an obvious exception to 4?
Dec 09 2007
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
Jason House wrote:Walter Bright wrote: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. --bbThat 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
Jason House wrote:Walter Bright wrote:You might, for example, want to create an associative array that behaved slightly differently, but use it just like an AA.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."
Dec 11 2007
Walter Bright wrote:Jason House wrote:... 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.Walter Bright wrote:You might, for example, want to create an associative array that behaved slightly differently, but use it just like an AA.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."
Dec 11 2007
Jason House wrote:Walter Bright wrote:opIn_r Overload and be happy. -- DanielJason House wrote:.... 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.Walter Bright wrote:You might, for example, want to create an associative array that behaved slightly differently, but use it just like an AA.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."
Dec 11 2007
On Tue, 11 Dec 2007 18:57:17 -0500, Jason House wrote:Walter Bright wrote: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; }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 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
Derek Parnell Wrote:On Tue, 11 Dec 2007 18:57:17 -0500, Jason House wrote: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 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 good way to think about this issue... I just don't know yet.Walter Bright wrote: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 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?
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