qt4cg / qtspecs

QT4 specifications
https://qt4cg.org/
Other
29 stars 15 forks source link

xsl:match - can we do better #118

Closed michaelhkay closed 2 years ago

michaelhkay commented 2 years ago

The draft spec proposes an instruction xsl:match to test whether a given item matches a specified pattern, returning a boolean.

While this fills a gap, it's rather clumsy, especially because instructions that return atomic values typically have to be wrapped in xsl:variable or xsl:function to be useful.

It might be better to try and make xsl:apply-templates work more nicely as a function.

The current <xsl:match select="N" match="P"/> is roughly equivalent to <xsl:apply-template mode="test-pattern" select="N"/> where

<xsl:mode name="test-pattern" as="xs:boolean">
   <xsl:template match="P"><xsl:sequence select="true()"/></xsl:template>
   <xsl:template match="."><xsl:sequence select="false()"/></xsl:template>
</xsl:mode>

Can we improve this?

(a) we could allow <xsl:mode name="test-pattern" function-name="my:test"/> so that a function call my:test(XXX) is equivalent to the instruction <xsl:apply-templates mode="test-pattern" select="X"/>: that is, each mode can declare the name of a function whose effect is to apply templates in that mode (with no parameters).

(b) we could allow a select attribute on xsl:template to provide a quick way of returning the result, avoiding xsl:sequence.

(c) we could allow <xsl:mode on-no-match="return false()"/> to avoid the fallback template rule.

The use-case would then become

<xsl:mode name="test-pattern" as="xs:boolean" on-no-match="return false()" function-name="my:test">
   <xsl:template match="P" select="true()"/>
</xsl:mode>
gimsieke commented 2 years ago

Is this function-name declaration supposed to be available for any xsl:mode declaration? What happens with tunnel parameters when xsl:apply-templates is replaced with a function invocation?

<xsl:mode name="mode2" function-name="my:mode2"/>

<xsl:template match="/">
  <xsl:apply-templates mode="mode1">
    <xsl:with-param name="foo" select="'bar'" tunnel="yes" as="xs:string"/>
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="/*" mode="mode1">
  <xsl:sequence select="my:mode2(node())"/>
</xsl:template>

<xsl:template match="/*/*" mode="mode2">
  <xsl:param name="foo" as="xs:string" tunnel="yes"/>
  …
</xsl:template>

Is $foo available when the last template matches?

It would be available if it were invoked with

<xsl:template match="/*" mode="mode1">
  <xsl:apply-templates mode="mode2"/>
</xsl:template>
michaelhkay commented 2 years ago

Good question about tunnel parameters. My thinking was no, this function only exposes a subset of the xsl:apply-templates functionality - e.g. no sorting, no with-param, no focus (just the context item as the single function argument).

rhdunn commented 2 years ago

I like all of these proposals (a, b, and c). The only thing to note is that if xsl:mode/@on-no-match is intended to be an xpath expression like xsl:sequence/@select then the example should be false() instead of return false(), as return is only a valid keyword in FLWOR expressions.

MHK response: my thinking was to let on-no-match take the syntax ( 'shallow-copy' | 'shallow-skip' | ... | 'return' Expr ) where return is a keyword that indicates that an XPath expression follows. But perhaps that's clumsy.

michaelhkay commented 2 years ago

Coming back to the original motivation for this, it feels like too little benefit to justify the complexity.

An alternative would be for xsl:switch to allow a match="pattern" attribute as an alternative (mutually exclusive) to the test attribute (and perhaps test should be renamed value to make the distinction clearer). Because we're moving in the direction of aligning patterns and types, this would give something like XQuery's typeswitch in XSLT. But a switch with match attributes isn't very different from a mode with inline template rules, so is it worth it?

I've also thought about <xsl:if match="pattern"> as an alternative to <xsl:if test="expression"> but I fear the distinction between match="*" and test="*" is too subtle.

michaelhkay commented 2 years ago

I haven't found a design for this requirement that I'm comfortable with, so I'm going to drop the feature. I'll delete the draft from the spec.