SIDENOTE: @amike: The run was good while it lasted, eh? ;-)

SIDENOTE.NEXT: After rereading the title, I’m not even sure it makes any sense. But then again, what’s new? ;-) :D

[Post.Body]
DonXml’s All Things Techie : Mixing Object, Functional and Aspect Oriented Programming

Within a DSL it would be cool if you could map its Nouns to Objects (described via OOP), its Verbs to Functions (described via FP), and its Adjectives and Adverbs to Aspects (via AOP).

I have to do some research, but does this fit within the definition of a composable language? I tried to fine a definition of what a composable language, but didn’t seem to find one.

Oh, the power of XSLT 2.0 (and XPath 2.0), where you can bring together the knowledge-pool and massive underlying code base of OOP, fold in the power of Functional Programming, and weave all of it together with AOP to produce a truly composable language as a result.

For example,


<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform  xmlns="http://nuxleus.com/message/response" xmlns:lookup="http://nuxleus.com/message/response/lookup" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:saxon="http://saxon.sf.net/" xmlns:clitype="http://saxon.sf.net/clitype" xmlns:at="http://atomictalk.org" xmlns:func="http://atomictalk.org/function" xmlns:queue-manager="clitype:Xameleon.Function.QueueManager?partialname=Xameleon" xmlns:aspnet-context="clitype:System.Web.HttpContext?partialname=System.Web" xmlns:queue="http://xameleon.org/service/queue" xmlns:guid="clitype:System.Guid?partialname=mscorlib" version="2.0" exclude-result-prefixes="#all">

    <xsl:import href="../../base.xslt" />

    <xsl:param name="current-context"/>

    <xsl:output method="xml" indent="no" />

    <xsl:variable name="response-output">
        <lookup:response-code-lookup>
            <lookup:message response-code="message sent">
                <lookup:human-readable>Message was sent successfully!</lookup:human-readable>
            </lookup:message>
            <lookup:message response-code="error">
                <lookup:human-readable>There was an error sending your message.</lookup:human-readable>
            </lookup:message>
        </lookup:response-code-lookup>
    </xsl:variable>

    <xsl:template match="queue:push">
        <xsl:variable name="message" select="@message"/>
        <xsl:variable name="message-queue" select="@message-queue"/>
        <xsl:sequence select="func:queue-push($current-context, $message-queue, $message)"/>
    </xsl:template>

    <xsl:function name="func:queue-push">
        <xsl:param name="current-context" />
        <xsl:param name="message-queue" as="xs:string"/>
        <xsl:param name="message" as="xs:string"/>
        <xsl:variable name="guid" select="string(guid:NewGuid())"/>
        <xsl:variable name="response">
            <xsl:sequence select="queue-manager:Push($current-context, $message-queue, concat($guid, ':', $message))"/>
        </xsl:variable>
        <response>
            <xsl:apply-templates select="$response" mode="generate-human-readable-response">
                <xsl:with-param name="message-id" select="$guid"/>
            </xsl:apply-templates>
        </response>
    </xsl:function>

    <xsl:template match="text()" mode="generate-human-readable-response">
        <xsl:param name="message-id"/>
        <xsl:variable name="message" select="$response-output//lookup:message[@response-code = current()]"/>
        <message response-code="{.}" message-id="{$message-id}">
            <xsl:value-of select="$message/lookup:human-readable"/>
        </message>
    </xsl:template>

</xsl:transform>

… in which you can bring it together with something as simple as,


<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="/service/transform/controller/message-queue/base.xslt"?>
<service:operation xmlns:service="http://xameleon.org/service" xmlns:operation="http://xameleon.org/service/operation" xmlns:queue="http://xameleon.org/service/queue">
    <operation:queue>
        <queue:push message-queue="foo" message="bar"/>
    </operation:queue>
</service:operation>

… to produce,


<?xml version="1.0" encoding="UTF-8"?>
<response xmlns="http://nuxleus.com/message/response">
    <message response-code="message sent" message-id="924ade3c-315a-4577-8c89-75edbd77cf83">Message was sent successfully!</message>
</response>

The Breakdown

While this is an oversimplified example, it seemed better to keep things simple for purpose of demonstration. No doubt that if the above (or what follows) makes sense to you, you’ll know where to take things from here :)


In the above XSLT 2.0 file, the namespace declaration xmlns:queue-manager="clitype:Xameleon.Function.QueueManager?partialname=Xameleon" represents a reference to the QueueManager object written in C#. In this particular case it’s a static class which enables us to access the underlying methods directly w/o first having to create an instance of the QueueManager object due to the fact that all members of a static class are also static. That said, in the true spirit of OOP, we could have just as easily created a non-static class and used queue-manager:new() to create a new instance of that object, wrapping it inside of a variable (e.g. <xsl:variable name="qm" select="queue-manager:new()"/>) to then use that variable as the first value in a call to any of its methods (e.g. queue-manager:Push($qm, $current-context, $message-queue, concat($guid, ':', $message)). I’ll let you decide which usage pattern you prefer (You’re welcome! :D).

With the above in mind, here’s the sequence of events,

The XML sequence file, when applied to the XSLT 2.0 file, is caught by the first template/rule <xsl:template match="queue:push"> (NOTE: It’s actually first caught by the service:operation and operation:queue templates which are not shown here, but both of these templates apply-templates to the underlying node-set after doing some more complex computations unrelated to this example, so I left them out to keep things simple) which in turn takes the values of the attributes @message and @message-queue and passes them to the function 'queue-push' via func:queue-push($current-context, $message-queue, $message). (NOTE: $current-context is the current ASP.NET request context object passed into the transformation via a param. This allows us to extract information related to the underlying HTTP request.) The queue-push function itself is really just a wrapper around our call to queue-manager:Push, and in this particular case it would have been just as easy to call it directly as it would have been to call the wrapper function. But this is primarily due to the oversimplification of this example. In *most* cases, the ability to create complex functions which can be invoked inline to an XPath far outweighs the cost of using a wrapper function to invoke the underlying system call. But as usual, when to use what is answered by: It depends. :)

Moving forward: The call to the queue-manager:Push extension function is converted to a sequence via <xsl:sequence select="queue-manager:Push($current-context, $message-queue, concat($guid, ':', $message))"/>, and the resulting sequence passed on down to the awaiting templates via xsl:apply-templates (along with some parameter values). In our oversimplified example, the first and only match is,

<xsl:template match="text()" mode="generate-human-readable-response">

… who’s primary purpose is to take the result of our call to the message-queue, compare the response to our lookup table, and weave into the output various bits of information that will enable the recipient of the message the ability to better decipher the result of the call to our message queue service, thus weaving AOP into our collective acronymic function from the title.

The end result?

A truly composable language featuring all of the goodness that comes from each item in our collection, all wrapped up into a manageable, queryable, and easily maintainable code base.

Hooray! :D

Questions?