« January 2008 | Main | March 2008 »

February 25, 2008

Flex 3, AIR 1, and OpenSource

Wow, it's amazing to think about all of the wonderful work that Adobe put into getting Flex 3 and AIR 1 out the door. My congratulations to everyone involved. Lastly the source for the Flex compilers is open source, I've already thought of some things I want to try with that. Download, play, enjoy.

As a side note, I've updated my simple Window Explorer application to run on the released version of AIR.

Tags: air flex oss

February 18, 2008

Abusing try..catch and throw

While looking something else up in the AS3 documentation this long weekend, I ran across this comment:

Typically, you throw instances of the Error class or its subclasses (see the Example section).

Emphasis added by me. If one typically does something, that means you can also not do it :) The throw statement takes an expression as its argument. The convention is to have it be a subclass of Error. This post isn't about that. In fact this post is a mental exercise as I don't recommend breaking from that convention, but I'm sure it might get someone's creative juices flowing, as I've not thought of a compelling use case, yet.

To demonstrate the alternative usage of throw, the code below simulates a switch statement branching based on the type of object being passed in. This code doesn't do anything with the value, but considering its all fun and games anyway extrapolating to doing some type conversion based on the incoming type should be easy. I've included all the standard primitive and top level classes, which are more examples than are needed to demonstrate the idea, but I was curious:

// This is an example of an AS3 language
// possibility, don't write code like this.
private function catchAsSwitch(value:*):void {
    try {
        throw value;
        // Tested alphabetically unless noted
        // otherwise       
    } catch(type:Array) {
        trace("Array");
    } catch(type:Boolean) {
        trace("Boolean");
    } catch(type:Class) {
        trace("Class");
    } catch(type:Date) {
        trace("Date");
        // This is the normal use case
    } catch(type:Error) {
        trace("Error");
    } catch(type:Function) {
        trace("Function");
    } catch(type:int) {
        trace("int");
        // uint must come before Number
        // since a uint satisfies both
        // is uint and is Number
    } catch(type:uint) {
        trace("uint");
    } catch(type:Namespace) {
        trace("Namespace");
    } catch(type:Number) {
        trace("Number");
    } catch(type:QName) {
        trace("QName");
    } catch(type:RegExp) {
        trace("RegExp");
    } catch(type:String) {
        trace("String");
    } catch(type:XML) {
        trace("XML");
    } catch(type:XMLList) {
        trace("XMLList");
        // Object must go last since everything
        // above satisfies is Object
    } catch(type:Object) {
        trace("Object");
        // Like switches' default
    } catch(type:*) {
        trace("*");
    }
}

An example function that calls the method with the various types:

private function go():void
{
    catchAsSwitch(new Array());
    catchAsSwitch(true);
    catchAsSwitch(Class);
    catchAsSwitch(new Date());
    catchAsSwitch(new Error());
    catchAsSwitch(function():void{});
    catchAsSwitch(-42);
    catchAsSwitch(0xFFFFFFFF);
    catchAsSwitch(new Namespace("com.neophi.test"));
    catchAsSwitch(42.42);
    catchAsSwitch(new QName("foo"));
    catchAsSwitch(/regexp/);
    catchAsSwitch("foo");
    catchAsSwitch(<foo/>);
    catchAsSwitch(<foo/>.children());
    catchAsSwitch(new Button());
    catchAsSwitch(null);
    catchAsSwitch(undefined);
}

The output of running that function is:

Array
Boolean
Class
Date
Error
Function
int
uint
Namespace
Number
QName
RegExp
String
XML
XMLList
Object
*
*

As noted in the comments of catchAsSwitch() catch clauses are evaluated in order so in some cases if the value could satisfy multiple different types the more specific is listed first. This is something to keep in mind when using the typical case, in that Error should be listed after more specific subclasses like ArgumentError.

Now that this alternative syntax exists could we do something with it? Well as I mentioned above I've not thought of a compelling case yet. In fact this entire post is the result of a big tangent on my part. It started off with reading some of Francis Cheng's recent posts about Proper Tail Calls (PTC). Having taken a compiler course from William Clinger in the past in which I had to implement PTC I found the topic interesting.

Reading those posts led me to the EMCAScript site and looking over the ES4 documentation. In one document comments about scoping issues with catch blocks had me discover that throw took an expression. Which made me remember some discussion about using throw instead of return for functions when people were first exploring Java. The net result of that discussion was a comparison to the considered harmful idea since it obfuscated the code's intent. I can't find a reference to the discussion but Cheng's PTC example prompted me to write a throw equivalent. It still suffers from stack overflow but again maybe it will spark someone else's imagination for a way to use the technique.

