Partial applications apply target function when all params are bound
I am aware of the Saxon 9 warning regarding multiple imports of arity.xsl; I'm going to choose to ignore this, formally in the future. Each of the stylesheets is treated like a module. Michael Kay, Saxon's author, discusses it here: http://stackoverflow.com/a/10102298master
parent
465ef7e8a5
commit
b384e5fad1
|
@ -26,16 +26,18 @@
|
||||||
<stylesheet version="2.0"
|
<stylesheet version="2.0"
|
||||||
xmlns="http://www.w3.org/1999/XSL/Transform"
|
xmlns="http://www.w3.org/1999/XSL/Transform"
|
||||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||||
xmlns:f="http://www.lovullo.com/hoxsl/apply">
|
xmlns:f="http://www.lovullo.com/hoxsl/apply"
|
||||||
|
xmlns:_f="http://www.lovullo.com/hoxsl/apply/_priv">
|
||||||
|
|
||||||
<import href="arity.xsl" />
|
<import href="arity.xsl" />
|
||||||
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Partially apply dynamic function reference FNREF
|
Partially apply dynamic function reference @var{fnref}
|
||||||
|
|
||||||
When provided to `f:apply', FNREF will be applied with ARGS as its
|
When provided to @code{f:apply}, @var{fnref} will be applied with
|
||||||
beginning argments, followed by any arguments to `f:apply'.
|
@var{args} as its beginning argments, followed by any arguments to
|
||||||
|
`f:apply'.
|
||||||
|
|
||||||
Note that you usually do not have to invoke this function directly:
|
Note that you usually do not have to invoke this function directly:
|
||||||
the dynamic function calls will handle currying/partial application
|
the dynamic function calls will handle currying/partial application
|
||||||
|
@ -44,6 +46,10 @@
|
||||||
Partially applied functions may continue to be partially applied
|
Partially applied functions may continue to be partially applied
|
||||||
until their parameters are exhausted. This can be used to implement
|
until their parameters are exhausted. This can be used to implement
|
||||||
currying.
|
currying.
|
||||||
|
|
||||||
|
When destructuring the result of this function, note that the
|
||||||
|
returned function reference may not match @var{fnref} by reference,
|
||||||
|
as it may have been modified.
|
||||||
-->
|
-->
|
||||||
<function name="f:partial" as="item()+">
|
<function name="f:partial" as="item()+">
|
||||||
<param name="fnref" as="item()+" />
|
<param name="fnref" as="item()+" />
|
||||||
|
@ -72,6 +78,10 @@
|
||||||
select="f:arity( $ref )" />
|
select="f:arity( $ref )" />
|
||||||
|
|
||||||
<choose>
|
<choose>
|
||||||
|
<when test="$argn eq $arity">
|
||||||
|
<sequence select="_f:apply-partial( $ref, $argout )" />
|
||||||
|
</when>
|
||||||
|
|
||||||
<when test="$argn gt $arity">
|
<when test="$argn gt $arity">
|
||||||
<apply-templates mode="f:partial-arity-error-hook"
|
<apply-templates mode="f:partial-arity-error-hook"
|
||||||
select="$ref">
|
select="$ref">
|
||||||
|
@ -157,4 +167,36 @@
|
||||||
$argn, ' arguments' ) )" />
|
$argn, ' arguments' ) )" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Apply a partial dynamic function application
|
||||||
|
|
||||||
|
This function is called automatically by @code{f:partial} when
|
||||||
|
partial application would otherwise result in the returning of a
|
||||||
|
nullary function. @emph{It performs no validations} to ensure the
|
||||||
|
integrity of the data.}
|
||||||
|
|
||||||
|
Just as @code{f:apply}, please note that @emph{up to eight arguments
|
||||||
|
are supported}. This should be enough.
|
||||||
|
-->
|
||||||
|
<function name="_f:apply-partial">
|
||||||
|
<param name="fnref" as="element(f:ref)" />
|
||||||
|
<param name="args" as="item()*" />
|
||||||
|
|
||||||
|
<variable name="fn" as="element()"
|
||||||
|
select="$fnref[ 1 ]" />
|
||||||
|
|
||||||
|
<!-- just as `f:apply', we support up to 8 arguments -->
|
||||||
|
<apply-templates select="$fn" mode="f:apply">
|
||||||
|
<with-param name="arg1" select="$args[ 1 ]" />
|
||||||
|
<with-param name="arg2" select="$args[ 2 ]" />
|
||||||
|
<with-param name="arg3" select="$args[ 3 ]" />
|
||||||
|
<with-param name="arg4" select="$args[ 4 ]" />
|
||||||
|
<with-param name="arg5" select="$args[ 5 ]" />
|
||||||
|
<with-param name="arg6" select="$args[ 6 ]" />
|
||||||
|
<with-param name="arg7" select="$args[ 7 ]" />
|
||||||
|
<with-param name="arg8" select="$args[ 8 ]" />
|
||||||
|
</apply-templates>
|
||||||
|
</function>
|
||||||
|
|
||||||
</stylesheet>
|
</stylesheet>
|
||||||
|
|
|
@ -50,7 +50,7 @@
|
||||||
|
|
||||||
<!-- technically it applies a template -->
|
<!-- technically it applies a template -->
|
||||||
<expect label="applies target function"
|
<expect label="applies target function"
|
||||||
test="$x:result[ 1 ] = foo:applied[ @n = 0 ]" />"
|
test="$x:result[ 1 ] = foo:applied[ @n = 0 ]" />
|
||||||
</scenario>
|
</scenario>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,9 @@
|
||||||
<!-- SUT -->
|
<!-- SUT -->
|
||||||
<import href="../../src/apply/partial.xsl" />
|
<import href="../../src/apply/partial.xsl" />
|
||||||
|
|
||||||
|
<!-- numerous templates for arity tests -->
|
||||||
|
<import href="../apply-test.xsl" />
|
||||||
|
|
||||||
<!-- generated -->
|
<!-- generated -->
|
||||||
<import href="partial-test.xsl.apply" />
|
<import href="partial-test.xsl.apply" />
|
||||||
|
|
||||||
|
@ -53,6 +56,7 @@
|
||||||
<param name="y" />
|
<param name="y" />
|
||||||
<param name="z" />
|
<param name="z" />
|
||||||
|
|
||||||
|
<foo:ternary-applied />
|
||||||
<sequence select="$x, $y, $z" />
|
<sequence select="$x, $y, $z" />
|
||||||
</function>
|
</function>
|
||||||
</stylesheet>
|
</stylesheet>
|
||||||
|
|
|
@ -36,6 +36,10 @@
|
||||||
<foo:b />
|
<foo:b />
|
||||||
<foo:c />
|
<foo:c />
|
||||||
<foo:d />
|
<foo:d />
|
||||||
|
<foo:e />
|
||||||
|
<foo:f />
|
||||||
|
<foo:g />
|
||||||
|
<foo:h />
|
||||||
</foo:parent>
|
</foo:parent>
|
||||||
</variable>
|
</variable>
|
||||||
|
|
||||||
|
@ -76,41 +80,252 @@
|
||||||
</scenario>
|
</scenario>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- our implementation can be thought of like currying -->
|
||||||
<scenario label="given arguments for all function params">
|
<scenario label="given arguments for all function params">
|
||||||
<call function="f:partial">
|
<call function="f:partial">
|
||||||
<param name="fnref" select="$fnref" />
|
<param name="fnref" select="$fnref" />
|
||||||
<param name="args" select="1, 2, 3" />
|
<param name="args" select="$args/foo:a,
|
||||||
|
$args/foo:b,
|
||||||
|
$args/foo:c" />
|
||||||
</call>
|
</call>
|
||||||
|
|
||||||
<expect label="returns FNREF as first item in sequence"
|
<expect label="applies target function"
|
||||||
test="$x:result[ 1 ] = $fnref" />
|
test="$x:result[ 1 ]
|
||||||
|
and $x:result[ 1 ] = foo:ternary-applied" />
|
||||||
|
|
||||||
<!-- we checked previously for argument values; we assume here
|
<expect label="applies target function with arguments, by reference"
|
||||||
that it is working in this case...hopefully that is not
|
test="$x:result[ 2 ] is $args/foo:a
|
||||||
wrong! -->
|
and $x:result[ 3 ] is $args/foo:b
|
||||||
<expect label="returns all arguments"
|
and $x:result[ 4 ] is $args/foo:c" />
|
||||||
test="count( $x:result ) = 4" />
|
|
||||||
</scenario>
|
</scenario>
|
||||||
|
|
||||||
|
|
||||||
<!-- partial applications should behave as their own functions -->
|
<!-- partial applications should behave as their own functions -->
|
||||||
<scenario label="partially applying partial application">
|
<scenario label="partially applying partial application, with
|
||||||
|
fewer arguments than target function arity">
|
||||||
<call function="f:partial">
|
<call function="f:partial">
|
||||||
<param name="fnref" select="f:partial( $fnref, (1, 2) )" />
|
<param name="fnref" select="f:partial( $fnref, $args/foo:a )" />
|
||||||
<param name="args" select="3" />
|
<param name="args" select="$args/foo:b" />
|
||||||
</call>
|
</call>
|
||||||
|
|
||||||
<expect label="returns FNREF as first item in sequence"
|
<expect label="returns FNREF as first item in sequence"
|
||||||
test="$x:result[ 1 ] = $fnref" />
|
test="$x:result[ 1 ] = $fnref" />
|
||||||
|
|
||||||
<expect label="returns arguments from both partial applications"
|
<expect label="returns arguments from both partial applications"
|
||||||
test="count( $x:result ) = 4" />
|
test="count( $x:result ) = 3" />
|
||||||
|
|
||||||
<expect label="arguments are ordered with previous applications
|
<expect label="arguments are ordered with previous applications
|
||||||
first in sequence"
|
first in sequence"
|
||||||
test="$x:result[ 2 ] = 1
|
test="$x:result[ 2 ] is $args/foo:a
|
||||||
and $x:result[ 3 ] = 2
|
and $x:result[ 3 ] is $args/foo:b" />
|
||||||
and $x:result[ 4 ] = 3" />
|
</scenario>
|
||||||
|
|
||||||
|
|
||||||
|
<scenario label="partially applying partial application, with all
|
||||||
|
arguments">
|
||||||
|
<call function="f:partial">
|
||||||
|
<param name="fnref" select="f:partial( $fnref, ($args/foo:a,
|
||||||
|
$args/foo:b) )" />
|
||||||
|
<param name="args" select="$args/foo:c" />
|
||||||
|
</call>
|
||||||
|
|
||||||
|
<expect label="applies target function"
|
||||||
|
test="$x:result[ 1 ]
|
||||||
|
and $x:result[ 1 ] = foo:ternary-applied" />
|
||||||
|
|
||||||
|
<expect label="applies target function with arguments, by reference"
|
||||||
|
test="$x:result[ 2 ] is $args/foo:a
|
||||||
|
and $x:result[ 3 ] is $args/foo:b
|
||||||
|
and $x:result[ 4 ] is $args/foo:c" />
|
||||||
|
</scenario>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- let the repitition begin (don't we wish we had macros?
|
||||||
|
...foreshadowing?) -->
|
||||||
|
<scenario label="completing partial application passes all
|
||||||
|
arguments to a target">
|
||||||
|
<scenario label="unary">
|
||||||
|
<call function="f:partial">
|
||||||
|
<param name="fnref">
|
||||||
|
<f:ref arity="1"><foo:fn1 /></f:ref>
|
||||||
|
</param>
|
||||||
|
<param name="args" select="$args/foo:a" />
|
||||||
|
</call>
|
||||||
|
|
||||||
|
<expect label="applies target function"
|
||||||
|
test="$x:result[ 1 ] = foo:applied[ @n = 1 ]" />
|
||||||
|
|
||||||
|
<expect label="applies target function with arguments, by reference"
|
||||||
|
test="$x:result[ 2 ] is $args/foo:a" />
|
||||||
|
</scenario>
|
||||||
|
|
||||||
|
|
||||||
|
<scenario label="binary">
|
||||||
|
<call function="f:partial">
|
||||||
|
<param name="fnref">
|
||||||
|
<f:ref arity="2"><foo:fn2 /></f:ref>
|
||||||
|
</param>
|
||||||
|
<param name="args" select="$args/foo:a,
|
||||||
|
$args/foo:b" />
|
||||||
|
</call>
|
||||||
|
|
||||||
|
<expect label="applies target function"
|
||||||
|
test="$x:result[ 1 ] = foo:applied[ @n = 2 ]" />
|
||||||
|
|
||||||
|
<expect label="applies target function with arguments, by reference"
|
||||||
|
test="$x:result[ 2 ] is $args/foo:a
|
||||||
|
and $x:result[ 3 ] is $args/foo:b" />
|
||||||
|
</scenario>
|
||||||
|
|
||||||
|
|
||||||
|
<scenario label="ternary">
|
||||||
|
<call function="f:partial">
|
||||||
|
<param name="fnref">
|
||||||
|
<f:ref arity="3"><foo:fn3 /></f:ref>
|
||||||
|
</param>
|
||||||
|
<param name="args" select="$args/foo:a,
|
||||||
|
$args/foo:b,
|
||||||
|
$args/foo:c" />
|
||||||
|
</call>
|
||||||
|
|
||||||
|
<expect label="applies target function"
|
||||||
|
test="$x:result[ 1 ] = foo:applied[ @n = 3 ]" />
|
||||||
|
|
||||||
|
<expect label="applies target function with arguments, by reference"
|
||||||
|
test="$x:result[ 2 ] is $args/foo:a
|
||||||
|
and $x:result[ 3 ] is $args/foo:b
|
||||||
|
and $x:result[ 4 ] is $args/foo:c" />
|
||||||
|
</scenario>
|
||||||
|
|
||||||
|
|
||||||
|
<scenario label="4-ary">
|
||||||
|
<call function="f:partial">
|
||||||
|
<param name="fnref">
|
||||||
|
<f:ref arity="4"><foo:fn4 /></f:ref>
|
||||||
|
</param>
|
||||||
|
<param name="args" select="$args/foo:a,
|
||||||
|
$args/foo:b,
|
||||||
|
$args/foo:c,
|
||||||
|
$args/foo:d" />
|
||||||
|
</call>
|
||||||
|
|
||||||
|
<expect label="applies target function"
|
||||||
|
test="$x:result[ 1 ] = foo:applied[ @n = 4 ]" />
|
||||||
|
|
||||||
|
<expect label="applies target function with arguments, by reference"
|
||||||
|
test="$x:result[ 2 ] = $args/foo:a
|
||||||
|
and $x:result[ 3 ] = $args/foo:b
|
||||||
|
and $x:result[ 4 ] = $args/foo:c
|
||||||
|
and $x:result[ 5 ] = $args/foo:d" />
|
||||||
|
</scenario>
|
||||||
|
|
||||||
|
|
||||||
|
<scenario label="5-ary">
|
||||||
|
<call function="f:partial">
|
||||||
|
<param name="fnref">
|
||||||
|
<f:ref arity="5"><foo:fn5 /></f:ref>
|
||||||
|
</param>
|
||||||
|
<param name="args" select="$args/foo:a,
|
||||||
|
$args/foo:b,
|
||||||
|
$args/foo:c,
|
||||||
|
$args/foo:d,
|
||||||
|
$args/foo:e" />
|
||||||
|
</call>
|
||||||
|
|
||||||
|
<expect label="applies target function"
|
||||||
|
test="$x:result[ 1 ] = foo:applied[ @n = 5 ]" />
|
||||||
|
|
||||||
|
<expect label="applies target function with arguments, by reference"
|
||||||
|
test="$x:result[ 2 ] = $args/foo:a
|
||||||
|
and $x:result[ 3 ] = $args/foo:b
|
||||||
|
and $x:result[ 4 ] = $args/foo:c
|
||||||
|
and $x:result[ 5 ] = $args/foo:d
|
||||||
|
and $x:result[ 6 ] = $args/foo:e" />
|
||||||
|
</scenario>
|
||||||
|
|
||||||
|
|
||||||
|
<scenario label="6-ary">
|
||||||
|
<call function="f:partial">
|
||||||
|
<param name="fnref">
|
||||||
|
<f:ref arity="6"><foo:fn6 /></f:ref>
|
||||||
|
</param>
|
||||||
|
<param name="args" select="$args/foo:a,
|
||||||
|
$args/foo:b,
|
||||||
|
$args/foo:c,
|
||||||
|
$args/foo:d,
|
||||||
|
$args/foo:e,
|
||||||
|
$args/foo:f" />
|
||||||
|
</call>
|
||||||
|
|
||||||
|
<expect label="applies target function"
|
||||||
|
test="$x:result[ 1 ] = foo:applied[ @n = 6 ]" />
|
||||||
|
|
||||||
|
<expect label="applies target function with arguments, by reference"
|
||||||
|
test="$x:result[ 2 ] = $args/foo:a
|
||||||
|
and $x:result[ 3 ] = $args/foo:b
|
||||||
|
and $x:result[ 4 ] = $args/foo:c
|
||||||
|
and $x:result[ 5 ] = $args/foo:d
|
||||||
|
and $x:result[ 6 ] = $args/foo:e
|
||||||
|
and $x:result[ 7 ] = $args/foo:f" />
|
||||||
|
</scenario>
|
||||||
|
|
||||||
|
|
||||||
|
<scenario label="7-ary">
|
||||||
|
<call function="f:partial">
|
||||||
|
<param name="fnref">
|
||||||
|
<f:ref arity="7"><foo:fn7 /></f:ref>
|
||||||
|
</param>
|
||||||
|
<param name="args" select="$args/foo:a,
|
||||||
|
$args/foo:b,
|
||||||
|
$args/foo:c,
|
||||||
|
$args/foo:d,
|
||||||
|
$args/foo:e,
|
||||||
|
$args/foo:f,
|
||||||
|
$args/foo:g" />
|
||||||
|
</call>
|
||||||
|
|
||||||
|
<expect label="applies target function"
|
||||||
|
test="$x:result[ 1 ] = foo:applied[ @n = 7 ]" />
|
||||||
|
|
||||||
|
<expect label="applies target function with arguments, by reference"
|
||||||
|
test="$x:result[ 2 ] = $args/foo:a
|
||||||
|
and $x:result[ 3 ] = $args/foo:b
|
||||||
|
and $x:result[ 4 ] = $args/foo:c
|
||||||
|
and $x:result[ 5 ] = $args/foo:d
|
||||||
|
and $x:result[ 6 ] = $args/foo:e
|
||||||
|
and $x:result[ 7 ] = $args/foo:f
|
||||||
|
and $x:result[ 8 ] = $args/foo:g" />
|
||||||
|
</scenario>
|
||||||
|
|
||||||
|
|
||||||
|
<scenario label="8-ary">
|
||||||
|
<call function="f:partial">
|
||||||
|
<param name="fnref">
|
||||||
|
<f:ref arity="8"><foo:fn8 /></f:ref>
|
||||||
|
</param>
|
||||||
|
<param name="args" select="$args/foo:a,
|
||||||
|
$args/foo:b,
|
||||||
|
$args/foo:c,
|
||||||
|
$args/foo:d,
|
||||||
|
$args/foo:e,
|
||||||
|
$args/foo:f,
|
||||||
|
$args/foo:g,
|
||||||
|
$args/foo:h" />
|
||||||
|
</call>
|
||||||
|
|
||||||
|
<expect label="applies target function"
|
||||||
|
test="$x:result[ 1 ] = foo:applied[ @n = 8 ]" />
|
||||||
|
|
||||||
|
<expect label="applies target function with arguments, by reference"
|
||||||
|
test="$x:result[ 2 ] = $args/foo:a
|
||||||
|
and $x:result[ 3 ] = $args/foo:b
|
||||||
|
and $x:result[ 4 ] = $args/foo:c
|
||||||
|
and $x:result[ 5 ] = $args/foo:d
|
||||||
|
and $x:result[ 6 ] = $args/foo:e
|
||||||
|
and $x:result[ 7 ] = $args/foo:f
|
||||||
|
and $x:result[ 8 ] = $args/foo:g
|
||||||
|
and $x:result[ 9 ] = $args/foo:h" />
|
||||||
|
</scenario>
|
||||||
</scenario>
|
</scenario>
|
||||||
</scenario>
|
</scenario>
|
||||||
|
|
||||||
|
@ -202,20 +417,6 @@
|
||||||
<expect label="returns true()"
|
<expect label="returns true()"
|
||||||
test="$x:result = true()" />
|
test="$x:result = true()" />
|
||||||
</scenario>
|
</scenario>
|
||||||
|
|
||||||
|
|
||||||
<!-- we still want to consider all arguments to be a partial
|
|
||||||
application (consider it as producing a nullary
|
|
||||||
function)—otherwise, the abstraction is broken -->
|
|
||||||
<scenario label="given arguments for each parameter">
|
|
||||||
<call function="f:is-partial">
|
|
||||||
<param name="fnref"
|
|
||||||
select="f:partial( foo:ternary(), (1, 2, 3) )" />
|
|
||||||
</call>
|
|
||||||
|
|
||||||
<expect label="returns true()"
|
|
||||||
test="$x:result = true()" />
|
|
||||||
</scenario>
|
|
||||||
</scenario>
|
</scenario>
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue