www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - struct subtyping?

reply spir <denis.spir gmail.com> writes:
Hello dear D community,

I need to express a system of related types. The values are actually values=
, meaning I absolutely need value semantics (no referencing, else I would b=
e forced to explicitely copy on each assignment). Also, they are structured=
, record-like thingies.
I was very pleased to discover that D has class-like structs: we can define=
 methods and even custom constructors. Even nicer, it provides a kind of li=
teral notation for struct "specimens" (instances). This brings me handy pla=
in value records. Great!

But for any reason, this logic is not pushed to the point of providing type=
 hierarchy by subtyping. It would have been great for me, since much of the=
 common functionality is generic. Without a type hierarchy, I need to dupli=
cate it on each struct type, which is _bad_ (as any programmer knows ;-).

I would like to learn about possible workarounds, or other alternative appr=
oaches to such problems, if ever you now any. Also, I would love to read ab=
out the rationale for _not_ having struct type hierarchies (probably this w=
ould not be a big deal, since D has it for classes (*)) Or maybe I have sim=
ply not correctly understood available docs?


Thank you,
Denis

(*) Oberon is rather close to D on this point: it has records with "type-bo=
und procedures" (read "methods"). But these records are "extendable" (read =
"subtype-able"). Methods are bound via dynamic (single) dispatch. D-like cl=
asses are provided by record-pointer types, on instances of which dereferen=
cing is automatic (when accessing slots) (only the type def explicitely men=
tions reference).
-- -- -- -- -- -- --
vit esse estrany =E2=98=A3

spir.wikidot.com
Oct 24 2010
next sibling parent reply "Simen kjaeraas" <simen.kjaras gmail.com> writes:
spir <denis.spir gmail.com> wrote:

 I would like to learn about possible workarounds, or other alternative  
 approaches to such problems, if ever you now any.
Basically, you probably want to use alias this: http://digitalmars.com/d/2.0/class.html#AliasThis It lets you subtype structs by propagating member field/function access to a member, if the struct or class does not implement the field or function itself. alias this is panacea - if a function expects a base struct, and returns it after doing some alterations, you cannot cast the result to a subtype (see below).
 Also, I would love to read about the rationale for _not_ having struct  
 type hierarchies (probably this would not be a big deal, since D has it  
 for classes (*)) Or maybe I have simply not correctly understood  
 available docs?
The main reason is the slicing problem. If the language lets a user store a struct that is a subtype of another struct, and that adds fields, in a location where space is only allocated for the base struct, the extra fields are eaten by grues, and bugs appear. -- Simen
Oct 24 2010
next sibling parent spir <denis.spir gmail.com> writes:
On Sun, 24 Oct 2010 23:41:07 +0200
"Simen kjaeraas" <simen.kjaras gmail.com> wrote:

 spir <denis.spir gmail.com> wrote:
=20
 I would like to learn about possible workarounds, or other alternative =
=20
 approaches to such problems, if ever you now any.
