xmlhaskellhxt

HXT: How to use output of an arrow as function argument?


It's very difficult to give good title to this question… I'm stuck with HXT again. I understand what I want to do, but I'm not sure how to make it play nicely with arrows. Here I give simplified description of the problem.

Function foo takes an Int and returns an arrow:

foo :: ArrowXml a => Int -> a XmlTree XmlTree

Function bar extracts value of some attribute:

bar :: ArrowXml a => a XmlTree String

Now, I need to write baz that takes a map from Strings to Ints and returns an arrow:

import qualified Data.Map.Lazy as M

baz :: ArrowXml a => M.Map String Int -> a XmlTree XmlTree

Logic of baz: extract value of the attribute with bar and look up it in the map. If M.lookup returns Just x, invoke foo x, otherwise don't do anything (input of the arrow goes through unchanged).

AFAIK every such an arrow works as a filter, so in reality ArrowXml a => a XmlTree String type means that it takes a XmlTree and returns (possibly empty) list of Strings. This makes me reformulate logic of baz. For given input XmlTree there may be many strings, every string should be used to look up an integer and first found integer should be passed to foo. If all of them result in Nothing, don't do anything.

Here what I've come up with:

baz :: ArrowXml a => M.Map String Int -> a XmlTree XmlTree
baz m = this &&& (bar >>> arr (`M.lookup` m)) >>> arr (uncurry f)
    where f xml Nothing  = xml
          f xml (Just x) = foo x xml
-- compiler says:          ^^^ not so fast, boy

Could not deduce (ArrowXml (->)) arising from a use of ‘foo’
from the context (ArrowXml a)
  bound by the type signature for
             baz :: ArrowXml a => M.Map String Int -> a XmlTree XmlTree

Not only compiler doesn't like it, but it's difficult to reason about too.


Solution

  • I took me some time to understand how to accomplish this, because when your arrow has type like a (b, XmlTree) XmlTree, you can really use it, because of type conflicts with the rest of the API.

    Here is another solution that seems to be more idiomatic:

    baz :: ArrowXml a => M.Map String Int -> a XmlTree XmlTree
    baz m = maybe this foo $< (bar >>> arr (`M.lookup` m))
    

    All the magic happens because of ($<) function. From the documentation:

    compute the parameter for an arrow with extra parameters from the input and apply the arrow for all parameter values to the input

    See also this section of HXT Wiki: 8.2 Transform external references into absolute references.