Re: [Haskell-cafe] [Snap] Argument Substitution in Heist Templates with Splices

2012-09-23 Thread Sebastian Fischer
Thanks! For the record, here is how to achieve what I want by
explicitly using `runChildren` and `stopRecursion`:

testSplice :: Splice IO
testSplice = do
input - getParamNode
kids - runChildren
stopRecursion
return [input { elementChildren = kids }]

It surprises me that an explicit call to `runChildren` is necessary,
especially after your comment regarding sensitivity to evaulation
order.

Your linked post shows that Heist splices are processed top down,
which reminds me of the `transform` combinator in Uniplate:


http://community.haskell.org/~ndm/downloads/paper-uniform_boilerplate_and_list_processing-30_sep_2007.pdf

The authors discuss bottom-up and top-down transformations in Sections
2.3 and 2.4 and argue for providing bottom-up transformations and only
a specific form of top-down transformations.

I think Heist's splice processing would be more intuitive (less
sensitive to evaluation order?) if applied bottom up rather than top
down. This only seems to require a slight change in the definition of
`runNode` from the post you linked - to process children before
applying the splice:

runNode :: Monad m = X.Node - Splice m
runNode (X.Element nm at ch) = do
newAtts - mapM attSubst at
newKids - runNodeList ch  -- added this line
let n = X.Element nm newAtts newKids -- changed this line
s - liftM (lookupSplice nm) getTS
maybe n (recurseSplice n) s  -- changed this line
  -- removed local function `runKids`
runNode n= return [n]

This change would simplify the definition of filter splices which
would not need to call `runChildren` explicitly. It would also make
the definition of substitution splices more uniform, because children
would be already processed when applying the splice - just like
attributes are.

Are Heist splices processed top down intentionally? (Reasons for doing
so are the same reasons people might have for preferring call-by-name
over call-by-value. However, I tend to agree with the discussion in
the Uniplate paper and would prefer call-by-value aka bottom-up
transformation.)

Best,
Sebastian

