- From: Norman Walsh <ndw@nwalsh.com>
- Date: Thu, 15 Oct 2009 14:58:05 -0400
- To: public-xml-processing-model-comments@w3.org
- Message-ID: <m24oq0jxhe.fsf@nwalsh.com>
After much discussion, both in email and at the most recent telcon (http://www.w3.org/XML/XProc/2009/10/15-minutes), it seems that we need to consider a radical overhaul of our versioning strategy. ============================================================ Problem statement: Following our telcon, at a high level, we want to: 1. Remove the requirement to load step declarations Consensus appears to be that it's too great a burden on users to require explicit declarations of 2.0 step types to be available to a 1.0 processor when evaluating a 2.0 pipeline. 2. Add some sort of version attribute Satisfying the first requirement is going to require relaxing some of the rules about static checking of the dependency graph. This must only apply when forwards-compatible processing is explicitly enabled. A 1.0 processor looking at a 1.0 pipeline must be able to report errors in the structure of the pipeline statically. 3. Add defaulting rules that will allow dynamic selection (p:choose/p:try) of steps in a backwards compatible way. It should be possible to write a pipeline that will use a V2.0 step in a 2.0 processor and do something else in a V1.0 processor. It ought to be possible to make this choice at runtime using p:choose or p:try. This means that a 1.0 processor operating in forwards-compatible mode must be able to build a dependency graph for a pipeline that includes steps it does not recognize. 4. Add something like XSLT's use-when so that authors can make explicit versioning choices based on statically available information (like the processor version) It seems unlikely that it will be possible to handle every possible future change with defaulting rules. In particular, the addition of a new compound step like p:map-reduce might introduce too much complexity for a 1.0 processor to "parse around" even in a conditional branch. Adding use-when gives the pipeline author complete control over what the 1.0 processor sees. In effect, armed with item 4, an author can always write a pipeline that will work in 1.0 without any new defaulting rules. So with use-when, the additional defaulting rules described above are unnecessary. But for the moment, I'm operating on the assumption that we might like some pipelines to work in a 1.0 processor even without making explicit use of use-when. ============================================================ Proposal 1: Add a version attribute. The version attribute is allowed on any element. On an element in the XProc namespace, it is "version" in no namespace. On an element not in the XProc namespace, it is "p:version" in the XProc namespace. The version value applies to the element on which it occurs and all descendant elements unless or until overriden by another explicit version setting. On an element with an explicit version greater than the highest version known by the processor, forwards-compatible behavior is enabled. ============================================================ Proposal 2: The version attribute is required on top-level p:pipeline, p:declare-step, and p:library elements. The current XProc specification defines the semantics of version "1.0". ============================================================ Proposal 3: Add a use-when attribute The use-when attribute is allowed on any element. On an element in the XProc namespace, it is "use-when" in no namespace. On an element not in the XProc namespace, it is "p:use-when" in the XProc namespace. The value of the use-when attribute is evaluted as an XPath expression with an undefined context node. It is a static error if the expression makes reference to anything in the dynamic context. Conceptually, this expression is evaluated while the pipeline is being loaded. If the effective boolean value of the expression in the use-when attribute is false, the element on which it occurs and all the descendants of that element are removed from the pipeline. It is as if they never occurred and they are not considered in any further static analysis or dynamic evaluation. ============================================================ Proposal 3: Attempt to treate unknown XProc step types as dynamic errors Pipelines that attempt to unconditionally evaluate unknown step types are guaranteed to fail. It's possible that some new XProc steps may have attributes or children that a previous processor could not be expected to interpret. In order to minimize the disruption such elements cause, while at the same time making it as easy as possible to write pipelines that will run in fowards-compatible mode on an earlier processor, we adopt the following rules: In forwards-compatible mode: Steps in the XProc namespace which the processor does not recognize are marked invalid. It is a dynamic error (err:XD00??) to attempt to evaluate a step that is marked invalid. Any subpipeline that directly contains a step which has been marked invalid is also invalid. If a p:when directly contains an unknown XProc step type, that entire branch is marked invalid. No static analysis is performed on the steps within that pipeline. It is a dynamic error (err:XD00??) if that branch of the p:choose is selected at runtime. If a p:group inside a p:try directly contains an unknown XProc step type, that group is marked invalid. No static analysis is performed on the steps within that group. An attempt to evaluate the p:try will immediately evaluate the p:catch with an empty errors port. If all of the branches of a p:choose are marked invalid, then the entire p:choose is invalid. If both the p:group and p:catch branches of a p:try are invalid, then the entire p:try is invalid. If all of the branches of a p:choose or p:try are invalid, the p:choose or p:try is treated as if it had no primary output port. ============================================================ Proposal 4: Support binding defaults in forwards-compatible mode In forwards-compatible mode: It is not a static error to encounter an unknown XProc step type. Such steps are assumed to have no primary input ports, no primary output ports, and no parameter input ports. It is a static error if, under these assumptions, a valid dependency graph cannot be constructed. It is not a static error to encounter explicit bindings to unknown ports on an XProc step type. Such bindings are assumed to be correct. Dynamically, a step must ignore all inputs that appear on an unknown input port and must produce an empty sequence of documents on unknown output ports. ============================================================ Examples: A. No version attribute. The following pipeline is statically invalid because it does not have a top-level version attribute. <p:pipeline> <p:identity/> </p:pipeline> B. Forwards-compatible mode The following pipeline runs in forwards-compatible mode. <p:pipeline version="2.0"> <p:identity/> </p:pipeline> C. Use-when case 1 From the perspective of a 1.0 processor, the following pipeline: <p:pipeline version="2.0"> <p:identity2 use-when="system-property('p:version') > 1.0"/> <p:identity use-when="system-property('p:version') <= 1.0"/> </p:pipeline> behaves exactly as if it was written thus: <p:pipeline version="2.0"> <p:identity/> </p:pipeline> From the perspective of a 2.0 processor, thus: <p:pipeline version="2.0"> <p:identity2/> </p:pipeline> D. Use-when case 2 From the perspective of a 1.0 processor, the following pipeline: <p:pipeline version="2.0"> <p:import use-when="system-property('p:version') > 1.0" href="v2.xpl"/> <p:import use-when="system-property('p:version') <= 1.0" href="v1.xpl"/> <p:pipeinfo> <cfg:value-1 p:use-when="system-property('p:version') <= 1.0"/> <cfg:value-2 p:use-when="system-property('p:version') > 1.0"/> </p:pipeinfo> <p:xslt name="xslt"> <p:input port="stylesheet">...</p:input> </p:xslt> <p:sink/> <p:identity> <p:input port="source" use-when="system-property('p:version') > 1.0"> <p:pipe step="xslt" port="messages"/> </p:input> <p:input port="source" use-when="system-property('p:version') <= 1.0"> <p:empty/> </p:input> </p:identity> </p:pipeline> behaves exactly as if it was written thus: <p:pipeline version="2.0"> <p:import href="v1.xpl"/> <p:pipeinfo> <cfg:value-1/> </p:pipeinfo> <p:xslt name="xslt"> <p:input port="stylesheet">...</p:input> </p:xslt> <p:sink/> <p:identity> <p:input port="source"> <p:empty/> </p:input> </p:identity> </p:pipeline> From the perspective of a 2.0 processor, thus: <p:pipeline version="2.0"> <p:import href="v2.xpl"/> <p:pipeinfo> <cfg:value-2/> </p:pipeinfo> <p:xslt name="xslt"> <p:input port="stylesheet">...</p:input> </p:xslt> <p:sink/> <p:identity> <p:input port="source"> <p:pipe step="xslt" port="messages"/> </p:input> </p:identity> </p:pipeline> E. Dynamic errors 1 The following pipeline is statically valid to an XProc 1.0 processor and will run without errors. <p:pipeline version="2.0"> <p:choose> <p:when test="system-property('p:version') > 1.0"> <p:fribble/> <p:sink/> </p:when> <p:otherwise> <p:identity/> </p:otherwise> </p:choose> </pipeline> The first p:when branch is marked invalid because it contains the unknown (from the 1.0 perspective) step p:fribble. As a result, no static analysis is performed on that subpipeline and that subpipeline does not contribute to the constraint that all the branches of a p:choose must declare the same outputs. F. Dynamic errors 2 The following pipeline is statically valid to an XProc 1.0 processor but will fail at runtime: <p:pipeline version="2.0"> <p:choose> <p:when test="true()"> <p:fribble/> <p:sink/> </p:when> <p:otherwise> <p:identity/> </p:otherwise> </p:choose> </pipeline> A processor may reject this statically, but is not required to do so. G. Binding defaults 1 The following pipeline is valid and will run forwards-compatible mode: <p:pipeline version="2.0"> <p:xslt name="xslt"> <p:input port="stylesheet">...</p:input> </p:xslt> <p:sink/> <p:identity> <p:input port="source"> <p:pipe step="xslt" port="messages"/> </p:input> </p:identity> </p:pipeline> It will always produce an empty sequence. H. Binding defaults 2 The following subpipeline has a well-defined dependency graph, but will be marked invalid: <p:group> <p:output port="result"> <p:pipe step="newv2" port="result"/> </p:output> <p:unknown-type name="newv2"/> </p:group> I. Binding defaults 3 This pipeline is statically invalid. <p:group> <p:output port="result"/> <p:unknown-type name="newv2"/> </p:group> In "H", the explicit binding to the unknown port is not an error. In the this case, the default binding for the output port 'result' is the primary output port of the last step in the subpipeline, however, as far as the V1.0 processor knows, the last step does not have a primary output port. J. Static errors 1 This pipeline is statically valid but will not run <p:declare-step version="2.0"> <p:unknown-type/> </p:declare-step> K. Static errors 2 This pipeline is statically *in*valid: <p:pipeline version="2.0"> <p:choose> <p:when test="system-property('p:version') > 1.0"> <p:unknown/> </p:when> <p:otherwise> <p:unknown/> </p:otherwise> </p:choose> </p:pipeline> There's nothing for the p:output on the p:pipeline to bind to by default. L. Static errors 3 This pipeline is statically valid: <p:declare-step version="2.0"> <p:choose> <p:when test="system-property('p:version') > 1.0"> <p:unknown/> </p:when> <p:otherwise> <p:unknown/> </p:otherwise> </p:choose> </p:declar-step> Though it will necessary fail without running any steps. I haven't attempted to propose what a 1.0 processor should do if the 2.0 step declarations are loaded and they differ from what it expected in 1.0. It is still an error to explicitly load the 1.0 declarations and the 2.0 declarations. I haven't thought through the implications of mixing 1.0 and 2.0 pipelines together. Presumably it all "just works" but I'm not cheerful about the prospect of implmenting that. :-/ Be seeing you, norm -- Norman Walsh <ndw@nwalsh.com> | It's a poor sort of memory that only http://nwalsh.com/ | works backward.--Lewis Carroll
Received on Thursday, 15 October 2009 18:58:47 UTC