I have some thoughts on vtables and I actually think they should be pretty
different from what was proposed. Well, I'll tell you my ideas on vtables.
Please note that I'm not doing this as an intent to confront the other
proposal, or to create discordance and divergence. I'm presenting this
because I think the other presented approach has some weak points, and I
think this proposal here could solve them someway.



I'm writing it under these assumptions:

1. There should be different vtables for scalars, arrays and hashes. In
every use of a variable in the language, it's always perfectly clear what
the variable type is, only by the syntax, so that I don't see a reason for
having one vtable that should be used for all kinds of variable.

2. Perl 5 doesn't separate well a `variable' from a `value', and this should
be done to achieve a more clear design. For example, a _variable_ should
know how to _store_ or _fetch_ a _value_, but the _value_ should know how to
be _added_ to another _value_. (Sorry for the underlines...). Arrays and
hashes are actually like this, but I'm seeing the other proposal that for
scalars it's not.

3. We should have an interface that resembles current `tie' interface for
variable-vtables, and an interface that resembles current `overload'
interface for value-vtables. This is enough low-level for implementing basic
operations on variables/values, and is enough high-level to implement `tie'
and `overload' directly above it.


Under this decisions, I'll expose my proposal. It's all in pseudo-C (and a
tiny bit of C++). I've changed the current Perl5 names a bit, to make the
separation between variable/value more clear. I'm probably missing much
here, and I may be very wrong about it, consider this only a preliminary
draft.


   template<class vtable_t>
   struct PMC {
      vtable_t   *vtable;
      void       *data;
      int         flags;
      void       *gc_data;
      void       *thr_data;
   };

   struct SVAR_PMC : public PMC<svar_vtable>;
   struct AVAR_PMC : public PMC<avar_vtable>;
   struct HVAR_PMC : public PMC<hvar_vtable>;
   struct HNDL_PMC : public PMC<hndl_vtable>;

   struct SVAL_PMC : public PMC<sval_vtable>;


Just couldn't resist... :-)
They are essentially the same, only each one carries its own type of vtable.
And I think strong typing should be ensured here, so that a AVAR cannot be
passed where a SVAR is expected, at least not without an explicit cast. (I
actually regard an explicit cast as signing the contract that says that you
know what you're doing...).

SVAR_PMC, AVAR_PMC, HVAR_PMC correspond to Perl5's SV, AV, HV. HNDL_PMC is
for file handles, and SVAL_PMC is for scalar values. The next session, on
variable vtables, is mainly taken from perltie. Arrays and hashes are
`crippled', they're here only to illustrate. The this argument is always the
one that has the vtable. Where return values are needed, they are the first
argument of the function. I included a `TYPE' slot to indicate the type of
the vtable (svar_vtable, avar_vtable, ...), althought I didn't find a use
for it. It can be used to identify if a variable is a scalar or an array,
but I really don't see much use for that, unless for sanity checking.


   struct svar_vtable {
      int     TYPE;
      void  (*FETCH)   (SVAL *result,  SVAR *this);
      void  (*STORE)   (SVAR *this,    SVAL *value);
      void  (*DESTROY) (SVAR *this);
   };

   struct avar_vtable {
      int     TYPE;
      void  (*FETCH)   (SVAL *result,  AVAR *this,  int index);
      void  (*STORE)   (AVAR *this,    int index,   SVAL *value);
      int   (*LENGTH)  (AVAR *this);
      void  (*DESTROY) (AVAR *this);
   };

   struct hvar_vtable {
      int     TYPE;
      void  (*FETCH)   (SVAL *result,  HVAR *this,  SVAL *key);
      void  (*STORE)   (HVAR *this,    SVAL *key,   SVAL *value);
      void  (*NEXTKEY) (SVAL *result,  HVAR *this,  SVAL *lastkey);
      void  (*DESTROY) (HVAR *this);
   };


Filehandles also follow the perltie model:


   struct hndl_vtable {
      int     TYPE;
      void  (*GETLINE) (SVAL *result,  HNDL *this);
      void  (*PRINT)   (HNDL *this,    SVAL **value);
      void  (*DESTROY) (HNDL *this);
   };


Please note that I cut most the things off here. SPLICE for arrays, EXISTS
and DELETE for hashes, READ and WRITE for handles, ... This is only meant to
be a draft.

Values are the new thing here. I think they correspond to Perl5's XIV, XPV,
..., but I'm not sure (I don't know the internals of Perl5 that well, I only
wrote some XS extensions... Well, actually some heavy ones...). Below are
the things I believe should be together with the value. I mostly borrowed
(and shortened) the names from Dan's vtable PDD.


   struct sval_vtable {
      int     TYPE;
      // type conversion
      STR   (*STRING)  (SVAL *this);
      int   (*INTEGER) (SVAL *this);
      NUM   (*NUMBER)  (SVAL *this);
      int   (*BOOL)    (SVAL *this);
      // operations (from overload.pm)
      void  (*ADD)     (SVAL *result,  SVAL *this,  SVAL *value);
      void  (*SUB)     (SVAL *result,  SVAL *this,  SVAL *value);
      void  (*MUL)     (SVAL *result,  SVAL *this,  SVAL *value);
      void  (*DIV)     (SVAL *result,  SVAL *this,  SVAL *value);
      void  (*MOD)     (SVAL *result,  SVAL *this,  SVAL *value);
      void  (*CONCAT)  (SVAL *result,  SVAL *this,  SVAL *value);
      void  (*REPEAT)  (SVAL *result,  SVAL *this,  SVAL *value);
      void  (*BITOR)   (SVAL *result,  SVAL *this,  SVAL *value);
      void  (*BITAND)  (SVAL *result,  SVAL *this,  SVAL *value);
      void  (*BITNOT)  (SVAL *result,  SVAL *this,  SVAL *value);
      void  (*SHL)     (SVAL *result,  SVAL *this,  SVAL *value);
      void  (*SHR)     (SVAL *result,  SVAL *this,  SVAL *value);
      // references
      void  (*DEREF_SCALAR)  (SVAR *result,  SVAL *this);
      void  (*DEREF_ARRAY)   (AVAR *result,  SVAL *this);
      void  (*DEREF_HASH)    (HVAR *result,  SVAL *this);
      void  (*DEREF_HANDLE)  (HNDL *result,  SVAL *this);
      // OO-stuff
      STR   (*CLASS)   (SVAL *this);
      void  (*METHOD)  (SUB_PMC *res,  SVAL *this,  STR meth_name);
      // cleanup
      void  (*DESTROY) (SVAL *this);
   };



The purpose of separating values from variables this way and giving values a
vtable of their own is that this way operations that apply to particular
values would be carried to the value independent of the variable it is
assigned to. See assumption [2] on the top. SVAR is responsible for
STORE/FETCH and SVAL is responsible for STRING/INT/NUMBER/BOOL, and other
things that a value should know.

One problem that comes up here is the handling of the `value' parameter in
the arithmetic functions. A typical vtable would tipically call the
STRING/INT/NUMBER/BOOL function of the `value' that is passed and operate
this value with its own value. This raises a problem if the left operand
(the one that is passed as `this', and whose vtable is used) is a simple int
value, but the right operand (`value' parameter) is a bigint, or something
that has the function overloaded. The left operand should have a way to
inspect its right operand and determine if that operand's vtable should be
used to do the operation instead of the simple one. Any idea on how to solve
it? The bigint/bigfloat particular case could be solved by having code that
inspects right operands for bigints/bigfloats and do the right thing, even
in the simple vtables, but what about the general case? Perhaps a
IADD/ISUB/IMUL (I stands by Inverse) to be used in case the right operand
should do the operation?



Well, I think implementation of tie and overload is pretty obvious from
here, right? Tying a variable would be actually setting the variable's
vtable to do the tied magic, and overloading would be setting the value's
vtable to do the overloaded stuff.

The trivial vtable for scalar variables uses a SVAL * directly in its `data'
PMC slot. STORE only sets `data' to the new SVAL, FETCH only returns `data'
into the result, and DESTROY does nothing. That's really all that's needed,
except for GC and thread synchronizing stuff. A more complex tied-like
variable could write the string value of the SVAL into a file on STORE, read
the file's content with FETCH and close/unlink the file on DESTROY, which
demonstrates the tie functionality.

Perl would have built-in some sval_vtables for different kinds of values.
For example, it would have one for simple scalars (such as ints or strings)
that would probably store a structure with a buffer, a length, an integer
value, a float value and some flags indicating which of them are valid, in
the same way Perl5 keeps it now. The sval_vtable that would take care of
this kind of structure would have STRING/INT/NUMBER/BOOL functions seeing if
the value is already there, converting as needed, and returning the value.
ADD/SUB/MUL/... are trivial. DEREF_* would `die' or throw an exception
anyhow, so would METHOD. CLASS would return "SCALAR", right? (I don't know
if it's "SCALAR" or ""...). And DESTROY would do nothing, perhaps freeing
some buffer memory though.

An array reference `\(1..4)' would use an AVAR * as the `data' slot of its
PMC. In the sval_vtable correspondent to an array reference, STRING/INT
would return the address of the array, the same way Perl5 does, BOOL would
always return true (since undef is not an array reference and should have
probably another sval_vtable). ADD/SUB/MUL/... would throw errors? Convert
to int than do the operation? Anyway it's trivial. DEREF_ARRAY would return
the AVAR * value directly from the `data' slot of the SVAL_PMC, DEREF_* for
the others would throw exception. CLASS would return "ARRAY". METHOD would
throw exception, or return NULL, and DESTROY would do nothing as well,
perhaps some GC stuff, I don't know about that.

I found all this trivial, would probably be very easy to implement. Maybe
I'm forgetting about some special cases, please point them if you find them.

As this is (kind of) Perl5's current interface of `tie' and `overload', I
think it would be enough high level to be used by both the language and the
extensions. Of course, I could be wrong...



I actually have some more on it, but I'm saving it for the next postings.
I'll wait for your opinions first. I really hope to see critics about this.
I'd really like to read them so please send them in!!!

- Branden

Reply via email to