[% setvar title Transparently integrate C %]

This file is part of the Perl 6 Archive

Note: these documents may be out of date. Do not use as reference!

To see what is currently happening visit http://www.perl6.org/

TITLE

Transparently integrate tie

VERSION

  Maintainer: Nathan Wiger <nate@wiger.org>
  Date: 25 Sep 2000
  Last Modified: 30 Sep 2000
  Mailing List: perl6-language-objects@perl.org
  Number: 319
  Version: 2
  Status: Frozen

ABSTRACT

RFC 200 proposes many enhancements to tie to make it more versatile and multipurpose. However, it still relies on using the tie keyword to create a tied variable, keeping tie separate.

Python and lots of other languages have figured out how to implement fully integrated data, operator, and method overloading. Perl should too, without looking horrendously OO-ish like Python.

DESCRIPTION

Implicit tie

This RFC proposes that tie be integrated with Perl from the ground up, and not remain as a separate concept. Instead, classes that provide TIE* methods will have them automatically invoked on declaration. For example:

   my float $x = 5.3;    # float->TIESCALAR($x); $x->STORE(5.3);

The TIE* methods are called implicitly on variable declaration. So:

   my packed $a;         # packed->TIESCALAR($a);
   $a = get_binary;      # $a->STORE(...);
   $a++;                 # $a->STORE($a->PLUS(1));
   undef $a;             # $a->DESTROY;

   my bigint @b :128bit; # int->TIEARRAY(@b, '64bit')
   @c = @b;              # empty list passed still
   @b = (1,2);           # @b->CLEAR(...); @b->STORE(0,1); ...

Note that the TIE* methods will only be called if they exist, just like currently. If a given TIE* method does not exist, then the appropriate error should be spit out:

   my Pet @spot = ("fluffy");
   Can't locate method "TIEARRAY" via package "Pet"

In this case, the package Pet has declared that it can't handle arrays, which is just fine. Note that this does not imply all classes would have to have TIE methods; see further down under the section on the :autotie package attribute.

Passing arguments

Since many tied variables require that extra arguments be passed, this RFC proposes two ways of doing this. Either could be used, depending on a person's preference:

The general attribute style

In the attribute style, extra arguments are simply specified as attributes, which are then passed into the TIE* function as a hashref of values. So this:

   my Apache::Session %session :Transaction;

Would be the same as this in Perl 5:

   tie %session, 'Apache::Session', { Transaction => 1 };

A bare attribute is assumed to be a switch and is simply given a value of "1". Note that attributes allow you to specify arguments, too, so this:

   my Dog $spot :coat('shiny') :bark('rough') :mean;

Would result in the following call:

   Dog->TIESCALAR($spot, { coat => 'shiny', bark => 'rough',
                           mean => 1 });

This form has the benefit that it makes user-defined variables appear as transparent as builtins, and also gives the TIE constructors easy access to the attributes declared.

The special :tie variable attribute

With this version, you could use a special :tie attribute that would be passed to the TIE methods verbatim. So this:

   my Apache::Session %session :tie( { Transaction => 1 } );

Would have the same effect as the above. Note that the benefit of this way is that you can specify an arbitrary list of numbers or other arguments:

   my Matrix @a :tie(1,2,3,4,5);

You could perhaps accomplish that as either:

   my Matrix @a = (1,2,3,4,5);
   my Matrix @a :values(1,2,3,4,5);  # { values => [1,2,3,4,5] }

But TMTOWTDI. Still, this method may not be necessary; the first one appears to be sufficient by itself.

Optimization and Inheritance

One of the main goals behind doing something like this is being able to create custom variable types that can take advantage of optimizations, and having these variables walk and talk like builtins.

In fact, it is possible that variable declaration and optimization could be handled through basic inheritance in Perl 6. For example:

   package var;         # main variable class

   # all the main Perl internal methods are defined, such
   # as TIESCALAR, TIEARRAY, STORE, FETCH, etc

   package int;
   use base 'var';

   # ideas for RFC 303
   use optimize storage => 16,   # how much space
                growable => 1,   # can we grow?
                growsize => 8,   # how much to grow by
                integer => 1,    # support ints
                string => undef, # but not strings
                float => undef,  # or floats
                promote => 'bigint';  # promote to class
                                      # when outgrow

   # TIESCALAR, STORE, etc need not be redefined, since
   # they could simply inherit from var's, but perhaps
   # we could define special math ops per RFC 159.

