Thread overview
struct subtyping?
Oct 24, 2010
spir
Oct 24, 2010
Simen kjaeraas
Oct 24, 2010
spir
Oct 24, 2010
spir
Oct 24, 2010
bearophile
Oct 24, 2010
bearophile
Oct 24, 2010
spir
Oct 25, 2010
Austin Hastings
October 24, 2010
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 be 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 literal notation for struct "specimens" (instances). This brings me handy plain 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 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. 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?


Thank you,
Denis

(*) Oberon is rather close to D on this point: it has records with "type-bound procedures" (read "methods"). But these records are "extendable" (read "subtype-able"). Methods are bound via dynamic (single) dispatch. D-like classes are provided by record-pointer types, on instances of which dereferencing is automatic (when accessing slots) (only the type def explicitely mentions reference).
-- -- -- -- -- -- --
vit esse estrany ☣

spir.wikidot.com

October 24, 2010
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
October 24, 2010
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
October 24, 2010
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
October 24, 2010
On Sun, 24 Oct 2010 23:41:07 +0200
"Simen kjaeraas" <simen.kjaras@gmail.com> wrote:

> 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.

All right, if I understand correctly, this is very similar to Lua metatables. (The __index field of an object's metatable tells what to do when slot lookup fails.) If I'm right, this would allow type extension by storing additional 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 stored. Or unions (but I don't know how they work actually). Alias this seems better since lookup mechanism would be "automagic", if I understand correctly.

> 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 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.

All right, thank you! (This is also the reason why Oberon prohibits returning 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 ☣

spir.wikidot.com

October 24, 2010
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...

Denis
-- -- -- -- -- -- --
vit esse estrany ☣

spir.wikidot.com

October 24, 2010
On Sun, 24 Oct 2010 23:41:07 +0200
"Simen kjaeraas" <simen.kjaras@gmail.com> wrote:

> 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.

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 = NamedConstant(3.14,"pi") ;
    writefln("%s * 2 = %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 instance of root type? (I had thought exactly the opposite.)


Denis
-- -- -- -- -- -- --
vit esse estrany ☣

spir.wikidot.com

October 25, 2010
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