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 := [EMAIL 
PROTECTED]).

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/

Attachment: pgpZ4bQLJ5AWC.pgp
Description: PGP signature

Reply via email to