It’s just data

Hobgoblin of Little Minds

While exploring Python on Parrot, I was curious to see how Parrot handled Dealing with Diversity.  Running pie-thon against print '1' + '2' produced the following PIR code:

         $P1 = new PerlInt
         $P1 = "1"
         $P3 = new PerlInt
         $P3 = "2"
         $P2 = new PerlInt               # BINARY_ADD
         $P2 = $P1 + $P3
         print_item $P2          # PRINT_ITEM
         print_newline           # PRINT_NEWLINE

Running Parrot without any options against that code produces 3 as the result.  You might think that that is because "1" and "2" are coerced into integers before the addition, but in fact the reverse is true.  Assigning a string to a PerlInt results in a morphing of the target:

     void set_string_native (STRING* value) {
         DYNSELF.morph(enum_class_PerlString);
         DYNSELF.set_string_native(value);
     }

It turns out that the addition itself will evaluate the strings prior to doing the addition — or will, but only if the --python option is not specified.  If it is specified, string addition is defined as concatenation.

Varying runtime behavior in this manner seems like an extraordinary bad idea.  A better solution would be a separate PythonString class.

The morphing behavior of assignment isn't consistent either.  If you define a ResizablePMCArray, assigning an integer to it seems to be the way you set the length.

In any case, here is a patch to Pirate to take advantage of this behavior.  This caused the range test to fail due to a latent bug in Pirate that was masked by another set of unexpected behavior — in this case how negative fractional numbers are rounded to integers also seems to depend indirectly on whether Parrot is run in --python mode or not.

Finally, running in --python mode changes the behavior of generators in Pirate.  Mind you, it doesn't make them work just yet, just behave differently.


I don't really understand the larger context, but some of this seems fine.  Obviously --python isn't a good idea, since it means you can't run different kinds of code alongside each other, which is part of the point of Parrot.  OTOH, I think there should be a Python version of +, not necessarily a Python version of strings (unless PerlStrings are mutable, in which case they are fundamentally incompatible).  Since strings are a fundamental data structure, it seems easier if they are kept consistent in type across languages.

The integer part doesn't seem, well, terribly bad. '$P1 = new PerlInt; $P1 = "1"' is essentially the code to load an integer literal.  I wouldn't necessarily expect '$P1 = "1"' to be in any way analogous to Python assignment.  If the assembler looked like 'DECLARE $P1 PerlInt; LOAD $P1 "1"' wouldn't it seem ok?

Posted by Ian Bicking at

Sam: thanks for the patch!

I'm pretty sure --python was a kludge for the purposes of the pie-thon contest. PythonString is definitely the "parrot way" to do things.

Ian: yep, perl strings are mutable:

$x = "abc\n";
substr($x, 0, 1, "A");
print $x; #  Abc

But there are a lot of reasons why perl strings and python strings are different... For one thing, python strings have a whole slew of methods on them.

Parrot actually has generic strings, ints, and floats. It even has separate sets of registers and opcodes for each of these types, to speed things up for statically typed languages. It also has PerlString, PerlInt, PerlNum, and so on which implement features specific to perl.

The "+" operator is implemented as an opcode, which is implemented by each PMC similarly to the way any class in python can override the _add_ method.

Posted by Michal Wallace at

Ian, one thing that Parrot definitely gets right is the use of Multimethod dispatch for binary opcode functions.  This enables operator overloading.  What this means is that a+b gets evaluated as a.add(b) instead of add(a,b).  This enables types to overload operators.

One could imagine a system in which PythonString inherited from PerlString.  Or one in which they both inherit from a common ParrotString.  Or simply morph into one another as needed.

As to the integer part, $P1 = new PerlInt; $P1 = "1" is a way to declare and load a PerlString literal.  The first thing the set_string method does is to morph the PerlInt into a PerlString, and then it sets it's value.  The end result is the same as $P1 = new PerlString; $P1 = "1".

Posted by Sam Ruby at

Heh. Actually a+b gets evaluated as (mmd_lookup(MMD_ADD, typeof(a), typeof(b)))(a, b), give or take some wacky syntax. That is, the function to do the addition lives outside the class of a or the class of b. This is handy as it allows for third-party libraries to install MMD methods for operators without having to open the left or right side classes, and it doesn't force a left-side-wins scheme on dispatch (though the current defaults system it looks like left-side-wins if there's no better method installed). The system's also extensible at runtime if you want to have extra binary MMD ops (if, for example, you had code for a language with extra operators) and didn't want to fall back to the more general MMD function/method call scheme, which is a bit more expensive to dispatch. (And unfinished :)

The --python and all the Perl* types are all byproducts of rush and unfinished work. There should be Python PMCs for the basic python types with the appropriate MMD functions installed where need be. (+ does string concat in some cases in python, right? That'd be doable with the right methods installed in the addition MMD table)

Posted by Dan at

Yes, mmd_lookup(MMD_ADD, PyString, PyString) should return a string concatenation function.

Posted by Sam Ruby at

Sam Ruby: Hobgoblin of Little Minds

[link]...

Excerpt from del.icio.us/tag/python at

Add your comment