In this example, we've used the int class to define several key optimizations for Perl to use. Since var is the grandfather class of all variables, its STORE and FETCH methods can be used, which actually do the internals of storing values and using the hints set by the use optimize pragma. Here, the use optimize pragma should be localizable and also inheritable, specifying characteristics for the package itself.

In reality, builtin types will be implemented in C and not Perl. However, that doesn't mean that other custom classes couldn't still inherit from these types as well. For example:

   package CoolInt;      # my own, real cool int
   use base 'int';
   # setup all my methods and optimizations

So, dispatch for builtin types could be very fast - the correct STORE et al methods (written in C) are simply called. Then, user-defined types would be derivable from builtin types with some slowdown, but nowhere near as bad as tie. Code could simply look like this:

   use CoolInt;
   my CoolInt $x = 42;       # CoolInt->TIESCALAR($x); $x->STORE(42);

Thus making it transparent to the user, and not requiring either of these:

   # Use our constructor
   my $x = CoolInt->new(42);

   # Use a tie interface
   tie CoolInt $x;
   $x = 42;

Finally, note that this RFC is not requiring that all builtin types use the embedded TIE approach. :-) Rather, int is just used as an example because it's easy to understand.

Assertion checking

With the above optimizations in place, it is quite possible for the proposed use strict 'types' to take advantage of them and automatically check that this:

   use strict 'types';
   my int $x = 5.3;

Should fail, simply by reading the optimizations from the int class and checking them against the value being assigned to $x.

This becomes even more integrated with Piers' proposed :isa attribute syntax:

   use strict 'types';
   my Pet $spot :isa(any(qw/Dog Cat Llama/));
   $spot = new Camel;        # fail

Now the compiler can check the type of $spot and verify the correct thing is supposed to happen before its STORE method is even called.

The :autotie attribute

In order to allow both autotied and other types of packages to easily coexist, there needs to be a way to inform the compiler that this package is providing an interface to automatically tie variables.

To do this, we add an :autotie attribute that can be specified on the package:

   package Pet :autotie;     # package will be auto-tied

This tells the compiler that any my or our declarations using this package as a type should result in that package's TIE* methods being automatically invoked.

As such, if the package Pet provides a TIESCALAR but not a TIEARRAY method, then the following:

   my Pet @pets;

Would result in an exception (same as with tie currently).

IMPLEMENTATION

The mild version

In this version, tie is implemented similarly to currently, only hopefully faster because of vtable stuff getting stuck in SV's. If you made it possible to inherit from builtin variable types like int and float, then this RFC would work pretty well. It would amount to basically a robust coat of sugar to make tied classes look like builtin types.

The over-the-top version

When taking this to its logical extreme, the idea is that the name of the method used to store data is STORE, even internally. Instead of mg.c having to call special functions and make special checks, STORE is called, whatever that STORE may be. If a variable is of type Pet, then that class's STORE is used, which may in fact be inherited from Mammal. Thus OO inheritance is embedded and fast.

This would only really work if the proposed embedded vtable stuff in Perl 6 was not dog slow. In fact, it would only work if the dispatch mechanism for this type of thing becomes really fast. If vtable stuff is embedded from the ground up, and all data stuff is implemented this way, it might be possible.

MIGRATION

This should be transparent if implemented correctly, and should not require migration.

REFERENCES

RFC 200: Objects: Revamp tie to support extensibility (Massive tie changes)

RFC 279: my() syntax extensions and attribute declarations

RFC 303: Keep use less, but make it work.

RFC 337: Common attribute system to allow user-defined, extensible attributes

RFC 265: Interface polymorphism considered lovely

RFC 218: my Dog $spot is just an assertion

RFC 137: Overview: Perl OO should not be fundamentally changed.

RFC 161: Everything in Perl becomes an object.

RFC 270: Replace XS with the Inline module as the standard way to extend Perl.