digitalmars.D - Extending typedefs
- Deewiant (58/58) Jan 26 2006 With strong typedefs, we can do type checking, like in the following can...
- Carlos Smith (20/94) Jan 26 2006 This is an interesting ideas. But it needs some work to make it practica...
- Deewiant (30/62) Jan 26 2006 You would only have to add them if you wanted to make a strongly typed c...
- Ben Phillips (8/11) Jan 26 2006 imho you should just use a plain old "int". I see no reason to have a bi...
- Craig Black (11/29) Jan 27 2006 My friend, you have no idea how many problems are caused by units
- Craig Black (8/82) Jan 26 2006 I don't think typedefs are the solution to the problem that you are tryi...
- Oskar Linde (5/10) Jan 26 2006 The feature requests needed for a SIUnits clone are:
- Craig Black (6/10) Jan 26 2006 I know Walter wants to eventually add implicit function template
- Deewiant (12/27) Jan 26 2006 If it requires quite a lot of template "magic" or some such, while such ...
- Craig Black (17/33) Jan 26 2006 It does. SIUnits is much more involved than what you propose. It is ba...
- BCS (31/78) Jan 26 2006 I have sketched some template code to see what it would take to do prity...
- Craig Black (6/34) Jan 26 2006 I agree. This would be a worthwhile addition to D's template capabiliti...
- Oskar Linde (33/45) Jan 30 2006 I apologize for that statement. It was made too hasty. Assignment
With strong typedefs, we can do type checking, like in the following canonical example: --- typedef int Mass, Volume; Mass x = 3; Volume y = 4; x = y; // error, cannot implicitly convert from Volume to Mass --- But what we cannot do is the following: --- Mass x; Volume y; Density z; z = x / y; --- Since x / y is an expression of type Mass (I believe), which can't be implicitly converted to Density. Thinking about this from physics's point of view, though, it should work: mass divided by volume is the definition of density. Of course, this can be done with sufficient casting. But as we all know, casts are clumsy and make code get ugly very quickly. Plus, things get really ugly when typedefing classes. See the following simple example, for instance: --- class Vector { real x, y; this(real a, real b) { x = a; y = b; } Vector opMul(real n) { return new Vector(n * x, n * y); } } typedef Vector Velocity; Velocity vel; vel = cast(Velocity)(cast(Vector)vel * 2)); --- The inner cast is needed since otherwise DMD complains that "this for opMul needs to be type Vector not type Velocity". The outer cast, of, course, is to make the now-Vector (vel * 2) back into a Velocity. I wasn't even aware of this before starting to write this post. Even worse: cast(Vector)vel *= 2; Makes sense, right? Not to me, at least. This is one of the few instances were an lvalue can be a cast expression. Surely the "this" pointer in the class should be typedefed along with the class itself! But even though I'd label the above as a bug (language or compiler, beats me), I digress. I'm proposing a syntax which would allow, for example, expressions of type (Mass / Volume) to be implicitly converted to Density. What I'm thinking of is along these lines: typedef int Mass, Volume, Density; typedef (Mass / Volume) Density; So now, Density behaves like a normal typedef of an int until an expression of type (Mass / Volume) comes along, at which point a cast is no longer needed to convert the expression to type Density. At best, the compiler could infer the other forms from the above, so that this is not needed: typedef (Mass / Volume) Density; typedef (Mass / Density) Volume; typedef (Density * Volume) Mass; typedef (Volume * Density) Mass; But it's enough for me if the above works, whether it needs one line or four. Comments on this or the "this" pointer casting?
Jan 26 2006
"Deewiant" <deewiant.doesnotlike.spam gmail.com> wrote in message news:dra43i$oec$1 digitaldaemon.com...With strong typedefs, we can do type checking, like in the following canonical example: --- typedef int Mass, Volume; Mass x = 3; Volume y = 4; x = y; // error, cannot implicitly convert from Volume to Mass --- But what we cannot do is the following: --- Mass x; Volume y; Density z; z = x / y; --- Since x / y is an expression of type Mass (I believe), which can't be implicitly converted to Density. Thinking about this from physics's point of view, though, it should work: mass divided by volume is the definition of density. Of course, this can be done with sufficient casting. But as we all know, casts are clumsy and make code get ugly very quickly. Plus, things get really ugly when typedefing classes. See the following simple example, for instance: --- class Vector { real x, y; this(real a, real b) { x = a; y = b; } Vector opMul(real n) { return new Vector(n * x, n * y); } } typedef Vector Velocity; Velocity vel; vel = cast(Velocity)(cast(Vector)vel * 2)); --- The inner cast is needed since otherwise DMD complains that "this for opMul needs to be type Vector not type Velocity". The outer cast, of, course, is to make the now-Vector (vel * 2) back into a Velocity. I wasn't even aware of this before starting to write this post. Even worse: cast(Vector)vel *= 2; Makes sense, right? Not to me, at least. This is one of the few instances were an lvalue can be a cast expression. Surely the "this" pointer in the class should be typedefed along with the class itself! But even though I'd label the above as a bug (language or compiler, beats me), I digress. I'm proposing a syntax which would allow, for example, expressions of type (Mass / Volume) to be implicitly converted to Density. What I'm thinking of is along these lines: typedef int Mass, Volume, Density; typedef (Mass / Volume) Density; So now, Density behaves like a normal typedef of an int until an expression of type (Mass / Volume) comes along, at which point a cast is no longer needed to convert the expression to type Density.This is an interesting ideas. But it needs some work to make it practical. Does it means you would have to add a zillion typedef'initions to D ? There are a lot of concepts in math, physics, ... defined in terms of numbers who represent quantities. Also, will you have to define: typedef (Mass / unsigned int ) Mass; typedef (Mass / negative int ) NegativeMass; ... And, how will you define the different types for Surface. typedef ( ... ) Surface; // for a circle. typedef ( ... ) Surface; // for a square. typedef ( ... ) Surface; // for a cube.At best, the compiler could infer the other forms from the above, so that this is not needed: typedef (Mass / Volume) Density; typedef (Mass / Density) Volume; typedef (Density * Volume) Mass; typedef (Volume * Density) Mass; But it's enough for me if the above works, whether it needs one line or four.May be, it is better to have a rule, that says, that new typedef, based on Basic Types of the language, inherit the properties of these Basic Types. And that means Mass can be divided by Volume because those are 2 integers. And the result can be assigned to Density, because this is also an integerComments on this or the "this" pointer casting?
Jan 26 2006
Carlos Smith wrote:Does it means you would have to add a zillion typedef'initions to D ? There are a lot of concepts in math, physics, ... defined in terms of numbers who represent quantities.You would only have to add them if you wanted to make a strongly typed complete mathematics and physics library. <g> I'm not saying there have to be unique typedefs for every single thing, just that the feature could be useful.Also, will you have to define: typedef (Mass / unsigned int ) Mass; typedef (Mass / negative int ) NegativeMass; ...No, not necessarily. Only if I want to be really picky about the distinction between a mass and a negative mass. I'd say the only sane thing is just to do a normal "typedef int Mass" and then deal with negatives as necessary. There is apparently such a thing as too strong typing. <g>And, how will you define the different types for Surface. typedef ( ... ) Surface; // for a circle. typedef ( ... ) Surface; // for a square. typedef ( ... ) Surface; // for a cube.typedef Surface Circle; typedef Surface Square; typedef Surface Cube; They're completely independent of each other. _Maybe_ something like "typedef (Square * Square * Square) Cube" in addition to the above, but it seems to me that that would require some strange functionality in Surface.opMul(), so probably not. It also seems to me that that should be done with classes and inheritance: circles and cubes are quite different things. Make a class (or interface) out of Surface and inherit as needed. I'm not trying to replace the class mechanism here or anything. It's just that while it is also possible in my Mass/Volume/Density example to just make classes of the three and make them interoperate as they should, there are reasons why I'd much rather not: * D doesn't allow overloading of =, so they would be pretty clumsy compared to aliased integers (which is what I'd use without my suggested typedefs). * I would basically be making an "abstract class Integer" which behaves just like a normal int (but see above point), except that it can be subclassed. * D isn't Ruby so I can't subclass int, which would deal with the above.And the result can be assigned to Speed, because that's also an integer. But it makes no physical sense to apply Volume to Speed. Did I misunderstand or did you just describe the alias keyword?At best, the compiler could infer the other forms from the above, so that this is not needed: typedef (Mass / Volume) Density; typedef (Mass / Density) Volume; typedef (Density * Volume) Mass; typedef (Volume * Density) Mass; But it's enough for me if the above works, whether it needs one line or four.May be, it is better to have a rule, that says, that new typedef, based on Basic Types of the language, inherit the properties of these Basic Types. And that means Mass can be divided by Volume because those are 2 integers. And the result can be assigned to Density, because this is also an integer
Jan 26 2006
In article <drabgr$uae$1 digitaldaemon.com>, Deewiant says...And the result can be assigned to Speed, because that's also an integer. But it makes no physical sense to apply Volume to Speed. Did I misunderstand or did you just describe the alias keyword?imho you should just use a plain old "int". I see no reason to have a billion different typedefs all for different units. Yes its OO, but to me it just looks like unnecessary abstraction. I mean the chances of messing up density = mass/volume are too slim in my opinion to be worth the extra effort (I'm having trouble thinking up more complex examples that are really so desparately in need of compile time arithmetic checking that its worth creating a huge hierarchy of variants on int)
Jan 26 2006
"Ben Phillips" <Ben_member pathlink.com> wrote in message news:drc0v4$la$1 digitaldaemon.com...In article <drabgr$uae$1 digitaldaemon.com>, Deewiant says...My friend, you have no idea how many problems are caused by units miscalculations. All the smart people at NASA can't even get it right! That's why features that provide safety for the programmer such as bounds checking and garbage collection are so popular. If we can provide more safe units conversion utilities it will definitely be worth the effort. However, I do believe that the approach that he proposes is not the right approach. Again, for a good example of a C++ library that already tackles this problem, see SIUnits. -CraigAnd the result can be assigned to Speed, because that's also an integer. But it makes no physical sense to apply Volume to Speed. Did I misunderstand or did you just describe the alias keyword?imho you should just use a plain old "int". I see no reason to have a billion different typedefs all for different units. Yes its OO, but to me it just looks like unnecessary abstraction. I mean the chances of messing up density = mass/volume are too slim in my opinion to be worth the extra effort (I'm having trouble thinking up more complex examples that are really so desparately in need of compile time arithmetic checking that its worth creating a huge hierarchy of variants on int)
Jan 27 2006
I don't think typedefs are the solution to the problem that you are trying to solve. C++ has libraries that have the capabilities that you propose (The SIUnits Library). They use templates and operator overloading to enforce correctness when dealing with units. Perhaps we can solve this problem without burdening Walter with another feature request. -Craig "Deewiant" <deewiant.doesnotlike.spam gmail.com> wrote in message news:dra43i$oec$1 digitaldaemon.com...With strong typedefs, we can do type checking, like in the following canonical example: --- typedef int Mass, Volume; Mass x = 3; Volume y = 4; x = y; // error, cannot implicitly convert from Volume to Mass --- But what we cannot do is the following: --- Mass x; Volume y; Density z; z = x / y; --- Since x / y is an expression of type Mass (I believe), which can't be implicitly converted to Density. Thinking about this from physics's point of view, though, it should work: mass divided by volume is the definition of density. Of course, this can be done with sufficient casting. But as we all know, casts are clumsy and make code get ugly very quickly. Plus, things get really ugly when typedefing classes. See the following simple example, for instance: --- class Vector { real x, y; this(real a, real b) { x = a; y = b; } Vector opMul(real n) { return new Vector(n * x, n * y); } } typedef Vector Velocity; Velocity vel; vel = cast(Velocity)(cast(Vector)vel * 2)); --- The inner cast is needed since otherwise DMD complains that "this for opMul needs to be type Vector not type Velocity". The outer cast, of, course, is to make the now-Vector (vel * 2) back into a Velocity. I wasn't even aware of this before starting to write this post. Even worse: cast(Vector)vel *= 2; Makes sense, right? Not to me, at least. This is one of the few instances were an lvalue can be a cast expression. Surely the "this" pointer in the class should be typedefed along with the class itself! But even though I'd label the above as a bug (language or compiler, beats me), I digress. I'm proposing a syntax which would allow, for example, expressions of type (Mass / Volume) to be implicitly converted to Density. What I'm thinking of is along these lines: typedef int Mass, Volume, Density; typedef (Mass / Volume) Density; So now, Density behaves like a normal typedef of an int until an expression of type (Mass / Volume) comes along, at which point a cast is no longer needed to convert the expression to type Density. At best, the compiler could infer the other forms from the above, so that this is not needed: typedef (Mass / Volume) Density; typedef (Mass / Density) Volume; typedef (Density * Volume) Mass; typedef (Volume * Density) Mass; But it's enough for me if the above works, whether it needs one line or four. Comments on this or the "this" pointer casting?
Jan 26 2006
In article <dratih$1gmj$1 digitaldaemon.com>, Craig Black says...I don't think typedefs are the solution to the problem that you are trying to solve. C++ has libraries that have the capabilities that you propose (The SIUnits Library). They use templates and operator overloading to enforce correctness when dealing with units. Perhaps we can solve this problem without burdening Walter with another feature request.The feature requests needed for a SIUnits clone are: - implicit function template instantiation - assignment operator overloading (only for structs is enough) /Oskar
Jan 26 2006
The feature requests needed for a SIUnits clone are: - implicit function template instantiation - assignment operator overloading (only for structs is enough) /OskarI know Walter wants to eventually add implicit function template instantiation, but as for assignment operator overloading, I don't know. Do you think new typedef features would be easier to add to the compiler? Do you think that they would facilitate a library with the capability of SIUnits? -Craig
Jan 26 2006
Craig Black wrote:If it requires quite a lot of template "magic" or some such, while such a library would be useful it doesn't exactly solve the problem, since it'd be difficult to add new units. Of course, if it provides its own mechanism for doing just that, that's fine. Unfortunately, porting such a library is probably a lot of work, even if we had the features that are, according to Oskar, required. My typedef suggestion just struck me as the simplest solution. I am not a compiler writer, so I don't know whether that's correct or not, but in my opinion it would be simpler for the D programmers, at least, than messing around with templates. And, of course, different syntax, unless assignment operator overloading arrives.The feature requests needed for a SIUnits clone are: - implicit function template instantiation - assignment operator overloading (only for structs is enough) /OskarI know Walter wants to eventually add implicit function template instantiation, but as for assignment operator overloading, I don't know. Do you think new typedef features would be easier to add to the compiler? Do you think that they would facilitate a library with the capability of SIUnits? -Craig
Jan 26 2006
If it requires quite a lot of template "magic" or some such, while such a library would be useful it doesn't exactly solve the problem, since it'd be difficult to add new units. Of course, if it provides its own mechanism for doing just that, that's fine.It does. SIUnits is much more involved than what you propose. It is based on the seven base units of the SI system: length, mass, time, electric current, temperature, amount of substance, and luminous intensity. All other SI units are merely combinations of one of these seven. The templates have the smarts to know when you multiply length times length, you get length squared, or area. If you divide length by time, you get velocity, etc. There are an infinite number of combinations of these units and therefore an infinite number of unit types. I believe such a system would be quite difficult to reproduce with typedefs as you propose.Unfortunately, porting such a library is probably a lot of work, even if we had the features that are, according to Oskar, required.I am less concerned with the difficulty of such a task and more concerned with eventually attaining the most complete and elegant solution.My typedef suggestion just struck me as the simplest solution. I am not a compiler writer, so I don't know whether that's correct or not, but in my opinion it would be simpler for the D programmers, at least, than messing around with templates. And, of course, different syntax, unless assignment operator overloading arrives.If someone else writes the template, and you merely have to use it, is that so bad? SIUnits was probably a challenge to write, but using it is not that hard. However, perhaps you are right. But you are going to have to flesh out your idea more if it is to be on par with the capability of SIUnits. -Craig
Jan 26 2006
I have sketched some template code to see what it would take to do prity mutch exactly what you all are talking about. I thin that all that would be needed is implicet specilization of a function and allowing the return type of sutch a function to be determoned by the type of it's aruments. Something like this: template Unit(int L, int T, int M, int I, int K) { struct Unit { real v Unit(L,T,M,I,K) opAdd(Unit(L,T,M,I,K) v2) { Unit(L,T,M,I,K) ret; ret.v = v+v2.v; return ret; } // multiply this by an instance of Unit specialized for any values // L,T,M,I&K come from this, l,t,m,i&k come from v2 Unit(L+l,T+t,M+m,I+i,K+k) opMul(Unit(l,t,m,i,k) v2) { Unit(L+l,T+t,M+m,I+i,K+k) ret; ret.v = v*v2.v; return ret; } } } this has been brought up a number of times and I would rely like to see this kind of capacity added to D. Craig Black wrote:IIRC it is possible to get away with 5 dimensions of units if you allow the Mole to be just a count and the candela to be power per area.If it requires quite a lot of template "magic" or some such, while such a library would be useful it doesn't exactly solve the problem, since it'd be difficult to add new units. Of course, if it provides its own mechanism for doing just that, that's fine.It does. SIUnits is much more involved than what you propose. It is based on the seven base units of the SI system: length, mass, time, electric current, temperature, amount of substance, and luminous intensity. All other SI units are merely combinations of one of these seven. The templateshave the smarts to know when you multiply length times length, you get length squared, or area. If you divide length by time, you get velocity, etc. There are an infinite number of combinations of these units and therefore an infinite number of unit types. I believe such a system would be quite difficult to reproduce with typedefs as you propose.Unfortunately, porting such a library is probably a lot of work, even if we had the features that are, according to Oskar, required.I am less concerned with the difficulty of such a task and more concerned with eventually attaining the most complete and elegant solution.My typedef suggestion just struck me as the simplest solution. I am not a compiler writer, so I don't know whether that's correct or not, but in my opinion it would be simpler for the D programmers, at least, than messing around with templates. And, of course, different syntax, unless assignment operator overloading arrives.If someone else writes the template, and you merely have to use it, is that so bad? SIUnits was probably a challenge to write, but using it is not that hard. However, perhaps you are right. But you are going to have to flesh out your idea more if it is to be on par with the capability of SIUnits. -Craig
Jan 26 2006
"BCS" <BCS_member pathlink.com> wrote in message news:drbgor$21oo$1 digitaldaemon.com...I have sketched some template code to see what it would take to do prity mutch exactly what you all are talking about. I thin that all that would be needed is implicet specilization of a function and allowing the return type of sutch a function to be determoned by the type of it's aruments. Something like this: template Unit(int L, int T, int M, int I, int K) { struct Unit { real v Unit(L,T,M,I,K) opAdd(Unit(L,T,M,I,K) v2) { Unit(L,T,M,I,K) ret; ret.v = v+v2.v; return ret; } // multiply this by an instance of Unit specialized for any values // L,T,M,I&K come from this, l,t,m,i&k come from v2 Unit(L+l,T+t,M+m,I+i,K+k) opMul(Unit(l,t,m,i,k) v2) { Unit(L+l,T+t,M+m,I+i,K+k) ret; ret.v = v*v2.v; return ret; } } } this has been brought up a number of times and I would rely like to see this kind of capacity added to D.I agree. This would be a worthwhile addition to D's template capabilities. Why do you think Oskar suggested that assignment operator overloading would be required? -Craig
Jan 26 2006
Craig Black wrote:"BCS" <BCS_member pathlink.com> wrote in message news:drbgor$21oo$1 digitaldaemon.com...<snip>I have sketched some template code to see what it would take to do prity mutch exactly what you all are talking about. I thin that all that would be needed is implicet specilization of a function and allowing the return type of sutch a function to be determoned by the type of it's aruments.I apologize for that statement. It was made too hasty. Assignment operator overloading would not be necessary for a pure dimensional library that only does compile time checking of dimensional correctness. Assignment overloading would only affect a runtime behavior that in this case is meant to remain unchanged. For a library that deals with units on the other hand, assignment overloading would probably be necessary to handle conversions between different units / unit prefixes. --- Off Topic, regarding assignment overloading: I do not propose that assignment overloading should be implemented for classes. Classes are after all reference types. Structs, on the other hand, are value based and assigning one struct to another means today that a memcpy is being made. Assignment of structs is already a potentially expensive operation. Making this user definable makes sense. This would among other things make it possible to create an efficient BigInt-like type that would behave as a primitive type. Ideally, structs should also have the possibility of defining a destructor that gets called when it goes out of scope. This would allow the implementation of ref-counted data and more. I.e. the following should work: T a,b; a = 17; b = a; a++; assert(a != b); for a custom type T, without the implementation of a++ needing to do defensive .dup-ing of the contained data. (For a BigInt, a++ is on average a very inexpensive operation but is unfortunately impossible to implement like that if the data may be shared and you have no way of knowing.) /Oskarthis has been brought up a number of times and I would rely like to see this kind of capacity added to D.I agree. This would be a worthwhile addition to D's template capabilities. Why do you think Oskar suggested that assignment operator overloading would be required?
Jan 30 2006