On Fri, Sep 21, 2012 at 6:03 PM, MightyByte mightyb...@gmail.com wrote:
 This is one of the more subtle corner cases of Heist.  My default, splices
 are recursively processed.  So when testSplice is executed for the test
 tag, the results are fed back into splice processing.  I think this is the
 right thing to do because it makes behavior less sensitive to evaluation
 order.  Obviously this can lead to infinite recursion, so Heist limits the
 splice call stack to a depth of 50.  If this limit is exceeded, then Heist
 simply stops recursing and returns the nodes unprocessed.  I also think this
 is the right thing to do because it is happening as we're serving a page to
 the end user, so there's an argument for failing quietly instead of going up
 in a ball of flames.

 In your case, you are returning the same node that was spliced in, so you
 are hitting the recursion limit and splice processing just stops.  I discuss
 this issue in my blog post about splice subtleties
 (http://softwaresimply.blogspot.com/2011/04/splice-subtleties.html).  Since
 you're writing a filter splice, you need to call stopRecursion.  But if you
 do that, then the child arg / tag won't be processed.  So what you need to
 do is use the runChildren function to process the child nodes, then return
 them in whatever your constructed node is.

 I think the easiest solution to your problem is to not write it as a filter
 splice.  Bind your testSplice function to the mytest tag and return a
 test tag.  This avoids the infinite recursion and will work the way you
 want without needing stopRecursion.

 On Thu, Sep 20, 2012 at 3:00 PM, Sebastian Fischer m...@sebfisch.de wrote:

 Hello,

 the following program demonstrates that arguments in Heist templates
 are sometimes not substituted in presence of splices:

 {-# LANGUAGE OverloadedStrings #-}

 import   Blaze.ByteString.Builder (toByteString)
 import qualified Data.ByteString.Char8as BS
 import   Data.Functor (($))
 import   Data.Maybe   (fromJust)
 import   Text.Templating.Heist

 -- just return input node unchanged
 testSplice :: Splice IO
 testSplice = (:[]) $ getParamNode

 main = do
 writeFile test.tpl arg /test attr='${arg}'arg //test
 state - either error id $ loadTemplates . defaultHeistState

 (builder,_) - fromJust $ renderWithArgs [(arg,42)] state test
 BS.putStrLn $ toByteString builder
 -- 42test attr='42'42/test

 let state' = bindSplices [(test,testSplice)] state
 (builder',_) - fromJust $ renderWithArgs [(arg,42)] state'
 test
 BS.putStrLn $ toByteString builder'
 -- 42test attr='42'arg/arg/test

 Without using splices, all occurrences of 'arg' in the template are
 substituted. When using a splice, 'arg' is not substituted underneath
 the input node of the splice. It is substituted in an attribute of the
 input 

Re: [Haskell-cafe] [Snap] Argument Substitution in Heist Templates with Splices

2012-09-21 Thread MightyByte
This is one of the more subtle corner cases of Heist.  My default, splices
are recursively processed.  So when testSplice is executed for the test
tag, the results are fed back into splice processing.  I think this is the
right thing to do because it makes behavior less sensitive to evaluation
order.  Obviously this can lead to infinite recursion, so Heist limits the
splice call stack to a depth of 50.  If this limit is exceeded, then Heist
simply stops recursing and returns the nodes unprocessed.  I also think
this is the right thing to do because it is happening as we're serving a
page to the end user, so there's an argument for failing quietly instead of
going up in a ball of flames.

In your case, you are returning the same node that was spliced in, so you
are hitting the recursion limit and splice processing just stops.  I
discuss this issue in my blog post about splice subtleties (
http://softwaresimply.blogspot.com/2011/04/splice-subtleties.html).  Since
you're writing a filter splice, you need to call stopRecursion.  But if you
do that, then the child arg / tag won't be processed.  So what you need
to do is use the runChildren function to process the child nodes, then
return them in whatever your constructed node is.

I think the easiest solution to your problem is to not write it as a filter
splice.  Bind your testSplice function to the mytest tag and return a
test tag.  This avoids the infinite recursion and will work the way you
want without needing stopRecursion.

On Thu, Sep 20, 2012 at 3:00 PM, Sebastian Fischer m...@sebfisch.de wrote:

 Hello,

 the following program demonstrates that arguments in Heist templates
 are sometimes not substituted in presence of splices:

 {-# LANGUAGE OverloadedStrings #-}

 import   Blaze.ByteString.Builder (toByteString)
 import qualified Data.ByteString.Char8as BS
 import   Data.Functor (($))
 import   Data.Maybe   (fromJust)
 import   Text.Templating.Heist

 -- just return input node unchanged
 testSplice :: Splice IO
 testSplice = (:[]) $ getParamNode

 main = do
 writeFile test.tpl arg /test attr='${arg}'arg //test
 state - either error id $ loadTemplates . defaultHeistState

 (builder,_) - fromJust $ renderWithArgs [(arg,42)] state test
 BS.putStrLn $ toByteString builder
 -- 42test attr='42'42/test

 let state' = bindSplices [(test,testSplice)] state
 (builder',_) - fromJust $ renderWithArgs [(arg,42)] state'
 test
 BS.putStrLn $ toByteString builder'
 -- 42test attr='42'arg/arg/test

 Without using splices, all occurrences of 'arg' in the template are
 substituted. When using a splice, 'arg' is not substituted underneath
 the input node of the splice. It is substituted in an attribute of the
 input node.

 Is this intentional? How can I ensure substitution also underneath the
 input node?

 Best,
 Sebastian

 ___
 Haskell-Cafe mailing list
 Haskell-Cafe@haskell.org
 http://www.haskell.org/mailman/listinfo/haskell-cafe

___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe


[Haskell-cafe] [Snap] Argument Substitution in Heist Templates with Splices

2012-09-20 Thread Sebastian Fischer
Hello,

the following program demonstrates that arguments in Heist templates
are sometimes not substituted in presence of splices:

{-# LANGUAGE OverloadedStrings #-}

import   Blaze.ByteString.Builder (toByteString)
import qualified Data.ByteString.Char8as BS
import   Data.Functor (($))
import   Data.Maybe   (fromJust)
import   Text.Templating.Heist

-- just return input node unchanged
testSplice :: Splice IO
testSplice = (:[]) $ getParamNode

main = do
writeFile test.tpl arg /test attr='${arg}'arg //test
state - either error id $ loadTemplates . defaultHeistState

(builder,_) - fromJust $ renderWithArgs [(arg,42)] state test
BS.putStrLn $ toByteString builder
-- 42test attr='42'42/test

let state' = bindSplices [(test,testSplice)] state
(builder',_) - fromJust $ renderWithArgs [(arg,42)] state' test
BS.putStrLn $ toByteString builder'
-- 42test attr='42'arg/arg/test

Without using splices, all occurrences of 'arg' in the template are
substituted. When using a splice, 'arg' is not substituted underneath
the input node of the splice. It is substituted in an attribute of the
input node.

Is this intentional? How can I ensure substitution also underneath the
input node?

Best,
Sebastian

___
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe