Skip to content
Find file
Fetching contributors…
Cannot retrieve contributors at this time
231 lines (164 sloc) 9.26 KB
=head1 INTRO
Blizkost Perl5 <-> Parrot interoperability semantics
The idea here is that migrating data between the Perl5 dataspace and the Parrot
dataspace is essentially the same problem as Parrot to C, and we need the same
technology - a FFI.
The fundamental object in Perl 5 is the B<SV>. A SV is a I<polymorphic
container>; it can hold a string, a floating point number, an integer, a
reference to another SV, a subroutine (not by reference), an array (not by
reference), a hash (not by reference), a file handle, a format (you don't want
to know), a symbol table entry (essentially a specialized hash mapping sigils
to SVs), or nothing at all. These are not mutually exclusive.
SVs have several behavioral restrictions. Most importantly, a SV which holds
an aggregate, cannot have ever held a different kind of data, and cannot change
to hold a different kind of data. However, non-aggreggate containers can
change value type freely.
SVs can also I<have magic>, which is kindof like traits, but more restricted.
SVs can be tied, which is like magic, except when it's not.
SVs are not garbage collected, instead they are I<reference counted>.
Perl extensions extensively rely on these behavioral quirks of SVs, as does the
core, except the core also depends on representation. I hold this responsible
for the death of ponie.
In an ideal world, it would always be possible for one Parrot module to simply
call into another, passing a PMC around, and everything would automatically
work through vtable magic. Unfortunately, some (many (most)) real languages
carry cultural B<universals> about what values do. These mismatches must be
handled at the Parrot I<import layer>.
There are two parties in any language clash. The B<aware component> knows that
there are two languages involved, and can accept glue responsibilities; the
B<unaware component> does not, and must not need to be modified. In current
Parrot, the aware component is always the importer and the unaware component
is always the exporter, but this is not required by the scheme detailed here;
it would be nice to also have the ability to write natively usable (say) Perl6
modules from Tcl with a minimum of fuss.
In order to minimize data damage, all coercion must occur as late as possible,
that is, I<import> time. When a value is imported into Perl5, it is turned
into some similar SV. By default, Blizkost uses the most conservative rules -
all non-readonly values are passed as tied objects. Compound and contravariant
objects require run-time coercion, which inherits the coercion rules of the
main import.
When importing or exporting a value, it should be possible to specify an object
(often a string, although other roles can also be used) along with the object
name. This allows tuning the mapping procedure; the best time to tune mapping
is at the same time as essential mapping, and data must therefore be passed to
that depth.
# from Perl5
use parrot Perl6 => 'Perl6Module';
# proxy objects are generated - slow but usually right
use parrot Perl6 => 'Perl6Module', -LOCAL => 'byvalue';
# values are copied - much faster
use parrot Perl6 => 'Perl6Module', -LOCAL => 'raw';
# we get PMC objects - all marshalling is our duty
# from Perl6
use Perl5Module:from<perl5>;
# proxy SVs used
use Perl5Module:from<perl5>:remote{:hot_spot<byvalue>};
# quicker, but not always right - now restricted to one symbol!
Blizkost relies on a number of B<mappers>. A mapper is something that knows
how to take a PMC and make it look like an SV, or vice versa. Mappers fall
into four groups, designated B<scalar export> (turns one PMC into one SV),
B<scalar import> (the reverse), B<list export> (turn a few PMCs into a few
SVs), and B<list import>.
Mappers are I<simply Parrot C<Callable>s> which operate on PMCs and SV handles,
so you can define your own if you want interesting semantics. See
In order to implement the mappers, it is necessary to write code (in one or
both universes) which can handle objects in the opposite world. For this
Blizkost provides B<handles> - two classes, C<perl5;Blizkost;PMC> and
C<parrot;Blizkost;SV> . These classes do I<no semantic translation>; they
simply present an object-oriented interface to data objects in the other world,
while mediating garbage collection.
If a mapper is called with no arguments (or some kind of void argument,
maybe?), it will return a stereotyped (but functionless) output object. This
will allow foreign importers in strongly typed languages to assign a better
type to such values.
Several simple mappers are provided for you; most of them only operate on a
single type, so are only useful in the context of a user-defined mapper. All
mappers are exported from C<parrot;Blizkost;Mappers>.
All mappers which copy values will set the copies to be read-only to catch
common aliasing errors.
Many of these functions are overloaded to handle both directions. This means
that you don't have to remember confusing arcane terminology like "exporter".
All of them will generate Parrot exceptions on range check error.
=head2 intvalue
This I<scalar importer> will coerce the SV to an integer, and return a new PMC
containing a copy of the value.
This I<scalar exporter> will call get_integer on a PMC, and return a new SV
containing the integer value.
=head2 numvalue
Same as intvalue, except for floating point.
=head2 strvalue
Same as intvalue, but for strings. Note that in Perl, string values are
logically immutable (although there are in-place optimizations for the case
where a string in memory is only held in one container).
=head2 scalarvalue
Combines intvalue, strvalue, and numvalue, and additionally will handle
"dualvar" objects such as Perl 5's C<$!> that have independant string and
number identities.
=head2 deeparray(CHILD)
Duplicates the immediate structure of the referent of a hash reference, or
object, using a child mapper to handle subobjects. Uses the array interface on
whatever it is copying.
=head2 deephash(CHILD)
Same as deeparray, but using string keys.
=head2 proxyscalar
Creates a general scalar container, which is magically bound to the source
object, such that any changes in the destination are reflected to the source.
The Perl-side scalar cannot hold references.
=head2 proxyarray(CHILD)
Creates a positional aggregate bound to the source object, using the CHILD to
handle importing and exporting children.
=head2 proxyhash(CHILD[, KEYCHILD])
Creates a keyed aggregate. KEYCHILD is used for SV-keyed tied hashes; if you
have one of these, you know. If it is not provided, the string interface will
be used.
=head2 asref(CHILD)
When importing, automatically dereferences Perl-side references. When
exporting, creates a new Perl-side reference to a value. You need this if you
want to put aggregates into other aggregates or a parameter list.
=head2 vectorize(CHILD)
Turns a scalar importer/exporter into a list version which treats arguments
=head2 wrapper(ARGS, RESULTS)
Imports and exports subroutine values by creating a wrapper sub on the other
side. Needs child I<list> mappers to handle boundary conversion.
=head2 objproxy(DEFARGS, DEFRES, {METHOD => [ARGS, RES]})
Creates an opaque proxy object, which relays only methods. If some methods
need special treatment, they can be named.
Attempting to use a Parrot continuation to exit the dynamic scope of a Perl 5
call won't do what you expect, unless you know how it's implemented, so don't
do that.
For actual exceptions, things are a bit easier. First, Perl 5 does not support
non-fatal exceptions or resuming handlers. If you throw a non-fatal exception
in Parrot code, it will be treated as unhandled and dropped on the floor after
it hits Perl 5. Fatal exceptions will propagate into Perl 5 and unwind the
Perl stack; when this happens, the resume continuation will be erased, as it
is not possible to un-unwind the Perl 5 activation stack.
Exceptions generated from Parrot are represented by a
C<perl5;Blizkost;ParrotException> object while in Perl. Exceptions generated
from Perl 5 are represented by a C<parrot;Blizkost;Perl5Exception> object while
in Parrot.
Blizkost provides a Perl 5 library which can be used to instantiate a Parrot
interpreter if needed and access Parrot modules. In general this is a bit
easier than the forward direction, as Parrot subs carry more metadata and
Parrot values are less overloaded; you should very rarely need to write custom
marshalling code.
A C<parrot> pragmatic module is provided to enable cross-language import.
use parrot java =>;
It will eventually be necessary to support multiple interpreters for a couple
reasons, principly that Perl interpeters cannot be shared between threads.
At any given time, each Parrot OS thread may have an attached Perl interpreter;
if no interpreters exist, one will autovivify if needed (but subsequent
interpreters must be explicitly created, for idiot resistance). The Blizkost
API uses a thread local context interpreter, like Perl itself.
vim: ft=pod
Jump to Line
Something went wrong with that request. Please try again.