By way of introduction, we quickly hit the law of diminishing returns
when we spend a lot of time justifying the existence of Parrot.
Especially when we could be spending that time finishing off Parrot. At
the end of the day, no amount of talking will convince the skeptics. A
completed virtual machine in production use will convince more, and make
the remaining skeptics irrelevant.
That said, I've spent some time here in the hope that this can work its
way into a document, so next time these questions come around we can say
"read the FAQ".
Will Coleda wrote:
1. Why Parrot?
http://www.parrotcode.org/docs/intro.html:
"Parrot is designed with the needs of dynamically typed languages
(such as Perl and Python) in mind, and should be able to run programs
written in these languages more efficiently than VMs developed with
static languages in mind (JVM, .NET). Parrot is also designed to
provide interoperability between languages that compile to it. In
theory, you will be able to write a class in Perl, subclass it in
Python and then instantiate and use that subclass in a Tcl program."
a. What, precisely, about Parrot makes possible more efficient
execution of a dynamically typed language than would be the case with
the JVM or the CLR?
This is an old FAQ entry that needs to be updated. At the time it was
written, there was an enormous gap between Parrot and the performance of
the JVM/CLR in handling dynamic languages. (Note: not just "dynamically
typed languages". Dynamic typing is one small aspect of dynamic
languages in general.)
The JVM & CLR have made progress, but they're still not all the way to
full handling of dynamic languages. They think they are, and are quite
happy to sit back and declare that they have the dynamic languages
problem licked. The only way they're ever going to tackle the full
problem is if someone else does it first to show them how. Just like the
only reason they ever tried working on dynamic languages in the first
place was because Parrot was there to show that a virtual machine could
do more than they imagined possible.
Back to the specific question: dynamic typing is probably one of the
least interesting features of Parrot. It's handled by the fact that a
PMC container can hold any type of PMC, and assignment between types is
handled by a set of standard vtable functions on the source PMC for
retrieving its value and on the destination PMC for setting its value.
b. Whatever that is, how will it adversely impact the execution of
statically typed languages, including type-inferred languages?
Since type declarations are not required, implementing a statically
typed language on a dynamically typed virtual machine means adding
additional code to do the type checking. This is likely less efficient
than implementing a statically typed language on a statically typed
virtual machine (though, likely not much less efficient, since the
statically typed virtual machine still has to do the type checking).
Someday we'll run some benchmarks to compare.
c. How will this impact the execution of statically typed code in
Perl, Python and other targeted languages?
That question doesn't make sense. Perl and Python don't have statically
typed code. See 1b for comments on efficiency of statically typed languages.
In general, we aren't targeting static languages (again, I'm referring
to a more general category than just "statically typed languages"). We
may implement a few static languages on Parrot so they can use Parrot's
libraries and tools. Static languages don't take advantage of the
advanced features of a dynamic virtual machine. They may not be fast in
an interpreted environment because they aren't designed for an
interpreted environment. They're specifically designed to take advantage
of a statically compiled environment by eliminating all dynamic
features. I'm not going to get into a discussion of whether static
languages or dynamic languages are "better". They're just different
creatures, and comparing them is about as useful as comparing a hybrid
car optimized for fuel economy with a race car optimized to win the
Daytona 500.
2. General Features
a. How will Parrot support reflection and attributes?
Simply put, reflection is the ability for code to access information
about itself and modify its own behavior. This is a concept so deeply
ingrained in the design of Parrot that the hard part of answering the
question isn't figuring out how Parrot supports reflection, but figuring
out which particular features to highlight. How about the fact than
instead of using a stack to control the flow of a program, it uses
introspectable continuation objects? Or the fact that subroutines,
exceptions, and namespaces are all introspectable, modifiable, and can
be replaced by subclasses with different behavior as long as they
respect the interface.
By "attributes" do you mean instance variables for objects (what Parrot
calls an attribute), or do you mean auxiliary data attached to an object
(what the CLR calls an attribute, and Parrot calls a property). Parrot
implements both.
b. How will Parrot support generics types?
Generics are mostly a static language way of allowing a tiny bit of
semi-dynamic behavior. There are many ways to do the same thing in
Parrot. The most literal-minded parallel would be declaring a class that
accepts a parameter on instantiation specifying the type of some element
that it contains or operates on. For example, the following code might
be used to instantiate a generic aggregate that holds elements of type
MyElement:
$P0 = find_class MyGenericAggregate
$P1 = $P0.new(elementtype=>'MyElement')
c. How will Parrot support interface types?
An interface is a role with no attributes and no implementation for its
methods (or, more specifically, it has implementations for its methods
that throw an exception complaining that the method needs to be
implemented).
d. What kind of security models will Parrot support?
The architecture is still in early stages. (Let us know if there's a
particular model you need.) In general we're taking a sandbox approach.
e. How will Parrot support small-footprint systems?
The absolute minimum needed to run Parrot is a bytecode interpreter.
We're aiming to fit a bytecode interpreter in 32M. Extremely small, but
that's what's needed to run on most modern cell phones. There isn't
currently active development work in this area.
f. How will Parrot support direct access to "unmanaged" resources?
My original answer was that "unmanaged" resources are a notion from the
CLR, and not really meaningful in the Parrot context. But from your
followup message it sounds like you actually mean access to C-level
resources. UnmanagedStruct (which Joshua mentioned) is one way, and
provides a Parrot interface to a C structure that isn't memory managed
by Parrot. NCI is another way, allowing calls into and returns from C
functions. In general, Parrot will never provide direct access to
C-level resources, it will only provide access through an interface,
where the interface handles the translation between C-like behavior and
Parrot-like behavior. As much as possible, those interfaces are defined
in a standard way so that using a new external resource from within
Parrot doesn't require writing an entirely new interface layer from
scratch, but only requires reusing, or easily extending an existing
interface layer.
g. How will Parrot facilitate distributed processing?
Parrot will provide a standard set of tools for concurrency. And, the
fact that Parrot provides a standard interface across multiple platforms
is an advantage to distributed processing. Distributed code can be
written to run on the Parrot virtual machine, abstracting away from the
details of the particular hardware or operating system. We'll also
provide a standard set of networking protocols, and tools for managing
some of the standard distributed "messaging" systems (SOAP, JSON, AJAX,
etc). We don't currently have any plans to support more advanced forms
of serializing an execution state to carry between systems, but they
could be hooked into the core (extensible) concurrency framework.
3. Parrot PMC Issues
The Parrot PMC vtable provides a large number of optional functions,
which PMCs can either implement or not. If not implemented, they will
throw an exception at runtime.
Or, will fall back to a default implementation.
a. What support will Parrot provide a compiler to interrogate a PMC at
compile time to know what it actually implements?
Some custom vtable functions like "can", "does", and "isa", but when all
else fails, you can fall back on "inspect" which gives you all the
nitty-gritty details about a PMC/object.
All of these functions appear to be predefined because there is no
mechanism for extending this functionality at runtime. It appears that
compilers will be limited to implementing functionality that is
defined in the vtable. The vtable contains the common operations
required by certain languages.
What kind of dynamic virtual machine would we be if we didn't allow
functionality to be extended at runtime? PMCs are just objects. To
extend the functionality at runtime, subclass the PMC and extend the
subclass. Or, add a runtime role to the object.
b. How will Parrot handle languages with operations that are not
provided?
Parrot provides low-level operations. If the HLL has a more complex
feature, the compiler translates it down to a series of low-level
operations. Ultimately all languages compile down to machine code, so
I'm not at all likely to be convinced that an HLL may come up with a
feature that can't be implemented as a combination of low-level operations.
http://www.parrotcode.org/docs/vtables.html:
"To be perfectly honest, this is a slightly flawed example, since it's
unlikely that there will be a distinct "Python scalar" PMC class. The
Python compiler could well type-inference variables such that a would
be a PythonString and b would be a PythonNumber. But the point remains
- incrementing a PythonString is very different from incrementing a
PerlScalar."
This document is very old, and currently being replaced.
c. How will Parrot address cross-language semantics?
All languages are implemented in terms of low-level Parrot operations. A
given language can have any semantics it wants, it just composes that
behavior from low-level components. When one language is interacting
with objects from another language, it performs standard Parrot
operations on those objects, so the objects can respond in the same way
they would respond to the same operations called from their own language.
(I once spent an hour trying to explain this to the CLR guys and they
just couldn't understand it.)
d. Will each language have to provide its own support for interacting
with PMCs for other languages?
No, all they need to do is call standard opcodes on the PMC from other
languages. Each PMC provides its own vtable functions for (as an
example) returning a string, integer, number, or PMC value.
e. How will a PerlScalar interact with a PythonString?
Interact how? If you assign the value of a PythonString to a PerlScalar,
the value will be extracted by a standard vtable function on the
PythonString, and assigned by a vtable function on the PerlScalar.
f. What will happen when a PythonString is incremented in Perl code?
The code will call the increment vtable function on the PythonString PMC.
Comparing the vtable for a PMC to the JVM and CLR base Object classes,
the PMC is essentially an "abstract" class with dozens of
"unimplemented" methods, while Java's Object provides (and implements)
the following public methods:
equals getClass hashCode notify notifyAll toString wait
This doesn't make any sense. Are you comparing Parrot's "default" PMC to
Java's Object? It would make more sense to compare Parrot's Object to
Java's Object.
Discounting the methods related to Java's peculiar threading
implementation, that's:
equals getClass hashCode toString
Similarly, the CLR's CTS Object provides:
Equals ReferenceEquals GetType GetHashCode ToString
And Parrot's Object provides:
isa can does get_attr set_attr get_class inspect name find_method
g. Why is it a good thing that PMCs essentially non-contractual
abstract base classes that define a lot of functionality without
implementing it?
Parrot implements sane defaults for the majority of PMCs. But
considering the wide range of types implemented as PMCs (scalars,
aggregates, subroutines, coroutines, continuations, namespaces,
exceptions, threads, iterators, STM, etc) there really isn't a sane
default for most operations that will apply to all PMC types. So,
instead, the sane defaults are built up by a chain of inheritance. For
example, all the different array types inherit sane defaults from a core
array type.
Perhaps what you're trying to ask is why we define a standard set of
vtable operations, instead of allowing a PMC to create any arbitrary
vtable entries. This goes back to the core design of interoperability
across languages. All PMCs are guaranteed to provide a sane response to
the standard vtable operations, even if that response is a "not
implemented" exception.
h. Why is there no first-tier depth in Parrot's type system, such as:
PMCString, PMCIntger, PMCNumber, ...
At the time the initial PMC system was implemented, Parrot had no notion
of namespaces. That is changing now. (Though, we'll never have a
namespace hierarchy level called "PMC", as that's an implementation
detail, not type information.) And yes, types are connected to namespace
hierarchies, though it's not always a one-to-one correspondence.
4. Parrot VM Issues
Parrot provides what it calls "registers" with no guarantee that these
map to hardware registers.
a. Will any registers ever map, in a Parrot-controlled way, to hardware
registers?
Well, of course they sometimes map to hardware registers underneath.
b. How can a compiler efficiently allocate registers if it does not
know which ones will map to hardware registers?
Hand-rolled allocation is never as efficient or as reliable as
programmatic allocation. The virtual machine handles the efficient
allocation of registers. The HLL compiler running on the virtual machine
doesn't pay any attention to it. This is an advantage of Parrot,
allowing new HLLs to be quickly and easily implemented without
reinventing low-level wheels like garbage collection and register
allocation.
5. Parrot Design Issues
Parrot has many operators and number of Core PMC types for them to
operate on. Parrot has so many operators that it appears to be using
them instead of having a standard library. This is markedly different
than the CLR and JVM systems.
a. Why was this done this way?
By operator, I'm assuming you mean opcode.
Since this is a *virtual* machine, the only real difference between an
opcode and a function is how you call it. See the last two entries in
<http://www.parrotcode.org/faq/>
b. What is the basis for deciding what will be an operator?
It's essentially a design decision of how core the operation is, how
frequently it will be used, and how standard it will be across all the
languages implemented on Parrot. Non-core, rarely used, or behavior that
varies widely between languages isn't canonized as an opcode (or is
canonized as an opcode that simply calls the relevant vtable function on
a PMC).
c. How can substantial quantities of additional functionality be added
to this design cleanly?
As libraries: dynamically loaded opcode libraries, libraries of
low-level functions, or libraries written in PIR or an HLL and compiled
to bytecode.
Allison