Functional Programming on Google AppEngine

I’ve always hoped that Java would be the next Google App Engine language. Google have provided a solid platform with a ‘real’ JVM and few restrictions.

I deployed the sample application created with the Eclipse plugins a few minutes after my account was upgraded to support Java, and then I went on to deploy an application written in CAL.

There are two ways of integrating CAL and Java. The first is to dynamically load and compile a workspace at run time, and then reflectively call functions in the workspace’s modules. I’ve used this approach in the past, for example for CAL/GWT integration.

There are two reasons not to use this approach for an AppEngine application. Firstly, when CAL loads a workspace it uses a SecurityManager, which GAE doesn’t allow. It’s just used to get the class of the caller of a function so the code could probably be replaced by looking at the stack trace of an un-thrown Exception, but that would involve a custom CAL build. I suspect that this approach would also want to create some local files, which also wouldn’t work on AppEngine.

The other reason not to use this approach is that AppEngine runs your application on (potentially) many different JVM instances, and may stop your application when it is idle and restart it when it receives a request. Nothing I’ve read so far indicates how often this happens, but start up overhead which would be perfectly acceptable in a normal application server is likely to be a bad idea in AppEngine.

CAL modules can also be compiled statically to a ’stand-alone’ jar file.

Each public function in the module becomes a static function on the resulting class, e.g.:

square :: Int -> Int;
public square n = n * n;

becomes:

public static int square(int n, ExecutionContext context)

If we hadn’t explicitly typed square, it would have the inferred type Num a => a -> a and the function would have the signature

public static CalValue square(CalValue n, ExecutionContext context);

Where a CalValue can hold any Java types which CAL knows how to convert to CAL types via the input function, and ExecutionContext really just caches Constant Applicative Forms.

So the components of a minimal CAL application for AppEngine are:

  1. A module which imports HttpServletRequest and HttpServletResponse and provides suitable functions to get their properties and to set attributes of the response.
  2. A function with the type HttpServletRequest -> HttpServletResponse -> ().
  3. A servlet which calls that function.
  4. Extensions to the example build.xml provided by Google which package up the CAL modules into a stand-alone jar, and include the runtime support jars CAL needs.

Additionally all the other AppEngine Services (Data Store, Cache, Mail etc.) need to be made available to CAL (in Monads of course :-) ), but that can be done piecemeal as you need them.

I’ll just give one example from the Http module, as it illustrates the issue of dealing with null being returned by Java methods.

First I import the HttpServletRequest.getParameter method:

foreign unsafe import jvm "method getParameter"
    jGetParameter :: HttpServletRequest -> String -> String;

But it isn’t made public. The ‘real’ getParameter function is defined like this:

public getParameter req s =
    let v = jGetParameter req s;
    in if isNullString v then Nothing else Just v;

Allowing the Maybe type to handle the possibility that a parameter may not be set.

The build.xml changes are:

<target name="copy-quark-jars">
    <copy
            todir="war/WEB-INF/lib"
            flatten="true">
          <fileset dir="${quark.dir}/Quark/bin/java/release">
            <include name="**/calLibraries.jar" />
            <include name="**/calPlatform.jar" /> <!-- maybe don't need? -->
            <include name="**/calRuntime.jar" />
            <include name="**/calUtilities.jar" />
          </fileset>
          <fileset dir="${quark.dir}/Quark/lib/Resources/External/java/">
            <include name="**/icu4j.jar" />
            <include name="**/log4j.jar" />
          </fileset>
        </copy>
</target>

<target name="build-quark-standalone" depends="copyjars">
    <exec executable="quarkc.sh">
        <arg value="workspace.cws"/>
        <arg value="-lib"/>
        <arg value="${moduleName}"/>
        <arg value="public"/>
        <arg value="${className}"/>
        <arg value="war/WEB-INF/lib/calmodule.jar"/>
        <env key="QUARK_CP" value="src:war/WEB-INF/lib/appengine-api-1.0-sdk-1.2.0.jar:${sdk.dir}/lib/shared/geronimo-servlet_2.5_spec-1.2.jar"/>
    </exec>
</target>

Now to do something useful with it!

Post a Comment

Your email is never shared. Required fields are marked *

*
*