=20 Basically, you probably want to use alias this: =20 http://digitalmars.com/d/2.0/class.html#AliasThis =20 It lets you subtype structs by propagating member field/function access to a member, if the struct or class does not implement the field or function itself.
All right, if I understand correctly, this is very similar to Lua metatable= s. (The __index field of an object's metatable tells what to do when slot l= ookup fails.) If I'm right, this would allow type extension by storing addi= tional fields/methods on a kind of sub-struct? (But not method overriding, = I guess; which anyway would require type-bound method dispatch). I also considered having a void* field where type-specific slots would be s= tored. Or unions (but I don't know how they work actually). Alias this seem= s better since lookup mechanism would be "automagic", if I understand corre= ctly.
 alias this is panacea - if a function expects a base struct, and returns
 it after doing some alterations, you cannot cast the result to a subtype
 (see below).
Do you mean "alias this _not_ is panacea"?
 Also, I would love to read about the rationale for _not_ having struct =
=20
 type hierarchies (probably this would not be a big deal, since D has it=
=20
 for classes (*)) Or maybe I have simply not correctly understood =20
 available docs?
=20 The main reason is the slicing problem. If the language lets a user store a struct that is a subtype of another struct, and that adds fields, in a location where space is only allocated for the base struct, the extra fields are eaten by grues, and bugs appear.
All right, thank you! (This is also the reason why Oberon prohibits returni= ng from funcs dynamic arrays and record: their size is not known at compile= -time -- the latter because they can be extended/subtyped. So that we need = to use pointers). Denis -- -- -- -- -- -- -- vit esse estrany =E2=98=A3 spir.wikidot.com
Oct 24 2010
prev sibling parent spir <denis.spir gmail.com> writes:
On Sun, 24 Oct 2010 23:41:07 +0200
"Simen kjaeraas" <simen.kjaras gmail.com> wrote:

 spir <denis.spir gmail.com> wrote:
=20
 I would like to learn about possible workarounds, or other alternative =
=20
 approaches to such problems, if ever you now any.
=20 Basically, you probably want to use alias this: =20 http://digitalmars.com/d/2.0/class.html#AliasThis =20 It lets you subtype structs by propagating member field/function access to a member, if the struct or class does not implement the field or function itself.
Seems I first misunderstood: alias this lets one alias _one_ field as fake = this? I experimented with this: struct NamedConstant { float value ; string name ; alias value this ; string toString () { return this.name ;} } void main () { NamedConstant PI =3D NamedConstant(3.14,"pi") ; writefln("%s * 2 =3D %s", PI, 2*PI) ; } This is similar to the ability offered by some languages to subtype builtin= types, no? The slot on which lookup is delegated should actually be an ins= tance of root type? (I had thought exactly the opposite.) Denis -- -- -- -- -- -- -- vit esse estrany =E2=98=A3 spir.wikidot.com
Oct 24 2010
prev sibling next sibling parent bearophile <bearophileHUGS lycos.com> writes:
spir:

 But for any reason, this logic is not pushed to the point of providing type
hierarchy by subtyping. It would have been great for me, since much of the
common functionality is generic. Without a type hierarchy, I need to duplicate
it on each struct type, which is _bad_ (as any programmer knows ;-).
 
 I would like to learn about possible workarounds, or other alternative
approaches to such problems, if ever you now any.
Beside the alias this answered by Simen kjaeraas, another option is to use template mixin to factorize common code, but then you will have to manage the hierarchy manually. You may even add a common enum tag (present in all your structs as first field or among the fields of the root of your hierarchy) that encodes the type of the struct. Sometimes to save space you may find a way to encode this tag inside some other number or even pointer. I have done all this in D, and it's not handy, it's error-prone, and the language never helps you much. I have build a generic TaggedPointer struct, but often that's not the best solution. It seems the Zen of the D language is to give you a safe high-level way to do something. If you refuse to use it then you are on your own, as in C, and the language doesn't catch your bugs. I don't love this much, but it helps keep the language simpler.
 (*) Oberon is rather close to D on this point:
Probably there are some other points where Oberon is similar to D. Bye, bearophile
Oct 24 2010
prev sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
spir:

 But for any reason, this logic is not pushed to the point of providing type
hierarchy by subtyping. It would have been great for me, since much of the
common functionality is generic. Without a type hierarchy, I need to duplicate
it on each struct type, which is _bad_ (as any programmer knows ;-).
Can you explain your use case better? I am curious. I have used a hierarchy of structs in D in a small raytracer, to encode 3D objects. Bye, bearophile
Oct 24 2010
parent reply spir <denis.spir gmail.com> writes:
On Sun, 24 Oct 2010 18:54:15 -0400
bearophile <bearophileHUGS lycos.com> wrote:

 spir:
=20
 But for any reason, this logic is not pushed to the point of providing =
type hierarchy by subtyping. It would have been great for me, since much of= the common functionality is generic. Without a type hierarchy, I need to d= uplicate it on each struct type, which is _bad_ (as any programmer knows ;-= ).
=20
 Can you explain your use case better? I am curious. I have used a hierarc=
hy of structs in D in a small raytracer, to encode 3D objects. I cannot explain in detail, because it's still vague in my mind. It would b= e for a toy OO dynamic language. The root struct type would represent to ro= ot "element" (piece of data) type. Then, the whole D-struct hierarchy would= mirror the source language's type hierarchy. I want a type hierarchy so that I can directly implement generic core langu= age features (that a record can store any kind of element) and types (eg co= llections). Also, every element of the language itself would be a record-el= ement, including types, methods, scopes... Denis -- -- -- -- -- -- -- vit esse estrany =E2=98=A3 spir.wikidot.com
Oct 24 2010
parent Austin Hastings <ah08010-d yahoo.com> writes:
On 10/24/2010 7:19 PM, spir wrote:
 On Sun, 24 Oct 2010 18:54:15 -0400
 bearophile<bearophileHUGS lycos.com>  wrote:

 spir:

 But for any reason, this logic is not pushed to the point of providing type
hierarchy by subtyping. It would have been great for me, since much of the
common functionality is generic. Without a type hierarchy, I need to duplicate
it on each struct type, which is _bad_ (as any programmer knows ;-).
Can you explain your use case better? I am curious. I have used a hierarchy of structs in D in a small raytracer, to encode 3D objects.
I cannot explain in detail, because it's still vague in my mind. It would be for a toy OO dynamic language. The root struct type would represent to root "element" (piece of data) type. Then, the whole D-struct hierarchy would mirror the source language's type hierarchy. I want a type hierarchy so that I can directly implement generic core language features (that a record can store any kind of element) and types (eg collections). Also, every element of the language itself would be a record-element, including types, methods, scopes...
I would recommend that you reconsider not wanting reference semantics for this. If you're doing a dynamic language, you probably don't want to represent "int" as a struct, but rather some BigInt or Scalar or some such. And then you almost immediately want to use pass-by-reference on that. You might define a common record-keeping element that is value based, and store that in a struct in the lowest-level object class. Also, keep in mind that the struct mechanism does not do dispatching at all. It simply knows what the type is, and invokes the appropriate function directly. This is more performant, especially at the low level where VM ops would be implemented, but it also means you have to know the type in question. If you are doing some kind of switch on an opcode type, your opcodes may encode the type of the operands. But it would have to be a one-to-one encoding, because there is no virtual dispatch with structs. So push-string has to be different from push-int and push-float, for example. On the other hand, you might make your object references a struct type, with the expectation that the set of operations defined on an object ref is constant. Then the object will respond to "methods" but the object reference struct would respond to "ops", and one "op" would be "call method". =Austin
Oct 25 2010