No access to a node's parent in a curried fn.

FXSL Help
2008-12-01
2013-04-15
  • Christoph Lange

    Christoph Lange - 2008-12-01

    Hi Dimitre, hi all,

      first of all, sorry for not being able to make the subject of this post nicer, but SourceForge restricts the length.

    Yesterday I wasted several hours on rewriting some of my code in functional style, only to realize that certain things are simply not possible.  I wanted to write a curried function that takes a node as an argument, and then I wanted that function to traverse the parent or ancestor axes.  Examining the implementation of f:curry I realized that this obviously doesn't work, as arguments to a curried function are reparented into an f-curry data structure.

    I'm afraid there is no way of making this work, is there?  Maybe it would be nice to push the XSLT developers to either add a data type for "pointers" to nodes, or, of course, to build functional programming into the language ;-)  But I'm not sure if that works.

    For now, Dimitre, may I suggest that you add a warning to the documentation?  Maybe in a section titled "known limitations".

    Thanks in advance,

    Christoph

     
    • Dimitre Novatchev

      Hi Christoph,

      A very nice post, thank you.

      Yes, this is a limitation of XSLT. We have a generate-id() function, but not its reverse.

      For now, a general solution is to pass not the node itself, but a function (and its parameters) that returns the node.

      This function can do something as simple as:

        document($pDocUrl)/identify-the-node-uniquely(other params)

      The document()  function is guaranteed by the Spec to always return the same document-node(). Therefore, the above function will always return exactly the same node.

      This is a temporary solution and I will be looking for something better.

      I will also notify the other guys on the team and ask for their opinion.

      Cheers,
      Dimitre

       
      • Christoph Lange

        Christoph Lange - 2008-12-02

        Hi Dimitre,
        thanks for your notification. I selected "monitor this forum" and will now automatically be notified by mail.
        So how would identify-the-node-uniquely(other params)  be implemented?  I have one idea, which is not really nice, but may work: While we still have the original node, how about a function that computes a string containing an XPath that uniquely points to this node? I did a quick hack in the style of "/*[1]/*[2]" XPaths below, just without the XPath syntax.  But maybe there is a shorter solution.

        Cheers,
        Christoph

        ----

            <xsl:function name="f:xpath-step" as="element()*">
                <f:xpath-step/>
            </xsl:function>

            <xsl:template match="f:xpath-step" mode="f:FXSL">
                <xsl:param name="arg1" as="node()"/>
                <xsl:param name="arg2"/>
                <xsl:sequence select="f:xpath-step($arg1,$arg2)"/>
            </xsl:template>

            <xsl:function name="f:xpath-step">
                <xsl:param name="node" as="node()"/>
                <xsl:param name="step"/>
                <xsl:sequence select="$node/*[$step]"/>
            </xsl:function>

            <xsl:function name="f:get-node">
                <xsl:param name="document-uri"/>
                <xsl:param name="xpath"/>
                <xsl:sequence select="f:foldl(f:xpath-step(), document($document-uri), $xpath)"/>
            </xsl:function>

            <xsl:function name="f:get-path" as="xs:integer*">
                <xsl:param name="node"/>
                <xsl:sequence select="for $p in $node/ancestor-or-self::* return count($p/preceding-sibling::*) + 1"/>
            </xsl:function>

        Example:

            <xsl:template match="/*[1]/*[2]">
                <xsl:message>before</xsl:message>
                <xsl:message select="."/>
                <xsl:variable name="xpath" select="f:get-path(.)"/>
                <xsl:message>path</xsl:message>
                <xsl:message>
                    <xsl:value-of select="$xpath" separator="/"/>
                </xsl:message>
                <xsl:message>after</xsl:message>
                <xsl:message select="f:get-node('', $xpath)"/>
            </xsl:template>

         
        • Dimitre Novatchev

          Hi Christoph,

          Yes, one general solution is to pass the root node of the document (I think then when it is curried the whole document will be copied), and an identifying XPath expression. I have a code snippet for producing such an expression for every type of node; here:

             http://www.topxml.com/code/cod-422_10053_build-a-xpath-expression-for-a-node.aspx

          I still think that the efficient way to do node identification would be to have two extension functions:

              bookmark  =  xx:bookmarkNode(node)

          and

              node =  xx:getNode(bookmark)

          and a possible implementation is to use an URI-Resolver.

          the standard generate-id() may be used to generate the bookmark, however we still need something to remember the association (bookmark, node).

          Could try implementing this in the .NET environment when I have free time.

          I guess Michael Kay could implement such extension functions in Saxon; we only need to convince him they are needed :)

           
          • Florent Georges

            Florent Georges - 2008-12-06

              Dear fellows,

              That's a very interesting subject (thanks Dimitre for the reminder
            on the mailing list, I thought I should receive a notification, as I
            actually monitor the forum, but I didn't; weird...)

              I have another way in mind, though, to solve that problem, that
            would also be effecient and even more general: the ability to have
            nested sequences.  That reminds me to ask about this for XSLT 2.1;
            thanks ;-)

              Because we already have a way to refer to nodes: as an item in a
            sequence.  The reason it is not usable for currying is because we want
            to create a single one item to identify the curried function.  If we
            return a sequence, we wouldn't be able to use it in another sequence,
            as it would then be atomized.

              So we are stuck: either we put the node in an XML structure (as it
            is done for now) and then it is copied (or at least that is as if it
            was) or we return just the node but then, we can't return currying
            information...  So as you said, we have to rely on extensions here.

              With nested sequences, we could simply return such a sequence (like
            a regular sequence, but that would never been automatically atomized,)
            with the currying info and then the curried items.  I had such an
            implementation for Saxon for years, but it was on the laptop that one
            stole to me :-(  But the idea was more or less to have a tiny proxy
            object in Java that stands for a whole sequence, and an XSLT module
            that encapsulate the creation of this object from a sequence and to
            get the sequence back from this object.

              I think this kind of sequences would help in currying implementation
            (and help solving the problem currently discussed,) as well as being
            more general and usable in other contexts.

              What do you think?  I could try to write again a PoC for this...

              Regards,

            --
            Florent Georges
            http://www.fgeorges.org/

             
    • Christoph Lange

      Christoph Lange - 2008-12-02

      Oops, the forum destroyed my indentation :-( Anyway, the code works.

       
    • Dimitre Novatchev

      > With nested sequences, we could simply return such a
      > sequence (like
      > a regular sequence, but that would never been
      > automatically atomized,)
      > with the currying info and then the curried items. I
      > had such an
      > implementation for Saxon for years

      Funny thing, I thought just a couple of days that my implementation of Finger Tree could be used.

      Of course, it doesn't limit the types of its items so any item can itself be a finger tree ...  :)

      Cheers,
      Dimitre

       
      • Florent Georges

        Florent Georges - 2008-12-07

          Interesting.  I didn't know Finger Trees.  If I am right,
        this is a particular kind of sequences, implemented by the
        help of a tree, and allowing an efficient implementation for
        some operations.

          But maybe in this case this is too much specialized and a
        simple nestable sequences API would be simpler?  (Finger Trees
        being one possible implementation of the API) If I am right,
        Finger Trees could be used in the processor implementation
        itself, helping in the string operations...

        -- 
        Florent Georges
        http://www.fgeorges.org/

         
      • Florent Georges

        Florent Georges - 2008-12-08

          Actually, there is a problem I didn't think about earlier.
        The goal is not to get a sequence that behaves as a single
        item (that is not atomized) but more precisely as a single
        *node*, because the FXSL dispatching mechanism is based on
        apply-templates.

          The only solution I can see would be to change the f:apply()
        implementation, to handle such a sequence as a special case in
        which applying templates to its first item...

        -- 
        Florent Georges
        http://www.fgeorges.org/

         

Log in to post a comment.