[added debug section to TemplatesHSP Jeremy Shaw **20120207222817 Ignore-this: e70051734b432592868c3bb4c20af2dc ] hunk ./TemplatesHSP.markdown.lhs 7 -

Using HSP

-

To enable HSP support, you must install the happstack-hsp package.

+

Using HSX/HSP

+

To enable HSX support, you must install the happstack-hsp package.

hunk ./TemplatesHSP.markdown.lhs 10 -HSP is an XML-based templating system that allows you to embed XML in your Haskell source files. If you have ever had to use PHP, you may want to run screaming from this idea. However, the HSP solution is far saner than the PHP solution, so you may want to give it a chance. +HSX is an XML-based templating system that allows you to embed XML in your Haskell source files. If you have ever had to use PHP, you may want to run screaming from this idea. However, the HSX solution is far saner than the PHP solution, so you may want to give it a chance. hunk ./TemplatesHSP.markdown.lhs 23 -HSP works by running the code through an external pre-processor named `trhsx`. This pragma at the top is how we tell GHC that this file needs to be run through the `trhsx` pre-processor in order to work. So, that options line looks a bit like line noise. You can try to remember it like this: +HSX works by running the code through an external pre-processor named `trhsx`. This pragma at the top is how we tell GHC that this file needs to be run through the `trhsx` pre-processor in order to work. So, that options line looks a bit like line noise. You can try to remember it like this: hunk ./TemplatesHSP.markdown.lhs 35 +> import Data.String (IsString(fromString)) +> import Data.Text (Text) hunk ./TemplatesHSP.markdown.lhs 104 -In order to use HSP it is very useful to understand what is actually +In order to use HSX it is very useful to understand what is actually hunk ./TemplatesHSP.markdown.lhs 385 +> empty :: IO () hunk ./TemplatesHSP.markdown.lhs 450 +> ifThen :: Bool -> IO () hunk ./TemplatesHSP.markdown.lhs 471 +> attrList :: IO () hunk ./TemplatesHSP.markdown.lhs 492 +> optAttrList :: Bool -> IO () hunk ./TemplatesHSP.markdown.lhs 502 + +

HSX and compilation errors

+ +One drawback to HSX is that it can result in some pretty ugly (and sometimes very long) error messages. Fortunately, the errors are almost always the same type of thing, so after a little experience it is easy to see what is going wrong. Here are some tips if you run into errors: + +

Line numbers are usually wrong

+ +As we saw, trhsx transforms the literal XML into normal Haskell code. Unfortunately, the error positions reported by GHC reflect where the error occurred in the transformed code, not the original input. HSX tries to help GHC by inserting LINE pragmas. While that helps to a degree, it still leaves a fair bit of fuzz. + +The trick is to look towards the bottom of the error message where it will usually show you the expression that contained the error. For example, if we have: + +
+#ifdef HsColour +> typeError :: XMLGenT (ServerPartT IO) XML +> typeError = <% 1 + 'a' %> +#endif +
+ +We will get an error like: + +
+
+TemplatesHSP.markdown.lhs:459:59:
+    No instance for (Num Char)
+      arising from a use of `+'
+    Possible fix: add an instance declaration for (Num Char)
+    In the first argument of `asChild', namely `(1 + 'a')'
+    In the first argument of `asChild', namely `((asChild (1 + 'a')))'
+    In the expression: asChild ((asChild (1 + 'a')))
+
+
+ +The last line says: + +
+
+    In the expression: asChild ((asChild (1 + 'a')))
+
+
+ +which is, indeed, where the type error is. + +A bug report about the line number issue has been filed, and there are ideas on how to fix it. You can read more here. + + +

Overlapping Instances

+ +Another common error is that of overlapping instances. For example, if we wrote the following: + +
+#ifdef HsColour +> overlapping =

overlapping