private function factorial(x:int):int {
    try {
        calculateFactorial(x, 1);
    } catch(result:int) {
        return result;
    }
    return 1;
}

private function calculateFactorial(x:int, result:int):void {
    if (x < 1) {
        throw result;
    }
    calculateFactorial(x - 1, result * x);
}

I don't know enough about the internals of the AVM to know if popping up the stack looking for a catch block is better than just returning the value in the current AVM. Obviously if PTC is added to ES4 and the AVM picks it up any such approach like the one above will be vastly slower. The nice mental exercise is that you have a function "returning" a value without returning a value.

In the process of experimenting with alternative try..catch use cases I ran into some compiler issues. Being a nonstandard use case I'm not surprised they didn't work. Yet another reason to not try this at home.

Update: Shortly after posting this I found a post that talks about using try..catch as an alternative flow control mechanism. This captures most of the same thoughts as that old Java thread I mentioned above.

Tags: as3 catch throw try

February 12, 2008

Slow Motion Moment

I had one of those made for the movies slow motion moments coming home tonight. The snow flurries had just started to fall making the roads a little wet but nothing too serious. I was biking home using my secondary route. My primary route is the one I commute on between Union Square and Harvard Square. My secondary route is the one that I take when I've crossed over the river into Boston. While I don't ride it as much it's a route that I'm very familiar with.

Overall the route is good minus a couple of bad sections of road. One section has cobble stones on a turn which in inclement weather can be nasty. Another turn has some enormous pot holes. It was at this turn that I had my slow motion moment. As I'm starting to make the turn I'm thinking to myself, "I need to get over to avoid the pot ..." BAM! Skidding across the ground I realized I had my thought a moment to late. Thankfully I was dressed in enough layers to protect myself from the winter weather that I only ended up with some minor road rash on my right arm which took the brunt of the fall. Besides that I'm fine.

I did manage to rip to shreds the handle bar grip and scratch up the brake on the right since that was also skidding along the ground. Overall the most annoying part was the puncture flat I got from the pot hole since that meant having to dry off and change the tube under a wet, dirty, and greasy tire. Thankfully that's all done. Tomorrow's ride into work should be fun as I'm sure I'll found out what part of my body I've not yet realized I banged up.

Tags: accident bike life

February 7, 2008

Puzzling Proxy Problem: Solved

The short version is what AS3 operator triggers a call to the isAttribute() method of a Proxy? The answer, none. isAttribute() is a utility function provided by Proxy that can be called to determine if the name passed to a function was specified as an attribute. A couple of Proxy's methods can't be called in an attribute context, as noted below. I've created a sample Proxy class called MyProxy to help show what's going on:

package com.neophi.test {
    import flash.utils.Proxy;
    import flash.utils.flash_proxy;

    public dynamic class MyProxy extends Proxy {
        override flash_proxy function callProperty(name:*, ... rest):* {
            trace("callProperty", name, rest);
            flash_proxy::isAttribute(name);
            return null;
        }

        override flash_proxy function deleteProperty(name:*):Boolean {
            trace("deleteProperty", name);
            flash_proxy::isAttribute(name);
            return false;
        }

        override flash_proxy function getDescendants(name:*):* {
            trace("getDescendants", name);
            flash_proxy::isAttribute(name);
            return null;
        }

        override flash_proxy function getProperty(name:*):* {
            trace("getProperty", name);
            flash_proxy::isAttribute(name);
            return null;
        }

        override flash_proxy function hasProperty(name:*):Boolean {
            trace("hasProperty", name);
            flash_proxy::isAttribute(name);
            return false;
        }

        // Don't override isAttribute(), it is a utility function
        // used by methods that specify a name:* parameter
        // to determine if the name argument was specified as an
        // attribute. It doesn't look like there is any other
        // way to determine if a name was specified as an
        // attribute besides calling Proxy's isAttribute()
        // implementation. 
        override flash_proxy function isAttribute(name:*):Boolean {
            var result:Boolean = super.flash_proxy::isAttribute(name);
            trace("isAttribute", name, result);
            return result;
        }

        override flash_proxy function nextName(index:int):String {
            trace("nextName", index);
            return null;
        }

        override flash_proxy function nextNameIndex(index:int):int {
            trace("nextNameIndex", index);
            return (1 - index);
        }

        override flash_proxy function nextValue(index:int):* {
            trace("nextValue", index);
            return null;
        }

        override flash_proxy function setProperty(name:*, value:*):void {
            trace("setProperty", name, value);
            flash_proxy::isAttribute(name);
        }
    }
}

I then exercise all of the methods (with and without namespaces) on MyProxy with this code:

