Debbie Lockett <debbie@saxonica.com>
Adam Retter <adam@evolvedbinary.com>
Be careful what you wish for...
Background to problem / What we set out to solve!
Side effects and concurrency
Introduction to EXPath Task module - define functions and how to use them
Code examples and demos using Tasks
Conclusions and future work
Conclusion: We need a better way to compute tasks
ixsl:schedule-action is inflexible and exhibits side effects
<xsl:template name="send-request">
<xsl:variable name="request" select="
map{
'method': 'POST',
'href': 'http://localhost:19757/mywebapp/receiveXML',
'body': $body,
'media-type': 'application/xml'
} "/>
<ixsl:schedule-action http-request="$request">
<xsl:call-template name="handle-response"/>
</ixsl:schedule-action>
</xsl:template>
<xsl:template name="handle-response">
<xsl:context-item as="map(*)" use="required"/>
<xsl:for-each select="?body">
<xsl:call-template name="process-response-body"/>
</xsl:for-each>
</xsl:template>
e.g. writing to a file or accessing a web page
e.g. printing a document while running some queries
or time for some baking...
Suppose we have a recipe for baking a cake...
Obviously some steps need to be carried out in the order specified.
Bake the cake before icing it.
Meanwhile it may be OK to do some steps concurrently.
Weigh the flour at the same time as cracking the eggs into a bowl.
Care must be taken when reordering to avoid problems from side effects.
If you get the cake decorations out too early, there's a risk they won't be there when you get to the decorating stage...
The recipe will not say exactly how long to bake for (only something like "put in the oven for 20 minutes, or until cooked"). While the cake is in the oven, rather than just waiting, the baker is able to get on with other steps. The task is asynchronous.
Put the cake in the oven - only take it out and proceed with the subsequent instructions, when it's done.
Meanwhile start preparing the icing, or have a cup of tea.
Replace standard recipe of sequential steps:
The recipe becomes something more like a flow diagram...
The baker now has explicit information about opportunities for reordering and/or concurrency.
It's not all about cakes
Recall: by definition side effects are NOT
allowed in pure functional languages!
Co-routines
Haskell IO Monad
Promises and Futures
Reactive Streams
Aim: provide a way to safely manage side effects and concurrent execution in XPDLs.
Functions are provided to:
A task is an object which encapsulates an action (which may have side effects); which could be lifted to be asynchronous.
map(xs:string, function(*))
For any $task, the action is stored in the "apply" entry of the map:
$task?apply is a function of type:
This function always returns a pair:
When tasks are composed, the 2nd task always takes the RealWorld from the result of the 1st task; and uses its $action-result as needed.
function(element(adt:realworld)) as item()+
(element(adt:realworld), $action-result)
task:value("hello world")
task:of(function() {'hello world'})
task:error(
xs:QName("local:oops"),
"something went wrong", ())
task:of(util:system-time#0)
Different functions are available for composing tasks (the result of each function is a task):
Function for executing a task (chain):
WARNING: This function is inherently unsafe, as it causes any side effects within the task chain to be actualised. It should only be invoked once in any application; at the end.
Function to construct an asynchronous task:
When an asynchronous task is executed, it returns an Async - a function which represents the asynchronous process, not the result of that process.
An Async is an "abstract type":
...~A is the type of the result of the asynchronous process
function(element(adt:scheduler)) as ~A
Using maps for tasks means we can provide alternative imperative-like fluent syntax for these functions:
Function based syntax | Imperative-like syntax |
---|---|
task:bind($task, $binder) | $task ? bind($binder) |
task:then($task, $next) | $task ? then($next) |
task:fmap($task, $mapper) | $task ? fmap($mapper) |
task:sequence($tasks) | $task1 ? sequence(tail($tasks)) |
task:async($task) | $task ? async() |
task:RUN-UNSAFE($task) | $task ? RUN-UNSAFE() |
Let's see some code!
task:of(function() { local:make-cake#0})
? fmap(local:bake#1)
? fmap(local:decorate-cake#1)
? async()
? bind(task:wait#1)
? RUN-UNSAFE()
Starting to write a cake recipe using Tasks
<xsl:template match="button[@id eq 'go']" mode="ixsl:onclick">
<xsl:variable name="onclick-page-updates-task"
select="task:of(f:onclick-page-updates#0)"/>
<xsl:variable name="http-post-task"
select="task:of(function(){
http:post($request-body, $request-options)
})"/>
<xsl:variable name="async-http-task"
select="$http-post-task
? fmap(f:handle-http-response#1)
? async()"/>
<xsl:sequence
select="task:RUN-UNSAFE(
task:then($onclick-page-updates-task, $async-http-task)
)"/>
</xsl:template>
Was it all worth it?
The EXPath Task module meets our initial requirements:
Allows concurrent programming to be explicitly described; implementable on systems offering preemptive or cooperative multitasking
How well can use of the Task module be incorporated into IXSL stylesheets for Saxon-JS applications?
XQuery
https://github/adamretter/task.xqJava (for XQuery in eXist-db)
https://github.com/eXist-db/exist/tree/expath-task-module-4.x.x/ex- tensions/expath/src/org/expath/task
Any questions?