1
votes

I want to reuse some code like the following:

instance ToJavascript j => YesodTemplate j where toBuilder = toJavascript

This requires overlapping using wrapped instances , etc. So the solution I have seen is to use a wrapper.

newtype Wrapped a = Wrapped { unwrapped :: using a }
instance ToJavascript j => YesodTemplate (Wrapped j) j where
  toBuilder = toJavascript

I have the extra unwrapped j in there so data declaration with default functions that I can write the template class as

class YesodTemplate yt inner where
  toBuilder :: inner -> Builder
  file :: (inner -> yt) -> FilePath -> Q Exp
  file wrap fp = readFileQ fp >>= stringToTH wrap

The wrap function is a dummy to satisfy the type system. But this still won't compile.

juliusFile :: FilePath -> Q Exp juliusFile = file (Wrapped :: ToJavascript j => j -> Wrapped j) Ambiguous type variable `inner1' in the constraint: (ToJavascript inner1) arising from an expression type signature Probable fix: add a type signature that fixes these type variable(s) In the first argument one of `file', namely `(Wrapped :: ToJavascript j => j -> Wrapped j)' In the expression: file (Wrapped :: ToJavascript j => j -> Wrapped j) In an equation for `juliusFile': juliusFile = file (Wrapped :: ToJavascript j => j -> Wrapped j)
1
The template haskell issue is a bit of a red herring. You should be able to get all the type errors that you're concerned about without template haskell, and then be able to reason about them much more reasonably. Use the -ddump-splices flag to see the generated TH. - sclv
When I run it with -ddump-splices it doesn't give any more useful information than the compiler already gave to me- it already gives the slice where the problem occurred. But yes, the problem is in my reasoning about the types, not the TH. I haven't created something with these kinds of complex types before. - Greg Weber
If you could post a distilled down bit of code that was template haskell free and illustrated your problem, it would be much more straightforward to think about and possibly help with. - sclv
Personally I hold polymorphic instances e.g. instance ToJavascript j => YesodTemplate j where ... to be a design flaw. I don't think the behavior of GHC is specified that the compiler will always choose the polymorphic instance or a type specialized instance defied elsewhere (maybe it chooses first defined?). There was a thread in Jan/Feb '11 on Haskell Cafe about it and I don't think anyone commented with a definitive description of what GHC should do or what it does do. The thread was named "Inheritance and Wrappers". - stephen tetley
Also for the purposes of discussion I've called such instances "Overarching Instances" - it might be handy if the term catches on :-) - stephen tetley

1 Answers

0
votes

Thanks for the help and encouragement from everyone!

What I ended up doing instead of using wrapped instances is to use a data declaration with default functions that can be overridden. This approach is a lot cleaner because there are no wrappers and no dummy variables- I think Stephen was right on that the original approach just isn't something that haskell/GHC was made for.