475 lines
16 KiB
XML
475 lines
16 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
|
<!--
|
|
Tests partial function application
|
|
|
|
Copyright (C) 2014 LoVullo Associates, Inc.
|
|
|
|
This file is part of hoxsl.
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
-->
|
|
|
|
<description xmlns="http://www.jenitennison.com/xslt/xspec"
|
|
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
|
xmlns:x="http://www.jenitennison.com/xslt/xspec"
|
|
xmlns:f="http://www.lovullo.com/hoxsl/apply"
|
|
xmlns:foo="http://www.lovullo.com/_junk"
|
|
stylesheet="partial-test.xsl">
|
|
|
|
<variable name="fnref"
|
|
select="foo:ternary()" />
|
|
|
|
<variable name="args">
|
|
<foo:parent>
|
|
<foo:a />
|
|
<foo:b />
|
|
<foo:c />
|
|
<foo:d />
|
|
<foo:e />
|
|
<foo:f />
|
|
<foo:g />
|
|
<foo:h />
|
|
</foo:parent>
|
|
</variable>
|
|
|
|
|
|
<scenario label="f:partial constructor">
|
|
<scenario label="given an empty argument list">
|
|
<call function="f:partial">
|
|
<param name="fnref" select="$fnref" />
|
|
<param name="args" select="()" />
|
|
</call>
|
|
|
|
<expect label="returns reference to original target"
|
|
test="f:QName( $x:result[ 1 ] )
|
|
= f:QName( $fnref )" />
|
|
|
|
<expect label="returns nothing else"
|
|
test="count( $x:result ) = 1" />
|
|
</scenario>
|
|
|
|
|
|
<scenario label="given arguments fewer than function param count">
|
|
<call function="f:partial">
|
|
<param name="fnref" select="$fnref" />
|
|
<param name="args" select="$args/foo:a, $args/foo:b" />
|
|
</call>
|
|
|
|
<expect label="returns target reference as first item in
|
|
sequence"
|
|
test="f:QName( $fnref )
|
|
= f:QName( $x:result[ 1 ] )" />
|
|
|
|
<!-- FIXME: this breaks the abstraction; use opaque
|
|
introspection -->
|
|
<expect label="returns each argument, ordered"
|
|
test="$x:result[ 2 ] is $args/foo:a
|
|
and $x:result[ 3 ] is $args/foo:b" />
|
|
</scenario>
|
|
|
|
|
|
<!-- our implementation can be thought of like currying -->
|
|
<scenario label="given arguments for all function params">
|
|
<call function="f:partial">
|
|
<param name="fnref" select="$fnref" />
|
|
<param name="args" select="$args/foo:a,
|
|
$args/foo:b,
|
|
$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>
|
|
|
|
|
|
<!-- partial applications should behave as their own functions -->
|
|
<scenario label="partially applying partial application, with
|
|
fewer arguments than target function arity">
|
|
<call function="f:partial">
|
|
<param name="fnref" select="f:partial( $fnref, $args/foo:a )" />
|
|
<param name="args" select="$args/foo:b" />
|
|
</call>
|
|
|
|
<expect label="returns target reference as first item in
|
|
sequence"
|
|
test="f:QName( $fnref )
|
|
= f:QName( $x:result[ 1 ] )" />
|
|
|
|
<expect label="returns arguments from both partial applications"
|
|
test="count( $x:result ) = 3" />
|
|
|
|
<expect label="arguments are ordered with previous applications
|
|
first in sequence"
|
|
test="$x:result[ 2 ] is $args/foo:a
|
|
and $x:result[ 3 ] is $args/foo:b" />
|
|
</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 label="f:is-partial predicate">
|
|
<scenario label="given a non-none">
|
|
<call function="f:is-partial">
|
|
<param name="fnref"
|
|
select="5" />
|
|
</call>
|
|
|
|
<expect label="returns false()"
|
|
test="$x:result = false()" />
|
|
</scenario>
|
|
|
|
|
|
<scenario label="given a non-fref node">
|
|
<call function="f:is-partial">
|
|
<param name="fnref">
|
|
<foo:moo />
|
|
</param>
|
|
</call>
|
|
|
|
<expect label="returns false()"
|
|
test="$x:result = false()" />
|
|
</scenario>
|
|
|
|
|
|
<!-- this leaks implementation details :x -->
|
|
<scenario label="given a non-fref node with spoofed attribute">
|
|
<call function="f:is-partial">
|
|
<param name="fnref">
|
|
<foo:moo partial="5" f:partial="5" />
|
|
</param>
|
|
</call>
|
|
|
|
<expect label="returns false()"
|
|
test="$x:result = false()" />
|
|
</scenario>
|
|
|
|
|
|
<scenario label="given a valid fref, not partially applied">
|
|
<call function="f:is-partial">
|
|
<param name="fnref"
|
|
select="foo:ternary()" />
|
|
</call>
|
|
|
|
<expect label="returns false()"
|
|
test="$x:result = false()" />
|
|
</scenario>
|
|
|
|
|
|
<!-- if partially applied with no arguments, then it may as well
|
|
not be; let's not alter the logic of callers just because it
|
|
was called with no effect -->
|
|
<scenario label="given a valid fref, partially applied with zero
|
|
arguments">
|
|
<call function="f:is-partial">
|
|
<param name="fnref"
|
|
select="f:partial( foo:ternary(), () )" />
|
|
</call>
|
|
|
|
<expect label="returns false()"
|
|
test="$x:result = false()" />
|
|
</scenario>
|
|
|
|
|
|
<scenario label="given a valid fref, partially applied with N>0
|
|
arguments, including argument sequence">
|
|
<call function="f:is-partial">
|
|
<param name="fnref"
|
|
select="f:partial( foo:ternary(), (1, 2) )" />
|
|
</call>
|
|
|
|
<expect label="returns true()"
|
|
test="$x:result = true()" />
|
|
</scenario>
|
|
|
|
|
|
<!-- caller may extract the function ref from the sequence and
|
|
assert on that; we are going to explicitly choose to *not*
|
|
support that behavior, since an fnref acquires meaning as an
|
|
opaque unit -->
|
|
<scenario label="given a valid fref, partially applied with N>0
|
|
arguments, without argument sequence">
|
|
<call function="f:is-partial">
|
|
<param name="fnref"
|
|
select="f:partial( foo:ternary(), (1, 2) )[ 1 ]" />
|
|
</call>
|
|
|
|
<expect label="returns false()"
|
|
select="false()" />
|
|
</scenario>
|
|
</scenario>
|
|
|
|
|
|
<scenario label="f:partial-arity-error-hook mode">
|
|
<scenario label="given more arguments than target function
|
|
parameters">
|
|
<!-- see partial-test.xsl for the result sequence order -->
|
|
<call function="f:partial">
|
|
<param name="fnref" select="$fnref" />
|
|
<param name="args" select="$args/foo:a,
|
|
$args/foo:b,
|
|
$args/foo:c,
|
|
$args/foo:d" />
|
|
</call>
|
|
|
|
<expect label="produces an error"
|
|
test="$x:result[ 1 ] instance of
|
|
element( foo:partial-error )" />
|
|
|
|
<expect label="provides source dynamic function reference"
|
|
test="$x:result[ 2 ] is $fnref" />
|
|
|
|
<expect label="provides arity of target (root of all partial
|
|
applications) function"
|
|
test="$x:result[ 1 ]/@arity = 3" />
|
|
|
|
<expect label="provides each argument of partial application
|
|
attempt, including excess"
|
|
test="$x:result[ 3 ] is $args/foo:a
|
|
and $x:result[ 4 ] is $args/foo:b
|
|
and $x:result[ 5 ] is $args/foo:c
|
|
and $x:result[ 6 ] is $args/foo:d" />
|
|
</scenario>
|
|
|
|
|
|
<scenario label="triggered when sum of partial applications yields
|
|
more arguments than target parameters">
|
|
<call function="f:partial">
|
|
<param name="fnref"
|
|
select="f:partial( $fnref, ($args/foo:a,
|
|
$args/foo:b) )" />
|
|
<param name="args" select="$args/foo:c,
|
|
$args/foo:d" />
|
|
</call>
|
|
|
|
<expect label="produces error with all arguments"
|
|
test="exists( foo:partial-error )
|
|
and $x:result[ 3 ] is $args/foo:a
|
|
and $x:result[ 6 ] is $args/foo:d" />
|
|
</scenario>
|
|
</scenario>
|
|
</description>
|