private function go():void
{
    namespace myNamespace = "com.neophi.test";
    var myProxy:MyProxy = new MyProxy();

    // callProperty()
    myProxy.foo();
    // Compiler error: 1041: Attributes are not callable.
    // myProxy.@foo();
    myProxy.myNamespace::foo();
    // Compiler error: 1041: Attributes are not callable.
    // myProxy.@myNamespace::foo();

    // deleteProperty(): uses isAttribute()
    delete myProxy.foo;
    delete myProxy.@foo;
    delete myProxy.myNamespace::foo;
    delete myProxy.@myNamespace::foo;

    // getDescendents(): uses isAttribute()
    myProxy..foo;
    myProxy..@foo;
    myProxy..myNamespace::foo;
    myProxy..@myNamespace::foo;

    // getProperty(): uses isAttribute()
    myProxy.foo;
    myProxy.@foo;
    myProxy.myNamespace::foo;
    myProxy.@myNamespace::foo;

    // hasProperty()
    "foo" in myProxy;
    // Compiler error: 1084: Syntax error: expecting identifier before foo.
    // @"foo" in myProxy;
    new QName(myNamespace, "foo") in myProxy;
    // Compiler error: 1084: Syntax error: expecting identifier before new.
    // @new QName(myNamespace, "foo") in myProxy;

    // nextName(): uses nextNameIndex() 
    for (var string:String in myProxy) {
        trace(string);
    }

    // nextValue(): uses nextNameIndex() 
    for each (var object:Object in myProxy) {
        trace(object);
    }

    // setProperty(): uses isAttribute()
    myProxy.foo = "bar";
    myProxy.@foo = "bar";
    myProxy.myNamespace::foo = "bar";
    myProxy.@myNamespace::foo = "bar";
}

The output of running is below (whitespace added to match up with blocks above):

callProperty foo
isAttribute foo false
callProperty com.neophi.test::foo
isAttribute com.neophi.test::foo false

deleteProperty foo
isAttribute foo false
deleteProperty foo
isAttribute foo true
deleteProperty com.neophi.test::foo
isAttribute com.neophi.test::foo false
deleteProperty com.neophi.test::foo
isAttribute com.neophi.test::foo true

getDescendants foo
isAttribute foo false
getDescendants foo
isAttribute foo true
getDescendants com.neophi.test::foo
isAttribute com.neophi.test::foo false
getDescendants com.neophi.test::foo
isAttribute com.neophi.test::foo true

getProperty foo
isAttribute foo false
getProperty foo
isAttribute foo true
getProperty com.neophi.test::foo
isAttribute com.neophi.test::foo false
getProperty com.neophi.test::foo
isAttribute com.neophi.test::foo true

hasProperty foo
isAttribute foo false
hasProperty com.neophi.test::foo
isAttribute com.neophi.test::foo false

nextNameIndex 0
nextName 1
null
nextNameIndex 1

nextNameIndex 0
nextValue 1
null
nextNameIndex 1

setProperty foo bar
isAttribute foo false
setProperty foo bar
isAttribute foo true
setProperty com.neophi.test::foo bar
isAttribute com.neophi.test::foo false
setProperty com.neophi.test::foo bar
isAttribute com.neophi.test::foo true

That then is a complete run down of all the Proxy methods and how to use them. I'd like to thank Jacob Wright for the pointer on how isAttribute() is used.

Tags: as3 flex proxy

February 3, 2008

Persepolis

Persepolis I & II by Marjane Satrapi is the first graphic novel that I've read. I've skimmed through others, but this one I actually read cover to cover. It's a fascinating way to tell a story. Being a fan of cinema I found this format provided a bridge between a traditional novel and a movie. The fact that all of the drawings are done in black and white I found enhanced the story since most of the material is somber.

Given the heightened fear among Americans of terrorism the story is wonderful in helping to view an "Axis of Evil" country in terms of its people. My knowledge of Iran is limited and this story helped me understand it better. It isn't presented in some idealized fashion but mixes the joys and despair that the author experienced during the revolution. In particular she makes many astute observations about the freer life she had compared to many of her compatriots and how that affected her world view.

Tags: books

Getting My Geek On

I just flipped the switch and NeoPhi is now running on new hardware. This project started last Friday Jan 25th when the new hardware came in. I ordered everything individually since I had a specific system in mind. In particular I wanted to increase my data redundancy by switch to RAID 6. The hard part is that since I run OpenBSD not that many RAID cards are supported. Thankfully I found one that was built a system up around it.

This was the second such time I'd done a whole hog hardware transfer. The last time was about four years ago and I think I'd forgotten what I nuisance such a migration is. Anyway it's done, seems to be working and boy am I tired right now.

Tags: neophi