Tuesday, August 18, 2009

Vector: Perl 6 is full of awesome

I've made a bunch of small changes since my last post, all of them delightful. First up, I finally switched has $.coordinates to has @.coordinates. This required switching all the usages of coordinates in the class definition. But much to my surprise, it did not require changing any of the usages outside the definition.

That's because the typical usage was something like $a.coordinates. What I realized this morning was that this is not a reference to the $.coordinates class member. Rather, it is a call to an automatically generated .coordinates method, which returns the $.coordinates member. That means switching to @.coordinates just means that .coordinates returns an array rather than a scalar reference to an array. No change at all is needed in how the method is used.

I realize that all the experienced Perl 6 hands just assumed I knew that when I wrote $a.coordinates. But I didn't, and it is a pleasant surprise.

My next discovery plays off of something I worked out days ago, but hadn't realized all the implications of until this morning. If you define operator +, Perl 6 automatically generates operator += for you. Now, presumably it just internally translates $a += $b to $a = $a + $b. This means the behavior of += is very different in C++ and Perl 6.

In C++, if you say a += b, a is still the same object before and after the operation; it just has a different value afterward. In Perl 6, $a actually is a different object after $a += $b. It doesn't even have to have the same type as it did before. Consider these two tests I added this morning:

In the first, $a starts out a Vector and ends up a Num. That's why the second code dies -- $a is declared a Vector.

That, in turn, means that you can declare the internals of a Vector "ro" and still use += on Vector variables. Each Vector is immutable, but a new Vector is created and assigned to the left-hand side.

This is great for obeying the LSP. For instance, if I define a UnitVector to be a Vector where { $v.Length == 1 }, then I can pass a UnitVector to a function written only knowing about the Vector class and it will automatically do the right thing. This is very different from C++, where passing a UnitVector to an algorithm expecting Vector can muddle up the type system.

The += operator itself provides a prime example of this. The += operator I've already (implicitly!) defined for Vector will work perfectly if passed a UnitVector; it will simply convert the first argument from UnitVector to Vector and get on with life. (I suppose if you have explicitly declared the first argument to be of type UnitVector it will signal an error.) In C++, if you didn't overload += especially for UnitVectors it would break the type invariant for the UnitVector.

One final bit of Perl 6 operator magic:

This defines a new parenthetical operator to calculate the length of a Vector, mimicking the standard mathematical notation. Not much more to say about that than "Awesome!" (Well, I haven't figured out a Texas equivalent yet -- but I've not worried because there is also the Length method.)

Also, I've figured out how to easily handle the Unicode dot and cross operators in TextMate. I programmed tab triggers for each (in the Perl environment) so that if you type, say, "dot" and hit tab, it substitutes the Unicode symbol for the word. It is the best of both worlds: easy to type and still pretty.

No comments:

Post a Comment