develooper Front page | perl.perl6.compiler | Postings from July 2005

Definition of containers.

Thread Next
From:
Autrijus Tang
Date:
July 30, 2005 15:48
Subject:
Definition of containers.
Message ID:
20050730224910.GB5372@aut.dyndns.org
I have just checked in the container type part of the new PIL runcore:

    http://svn.openfoundry.org/pugs/src/PIL.hs

In the Pugs directory, you can run a sample test with:

    *PIL> tests
    ==> %ENV =:= %ENV;
    True
    ==> %ENV =:= %foo;
    False
    ==> untie(%ENV); my %foo := %ENV;
    ()
    ==> my %foo := %ENV;

Following is a semi-formal treatment for containers, directly transliterated
from the Haskell source.

Containers come in two flavours: Non-tieable and Tieable.  Both are typed,
mutable references.  There is no way in runtime to change the flavour.

    data Container s a
	= NCon (STRef s (NBox a))
	| TCon (STRef s (TBox a))

A Non-tieable container is comprised of an Id and a storage of that type, which
can only be Scalar, Array or Hash.  Again, there is no way to cast a Scalar
container into a Hash container at runtime.

    type NBox a = (BoxId, a)

A Tieable container also contains an Id and a storage, but also adds a
tie-table that intercepts various operations for its type.

    type TBox a = (BoxId, a, Tieable a)

The type of tie-table must agree with the storage type.  Such a table
may be empty, as denoted by the nullary constructor "Untied".  Each of
the three storage types comes with its own tie-table layout.

    data Tieable a where
	Untied     :: Tieable a
	TieScalar  :: TiedScalar -> Tieable Scalar
	TieArray   :: TiedArray  -> Tieable Array
	TieHash    :: TiedHash   -> Tieable Hash

Binding only happens between containers of the same type:

    bind :: Container s a -> Container s a -> ST s ()

Additionally, the compiler needs to compile ($x := @y) into ($x := \@y).

To bind a container to another, we first check to see if they are of the
same tieableness.  If so, we simply overwrite the target one's Id,
storage and tie-table (if any):

    bind (TCon x) (TCon y) = writeSTRef x =<< readSTRef y
    bind (NCon x) (NCon y) = writeSTRef x =<< readSTRef y

To bind an non-tieable container to a tieable one, we implicitly remove
any current ties on the target, although it can be retied later:

    -- %*ENV := %foo
    bind (TCon x) (NCon y) = do
	(id, val) <- readSTRef y
        writeSTRef x (id, val, Untied)

To bind a tieable container to a tied one, we first check if it is
actually tied.  If yes, we throw a runtime exception.  If not, we
proceed as if both were non-tieable.

    -- %foo := %*ENV
    bind (NCon x) (TCon y) = do
	(id, val, tied) <- readSTRef y
	case tied of
	    Untied -> writeSTRef x (id, val)
	    _      -> fail "Cannot bind a tied container to a non-tieable one"

We can compare two containers for Id equivalence.  If the container types
differ, this should never return True:

    (=:=) :: Container s a -> Container s b -> ST s Bool
    x =:= y = do
	x_id <- readId x
	y_id <- readId y
	return (x_id == y_id)

Untie an non-tieable container is a no-op:

    untie (NCon x) = return ()

For a tieable container, we first invokes the "UNTIE" handler, then set
its "tied" slot to Untied:

    untie (TCon x) = do
	(id, val, tied) <- readSTRef x
	case tied of
	    Untied  -> return ()
	    _       -> do
		tied `invokeTie` UNTIE
		writeSTRef x (id, val, Untied)

Thanks,
/Autrijus/


Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About