TemplateHaskell class name with conflict newName - haskell

I have a TemplateHaskell function creating a class name:
test :: Q [Dec]
test = do
clsname <- newName "A"
a <- newName "a"
return [
ClassD [] clsname [PlainTV a] [][]
]
The classname is generated with newName, so should be conflict free (the reason is I create the instances directly in TH and don't need it to be visible).
test
test
Schema.hs:27:1: error:
Multiple declarations of ‘A’
Declared at: Schema.hs:26:1
Schema.hs:27:1
However testing it with Debug.Trace, the name of A is indeed something like A_1627476119. This is the same in both GHC 7.10.3 and GHC8. Is this a bug or do I understand it wrong?

newName doesn't work the way you're imagining. It doesn't create a random unused symbol using the supplied string merely as a prefix, and -- as far as I can tell -- Template Haskell doesn't have a standard function to do that. However you can get the equivalent effect with:
gensym :: String -> Q Name
gensym pfx = mkName . show <$> newName pfx
which should work for your anonymous classes:
test :: Q [Dec]
test = do
clsname <- gensym "A" -- use gensym here
a <- newName "a" -- this is fine, though
return [
ClassD [] clsname [PlainTV a] [][]
]
If you're interested in the longer explanation, what newName does do is create a name that cannot be captured by "deeper" bindings, but it does this by attaching additional information to the created Name object, not by mangling the actual name. If such a Name is used to create a binding, the binding uses the original supplied name, not a mangled version.
To see this, note first that the Name created by mkName has more structure than its printable representation suggests:
GHCi> :m Language.Haskell.TH Language.Haskell.TH.Syntax
GHCi> nm <- runQ (newName "foo")
GHCi> nm
foo_16
GHCi> let Name occname nmtype = nm
GHCi> occname
OccName "foo"
GHCi> nmtype
NameU 16
GHCi>
Second, note that the quotation:
[d| one = 1 |]
is equivalent to the following do-block using newName:
do nm <- newName "one"
decl <- valD (varP nm) (normalB (litE (integerL 1))) []
return [decl]
so you can write the following:
{-# LANGUAGE TemplateHaskell #-}
import Language.Haskell.TH
$(do nm <- newName "one"
decl <- valD (varP nm) (normalB (litE (integerL 1))) []
return [decl])
main = print one
illustrating that the "one" name created by newName can be used to create a top-level binding that is referenced in the main function using its plain, unadorned, name: one. (If you created an extra copy of the splice here, you'd get the same "multiple declarations" error you got with your classes.)

Related

How to have a sum-type (ADT) with a known set of string literals?

Is it possible to write code in the following spirit:
data EventTable = "table1" | "table2" | "some_other_table"
case eventTable of
"table1" -> -- do something
"table2" -> -- do something else
"some_other_table" -> -- do something else
"unknown_table"-> -- SHOULD NOT COMPILE
I'm trying to work directly with the string literals that a remote API provides, instead of first mapping them to a regular Haskell sum-type/ADT and having to write serialisation and deserialisation functions for it.
Haskell doesn't have anything like TypeScript's string literal types (which are singleton types: TypeScript will only allow you to use a given string if it can tell that you've checked the string does indeed fit the type), and the best way is probably to just hand-roll a regular datatype and a simple smart constructor. But as #chi points out in the comments, if you have a lot of strings to deal with this is probably a job for code generation.
We'll write a Template Haskell helper to turn splices like
stringLitTy "Foo" ["bar", "baz"]
into a data declaration, a smart constructor, and a toString function:
data Foo = Bar | Baz deriving (Eq, Ord, Bounded, Enum, Show, Read)
mkFoo :: String -> Maybe Foo
mkFoo "bar" = Just Bar
mkFoo "baz" = Just Baz
mkFoo _ = Nothing
fooToString :: Foo -> String
fooToString Bar = "bar"
fooToString Baz = "baz"
The code to do this is simple enough, so if you're not familiar with TH this'll be a good crash course.
First let's create some names for the type and the functions, and a mapping from the string literals to some constructor names.
{-# LANGUAGE TemplateHaskell #-}
module StringLit where
import Data.Char
import Language.Haskell.TH
legaliseCon :: String -> String
legaliseCon (x:xs) = toUpper x : map (\c -> if not (isAlphaNum c) then '_' else c) xs
legaliseFun :: String -> String
legaliseFun (x:xs) = toLower x : map (\c -> if not (isAlphaNum c) then '_' else c) xs
stringLitTy :: String -> [String] -> Q [Dec]
stringLitTy typeName strs =
let tyName = mkName $ legaliseCon typeName
constrName = mkName $ legaliseFun ("mk" ++ typeName)
toStringName = mkName $ legaliseFun (typeName ++ "ToString")
conNames = [(n, mkName $ legaliseCon n) | n <- strs]
in sequenceA [
mkDataDecl tyName (map snd conNames),
mkConstrDecl constrName conNames,
mkToStringDecl toStringName conNames
]
legaliseCon and legaliseFun are blunt instruments to get a string into a form which is valid for a constructor or a function. (There's definitely room for improvement there!) stringLitTy calls mkDataDecl, mkConstrDecl and mkToStringDecl, below, to generate the top-level declarations. They're all pretty simple: mkDataDecl calls dataD to construct the datatype declaration with an appropriate deriving clause.
enumClasses = sequenceA [
[t| Eq |],
[t| Ord |],
[t| Bounded |],
[t| Enum |],
[t| Show |],
[t| Read |]
]
mkDataDecl :: Name -> [Name] -> Q Dec
mkDataDecl tyName conNames =
dataD
(return []) -- datatype context
tyName -- name
[] -- type parameters
Nothing -- kind annotation
[normalC n [] | n <- conNames] -- constructors, none of which have any parameters
enumClasses -- "deriving" classes
mkConstrDecl uses funD to generate the code for the smart constructor (mkFoo), based on the mapping from strings to the generated constructors' names.
mkConstrDecl :: Name -> [(String, Name)] -> Q Dec
mkConstrDecl name map = funD name $ [
clause
[litP $ stringL str] -- the string literal pattern on the LHS
(normalB $ appE [| Just |] (conE con)) -- Just Con on the RHS
[] -- where clauses
| (str, con) <- map]
++ [clause [wildP] (normalB $ [| Nothing |]) []] -- mkFoo _ = Nothing
And mkToStringDecl does much the same, except the constructors are on the left hand side and the string literals are on the right. And there's need for a wildcard clause or the Maybe.
mkToStringDecl :: Name -> [(String, Name)] -> Q Dec
mkToStringDecl name map = funD name [
clause
[conP con []]
(normalB $ litE $ stringL str)
[]
| (str, con) <- map]
So, if I import StringLit in another module and write a splice,
{-# LANGUAGE TemplateHaskell #-}
module Test where
import StringLitTy
stringLitTy "EventTable" ["table1", "table2", "some_other_table"]
I can perform case analysis on the constructors of the generated EventTable type. It's not exactly what you asked for in the question, but I think it gets you 90% of the way there.
tableNumber Table1 = Just 1
tableNumber Table2 = Just 2
tableNumber Some_other_table = Nothing
-- for good measure:
ghci> :l Test
[1 of 2] Compiling StringLitTy ( StringLitTy.hs, interpreted )
[2 of 2] Compiling Test ( Test.hs, interpreted )
Ok, modules loaded: Test, StringLitTy.
ghci> :bro
data EventTable = Table1 | Table2 | Some_other_table
mkEventTable :: [Char] -> Maybe EventTable
eventTableToString :: EventTable -> [Char]
ghci> tableNumber Table1
Just 1
Oh, one more thing: since the Q monad allows you to run IO actions in your splices, you can (say) query the database to get your table names. Template Haskell programming is "just programming", so you can do all the usual Monad stuff with Q (like traverse):
getTablesFromDb :: IO [(String, [String])]
getTablesFromDb = {- ... -}
mkTables :: Q [Dec]
mkTables = do
tables <- runIO getTablesFromDb
concat <$> traverse (uncurry stringLitTy) tables

Generate a data declaration with TemplateHaskell

I wonder how to generate a bunch of constants based on a list of names.
I started with this working example:
ConstantCreation.hs
module ConstantCreation where
import Language.Haskell.TH
createConstant :: String -> Q [Dec]
createConstant constantName = do constantType <- newName constantName
constant <- newName constantName
return [ DataD []
constantType []
[NormalC constant []]
[] ]
MyConstants.hs
{-# LANGUAGE TemplateHaskell #-}
module MyConstants where
import ConstantCreation
$(do constantsDeclarations <- mapM createConstant
[ "MyFirstCustomConstant" ,
"MySecondCustomConstant" ]
return $ mconcat constantsDeclarations)
But things get tricky when I try to add a deriving Show.
I first tried changing the function createConstant like this:
createConstant constantName = do constantType <- newName constantName
constant <- newName constantName
return [ DataD []
constantType []
[NormalC constant []]
[GHC.Show.Show] ]
as suggested if I run the command runQ [d|data MyConstant = MyConstant deriving Show|] in GHCi, but it throws the error Not in scope: data constructor ‘GHC.Show.Show’
So I tried do define my function like this :
createConstant constantName = [d|data $(ConT $ newName constantName) = $(NormalC (newName constantName) []) deriving Show|]
but then I had the following error:
Cannot parse data constructor in a data/newtype declaration: $(NormalC
(newName constantName) [])
It would really be a pitty to have to define Show instances by hand, so I wonder what's going badly.
Thanks for any advice or explanation.
You can use ''Show to get the Type with the name that is in scope.
{-# LANGUAGE TemplateHaskell #-}
module Constant where
import Language.Haskell.TH
createConstant constantName = do
tname <- newName constantName
cname <- newName constantName
return [DataD [] tname [] [NormalC cname []] [''Show]]

Pagination: Error While Converting Parameter Value into Integer

I know there is a Paginator package for Yesod but I prefer a simpler UI so I was creating a simple pagination logic for my app. However, I couldn't figure out a way to convert the parameter value to Integer.
import Data.Text (unpack, singleton)
import Data.Maybe
one = singleton '1' -- convert char to Text, required by fromMaybe
getTestPanelR :: Handler Html
getTestPanelR = do
ptext <- lookupGetParam "p" -- guessing returns Maybe Text
p <- fromMaybe one ptext -- ??? does not work
-- pn <- ??? Once p is extracted successfully, how to convert to an integer?
s <- runDB $ selectList [] [Asc PersonName, LimitTo 10 , OffsetBy $ (pn - 1) * 10]
(widget, enctype) <- generateFormPost $ entryForm Nothing
defaultLayout $ do
$(widgetFile "person")
When I run the above Code I get the following error message:
No instance for (MonadHandler Maybe)
arising from a use of `lookupGetParam'
Possible fix: add an instance declaration for (MonadHandler Maybe)
In the second argument of `($)', namely `lookupGetParam "p"'
In a stmt of a 'do' block:
p <- fromMaybe one $ lookupGetParam "p"
In the expression:
...
When I write out 'ptext' using #{show ptext} it shows Just "1". Having gotten the GET parameter, how do I convert it to an integer so I can do pagination? (need to add 1 for 'next' and subtract 1 for 'prev')
FWIW, when I try this using GHCi, it works fine:
Prelude Data.Maybe Data.Text> let one = singleton '1'
Prelude Data.Maybe Data.Text> let x = Just $ singleton '5'
Prelude Data.Maybe Data.Text> let y = fromMaybe one x
Prelude Data.Maybe Data.Text> y
"5"
Prelude Data.Maybe Data.Text> read $ Data.Text.unpack y ::Int -- This is probably unsafe because I cannot trust 'y' in my web app
5
Update:
I tired #Ankur's suggestion pageNumber <- (lookupGetParam "p" >>= return . (read :: String -> Int) . fromMaybe "1") and I get the following error:
Couldn't match expected type `String' with actual type `Text'
Expected type: Maybe Text -> String
Actual type: Maybe Text -> Text
In the return type of a call of `fromMaybe'
In the second argument of `(.)', namely `fromMaybe "1"'
Build failure, pausing...
If change the "1" to one (Data.Text.singleton '1'), I still get the exact same error message.
Thanks!
lookupGetParam returns ParamValue which is type ParamValue = String. So basically it is String rather than Text.
Try this:
pageNumber <- (lookupGetParam "p" >>= return . (read :: String -> Int) . fromMaybe "1")
UPDATE:
Actually the latest version of lookupGetParam is Text based so adding the OverloadedStrings language extension should get the job done:
Put this {-# LANGUAGE OverloadedStrings #-} at the start of the code file and use:
pageNumber <- (lookupGetParam "p" >>= return . (read :: String -> Int) . unpack . fromMaybe "1")

changing how Setup.hs is built

The encoding package uses HaXml in its build script (in Setup.hs). It happens to use bits of the interface that changed between HaXml-1.19 and HaXml-1.22. It would be nice if the encoding package were able to build with either version. I tried using the usual Cabal trick, namely, doing something like
{-# LANGUAGE CPP #-}
#if MIN_VERSION_HaXml(1,22,0)
-- HaXml-1.22 code
#else
-- HaXml-1.19 code
#endif
...but the magic defines can't exist before the package is configured, and this file is being built to make the configure step possible. What are my options? Is there a way to change the command that cabal-install calls to compile Setup.hs? Is there another mechanism for conditionally selecting code that sidesteps cabal?
The Data.Data interface is capable (just about!) of constructing and deconstructing values of a type that may or may not exist. Unfortunately, HaXml doesn't appear to have Data instances for its types, and you can't define one since you can't refer to the type that might or might not exist, so we have to resort to Template Haskell:
The following module exports qnameCompat:
{-# LANGUAGE TemplateHaskell #-}
module HaXmlCompat (qnameCompat) where
import Language.Haskell.TH
qnameCompat :: Q [Dec]
qnameCompat = do
mi <- maybeReify "N"
case mi of
Nothing -> sequence [
tySynD (mkName "QName") [] [t| String |],
valD [p| toQName |] (normalB [| id |]) [],
valD [p| fromQName |] (normalB [| Just |]) []]
Just (DataConI n _ _ _) -> do
s <- newName "s"
sequence [
valD [p| toQName |] (normalB (conE n)) [],
funD (mkName "fromQName") [
clause [conP n [varP s]] (normalB (appE [| Just |] (varE s))) [],
clause [ [p| _ |] ] (normalB [| Nothing |]) []]]
Just i -> fail $
"N exists, but isn't the sort of thing I expected: " ++ show i
maybeReify :: String -> Q (Maybe Info)
maybeReify = recover (return Nothing) . fmap Just . reify . mkName
When spliced at the top level using Template Haskell, qnameCompat will check if N exists. If it does, it produces the following code:
toQName = N
fromQName (N s) = Just s
fromQName _ = Nothing
If it doesn't, the following is produced:
type QName = String
toQName = id
fromQName = Just
Now you can create and deconstruct Elements, e.g. using the ViewPatterns extension:
myElt :: String -> Element i
myElt = Elem (toQName "elemName") [] []
eltName :: Element i -> String
eltName (Elem (fromQName -> Just n) _ _) = n
ViewPatterns is convenient, but not essential, of course: using ordinary pattern matching on the result of fromQName will work just as well.
(These ideas are what led me to develop the notcpp package, which includes maybeReify and some other useful utilities)
There don't seem to be very many knobs in cabal-install/Distribution/Client/SetupWrapper.hs controlling the compilation of Setup.hs, so your best bet may be to create a stub Setup.hs file which performs the version test, and then hands off to real Setup.hs once it has figured out what the version is.
Another trick is to make a compatibility shim library which your Setup script uses, which has the appropriate version tricks.
But maybe the real question to ask, is this: why is Setup.hs using external libraries?

Typechecking inside quasi-quotes in Template Haskell

I'm trying to become familiar with Template Haskell, and to my surprise the code below compiles under ghc (version 6.10.4).
main = do
let
y = [| "hello" + 1 |]
putStr ""
This suggest to me that there's no typechecking inside quasi-quotes. This is not what I'd have expected after reading the original paper on Template Haskell. Moreover the following program does not compile.
main = do
let
y = [| "hello" && True |]
putStr ""
What's going on here?
It looks like GHC does type check all quotations but assumes that all generated instance constraints can be satisfied.
In this code:
main = do
let
y = [| "hello" + 1 |]
putStr ""
The y bracket is typeable under the assumption that we have a Num String instance. Since GHC can't say for sure that you won't introduce such an instance before y is spliced in, it doesn't give a type error.
In this code:
main = do
let
y = [| "hello" && True |]
putStr ""
There is no way that y can ever be spliced in successfully, no matter what instance environment you set up.
This is just one example of how Template Haskell's typechecking mechanism is too lenient -- further examples are discussed in Simon PJ's blog post at http://hackage.haskell.org/trac/ghc/blog/Template%20Haskell%20Proposal, where he proposes a change to not type check any quotations at all.
Template Haskell has two main operations:
lifting: [| |]
splicing $( )
When you wrap something in the Oxford brackets, you delay its type checking (and evaluation), and instead build an AST fragment that will be type checked when it is spliced back in.
The AST that is built can be observed:
{-# LANGUAGE TemplateHaskell #-}
import Language.Haskell.TH
main = print =<< runQ [| "hello" + 1 |]
Running this program (or typing the bracket expression into GHCi), and we get a well-formed AST, but one that is not type correct if treated as a Haskell fragment:
InfixE (Just (LitE (StringL "hello"))) (VarE GHC.Num.+) (Just (LitE (IntegerL 1)))
Now when we try to actually splice it, type checking happens:
*Main> :t [| "hello" + 1 |]
[| "hello" + 1 |] :: Q Exp
*Main> $( [| "hello" + 1 |] )
<interactive>:1:4:
No instance for (Num [Char])
arising from the literal `1'
As we expect. So, yes, TH expressions are type checked, but at a late point, when spliced back into a program.

Resources