+#endif +
+ +We would get an error like: + +
+
+TemplatesHSP.markdown.lhs:495:36:
+    Overlapping instances for EmbedAsChild m0 [Char]
+      arising from a use of `asChild'
+    Matching instances:
+      instance [overlap ok] HSX.XMLGen m => EmbedAsChild m String
+        -- Defined in `HSX.XMLGenerator'
+      instance EmbedAsChild Identity String -- Defined in `HSP.Identity'
+      instance Monad m => EmbedAsChild (ServerPartT m) String
+        -- Defined in `HSP.ServerPartT'
+    (The choice depends on the instantiation of `m0'
+     To pick the first instance above, use -XIncoherentInstances
+     when compiling the other instance declarations)
+    In the expression: asChild ("overlapping")
+    In the third argument of `genElement', namely
+      `[asChild ("overlapping")]'
+    In the expression:
+      (genElement (Nothing, "p") [] [asChild ("overlapping")])
+
+
+ +I have never enabled `IncoherentInstances` and actually had it do what +I wanted. In this case, the solution is to add an explicit type +signature that mentions the missing constraint: + +
+ +> overlapping :: (EmbedAsChild m String) => XMLGenT m (HSX.XML m) +> overlapping =

overlapping

+ +
+ +In general, there can be a lot of required `EmbedAsChild` and +`EmbedAsAttr` instances. So, often times you can save a lot of typing +by using the `XMLGenerator` class alias: + +
+ +> overlapping' :: (XMLGenerator m) => XMLGenT m (HSX.XML m) +> overlapping' =

overlapping

+ +
+ +Sometimes a type signature for the parent function is not enough. For example, let's say we have: + +
+#ifdef HsColour +> overlapping2 :: (EmbedAsChild m String) => XMLGenT m (HSX.XML m) +> overlapping2 =

<% fromString "overlapping" %>

+#endif +
+ +That will generate an error like this one: + +
+
+TemplatesHSP.markdown.lhs:557:28:
+    Ambiguous type variable `c0' in the constraints:
+      (IsString c0)
+        arising from a use of `fromString'
+        at TemplatesHSP.markdown.lhs:557:28-37
+      (EmbedAsChild m c0)
+        arising from a use of `asChild'
+        at TemplatesHSP.markdown.lhs:557:19-25
+    Probable fix: add a type signature that fixes these type variable(s)
+    In the first argument of `asChild', namely
+      `(fromString "overlapping")'
+    In the first argument of `asChild', namely
+      `((asChild (fromString "overlapping")))'
+    In the expression: asChild ((asChild (fromString "overlapping")))
+Failed, modules loaded: none.
+
+
+ +Here we are trying to use `fromString` to convert `"overlapping"` into some type, and then we embed that type using `asChild`. But there is not enough information to figure out what the intermediate type should be. It is the same problem we have if we try to write: + +
+#ifdef HsColour +> \str -> show (read str) +#endif +
+ +The solution here is to add an explicit type signature to the result of `fromString`: + +
+ +> overlapping2 :: (EmbedAsChild m Text) => XMLGenT m (HSX.XML m) +> overlapping2 =

<% (fromString "overlapping") :: Text %>

+ +
+ +

HSX + OverloadedStrings

+ +Unfortunately, HSX and the `OverloadedStrings` extension do not play together nicely. If we were to write: + +
+ +> overloaded :: XMLGenT Identity XML +> overloaded =

Hello

+ +
+ +That would be transformed into: + +
+#ifdef HsColour +> overloaded = (genElement (Nothing, "p") [] [asChild ("Hello")]) +#endif +
+ +However, with the `OverloadStrings` extension enabled, the compiler will automatically insert `fromString` in front of every string literal like this: + +
+#ifdef HsColour +> overloaded = (genElement (Nothing, fromString "p") [] [asChild (fromString "Hello")]) +#endif +
+ +Which results in the overlapping instances error as we saw in the last section. + +The best workaround for this at the moment is to not use `OverloadedStrings` and `HSX` in the same module. If you really want `OverloadedStrings` in some of your code, then you might want to put all your HSX templates in a separate module that does not use the extension. Aside from working around the issue, it can also help to improve your code structure. Many people frown on mixing the presentation and business logic layers. Putting your templates in separate modules can help keep the presentation and business layer separate. + +Finding a better solution is still an open problem which has not been examined yet. It would be great if HSX played nicely with `OverloadedStrings`. It would also be nice to create an XML type for use with HSX that was based around `Text`. These two things are a bit intertwined since you generally want to use `OverloadedStrings` when using `Text`. + +