Partial application initial development
No support yet for recursive partial applications; just a basic concept.master
parent
0f2212e7d4
commit
8ba1f04147
5
Makefile
5
Makefile
|
@ -19,12 +19,13 @@
|
||||||
path_src := src
|
path_src := src
|
||||||
path_test := test
|
path_test := test
|
||||||
|
|
||||||
test_in_apply_gen := $(path_test)/transform/apply-gen-test-in.xsl.apply
|
test_apply := $(path_test)/apply/partial-test.xsl.apply \
|
||||||
|
$(path_test)/transform/apply-gen-test-in.xsl.apply
|
||||||
|
|
||||||
.PHONY: check test
|
.PHONY: check test
|
||||||
|
|
||||||
test: check
|
test: check
|
||||||
check: $(test_in_apply_gen)
|
check: $(test_apply)
|
||||||
$(path_test)/runner
|
$(path_test)/runner
|
||||||
|
|
||||||
%.apply: %
|
%.apply: %
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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/>.
|
||||||
|
|
||||||
|
Partial applications may only be used with dynamic functions
|
||||||
|
(functions compatible with `f:apply').
|
||||||
|
-->
|
||||||
|
|
||||||
|
<stylesheet version="2.0"
|
||||||
|
xmlns="http://www.w3.org/1999/XSL/Transform"
|
||||||
|
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||||
|
xmlns:f="http://www.lovullo.com/hoxsl/apply">
|
||||||
|
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Partially apply dynamic function reference FNREF
|
||||||
|
|
||||||
|
When provided to `f:apply', FNREF will be applied with ARGS as its
|
||||||
|
beginning argments, followed by any arguments to `f:apply'.
|
||||||
|
|
||||||
|
Note that you usually do not have to invoke this function directly:
|
||||||
|
the dynamic function calls will handle currying/partial application
|
||||||
|
for you, which has a much more inviting syntax.
|
||||||
|
|
||||||
|
TODO: Accept partial application as FNREF.
|
||||||
|
-->
|
||||||
|
<function name="f:partial">
|
||||||
|
<param name="fnref" as="element(f:ref)" />
|
||||||
|
<param name="args" as="item()*" />
|
||||||
|
|
||||||
|
<f:ref>
|
||||||
|
<sequence select="$fnref/@*" />
|
||||||
|
|
||||||
|
<attribute name="partial"
|
||||||
|
select="count( $args )" />
|
||||||
|
|
||||||
|
<sequence select="$fnref/*" />
|
||||||
|
</f:ref>
|
||||||
|
|
||||||
|
<!-- nested sequences are implicitly flattened, so we're not
|
||||||
|
returning a sub-sequence here -->
|
||||||
|
<sequence select="$args" />
|
||||||
|
</function>
|
||||||
|
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Determines whether FNREF is a partial application
|
||||||
|
|
||||||
|
You really should not rely on this for your code—it is intended for
|
||||||
|
internal use. Instead, treat partial applications opaquely as their
|
||||||
|
own functions.
|
||||||
|
-->
|
||||||
|
<function name="f:is-partial" as="xs:boolean">
|
||||||
|
<param name="fnref" as="item()+" />
|
||||||
|
|
||||||
|
<!-- if we're passed a sequence, then the function portion of the
|
||||||
|
reference is first; all others are arguments -->
|
||||||
|
<variable name="fn"
|
||||||
|
select="$fnref[ 1 ]" />
|
||||||
|
|
||||||
|
<!-- we never want to fail, so we perform our type check here rather
|
||||||
|
than using param/@as -->
|
||||||
|
<sequence select="$fn instance of element(f:ref)
|
||||||
|
and exists( $fn/@partial )
|
||||||
|
and number( $fn/@partial ) gt 0" />
|
||||||
|
</function>
|
||||||
|
|
||||||
|
</stylesheet>
|
|
@ -0,0 +1,43 @@
|
||||||
|
<?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/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<stylesheet version="2.0"
|
||||||
|
xmlns="http://www.w3.org/1999/XSL/Transform"
|
||||||
|
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||||
|
xmlns:f="http://www.lovullo.com/hoxsl/apply"
|
||||||
|
xmlns:foo="http://www.lovullo.com/_junk">
|
||||||
|
|
||||||
|
<!-- SUT -->
|
||||||
|
<import href="../../src/apply/partial.xsl" />
|
||||||
|
|
||||||
|
<!-- generated -->
|
||||||
|
<import href="partial-test.xsl.apply" />
|
||||||
|
|
||||||
|
|
||||||
|
<function name="foo:ternary">
|
||||||
|
<param name="x" />
|
||||||
|
<param name="y" />
|
||||||
|
<param name="z" />
|
||||||
|
|
||||||
|
<sequence select="$x, $y, $z" />
|
||||||
|
</function>
|
||||||
|
</stylesheet>
|
|
@ -0,0 +1,202 @@
|
||||||
|
<?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">
|
||||||
|
|
||||||
|
<scenario label="f:partial">
|
||||||
|
<variable name="fnref"
|
||||||
|
select="foo:ternary()" />
|
||||||
|
|
||||||
|
<variable name="args">
|
||||||
|
<foo:parent>
|
||||||
|
<foo:a />
|
||||||
|
<foo:b />
|
||||||
|
<foo:c />
|
||||||
|
</foo:parent>
|
||||||
|
</variable>
|
||||||
|
|
||||||
|
|
||||||
|
<scenario label="given an empty argument list">
|
||||||
|
<call function="f:partial">
|
||||||
|
<param name="fnref" select="$fnref" />
|
||||||
|
<param name="args" select="()" />
|
||||||
|
</call>
|
||||||
|
|
||||||
|
<expect label="returns FNREF"
|
||||||
|
test="$x:result[ 1 ] = $fnref" />
|
||||||
|
|
||||||
|
<expect label="indicates that 0 arguments were partially applied"
|
||||||
|
test="$x:result/@partial = 0" />
|
||||||
|
|
||||||
|
<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 FNREF as first item in sequence"
|
||||||
|
test="$x:result[ 1 ] = $fnref" />
|
||||||
|
|
||||||
|
<expect label="indicates that N arguments were partially applied"
|
||||||
|
test="$x:result/@partial = 2" />
|
||||||
|
|
||||||
|
<expect label="returns each argument, ordered"
|
||||||
|
test="$x:result[ 2 ] = $args/foo:a
|
||||||
|
and exists(
|
||||||
|
$x:result[ 2 ]/parent::foo:parent )
|
||||||
|
and $x:result[ 3 ] = $args/foo:b
|
||||||
|
and exists(
|
||||||
|
$x:result[ 3 ]/parent::foo:parent )" />
|
||||||
|
</scenario>
|
||||||
|
|
||||||
|
|
||||||
|
<scenario label="given arguments for all function params">
|
||||||
|
<call function="f:partial">
|
||||||
|
<param name="fnref" select="$fnref" />
|
||||||
|
<param name="args" select="1, 2, 3" />
|
||||||
|
</call>
|
||||||
|
|
||||||
|
<expect label="returns FNREF as first item in sequence"
|
||||||
|
test="$x:result[ 1 ] = $fnref" />
|
||||||
|
|
||||||
|
<!-- we checked previously for argument values; we assume here
|
||||||
|
that it is working in this case...hopefully that is not
|
||||||
|
wrong! -->
|
||||||
|
<expect label="returns all arguments"
|
||||||
|
test="count( $x:result ) = 4" />
|
||||||
|
</scenario>
|
||||||
|
</scenario>
|
||||||
|
|
||||||
|
|
||||||
|
<scenario label="f:is-partial">
|
||||||
|
<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 -->
|
||||||
|
<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 true()"
|
||||||
|
test="$x:result = true()" />
|
||||||
|
</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>
|
||||||
|
</description>
|
|
@ -73,7 +73,7 @@
|
||||||
<xsl:function name="foo:bar">
|
<xsl:function name="foo:bar">
|
||||||
<xsl:param name="a" />
|
<xsl:param name="a" />
|
||||||
<xsl:param name="b" as="xs:decimal" />
|
<xsl:param name="b" as="xs:decimal" />
|
||||||
<xsl:param name="c" />"
|
<xsl:param name="c" />
|
||||||
</xsl:function>
|
</xsl:function>
|
||||||
</xsl:stylesheet>
|
</xsl:stylesheet>
|
||||||
</context>
|
</context>
|
||||||
|
@ -187,7 +187,7 @@
|
||||||
<scenario label="f:arity">
|
<scenario label="f:arity">
|
||||||
<call function="f:arity">
|
<call function="f:arity">
|
||||||
<param name="fnref" select="foo:sub-two()" />
|
<param name="fnref" select="foo:sub-two()" />
|
||||||
</call>"
|
</call>
|
||||||
|
|
||||||
<expect label="is able to determine arity"
|
<expect label="is able to determine arity"
|
||||||
test="$x:result = 2" />
|
test="$x:result = 2" />
|
||||||
|
|
Loading…
Reference in New Issue