How to call constructor from Template Haskell - haskell

I have function (let's call it mkSome) which constructs some data type with Template Haskell. It has typical signature Name -> Q [Dec].
Somewhere in its body I'm extracting constructors of another type with
pattern-matching:
case tyCons of
DataD ctx nm tyVars mbKind cs derivs -> ...
Type of those constructors cs instantiates some class like this:
class MyClass a where
specialValue :: a
So, I'm iterating over those cs but I want to skip one of them which is
equal to specialValue. Something like this:
[c | c <- cs, c /= specialValue]
Example:
data OtherData = A | B | C
instance MyClass OtherData where
specialValue = C
$(mkSome ''OtherData) -- mkSome must skip C-constructor!
How to do this in Template Haskell's (with Con type: c is it) ? Sure, I can't simply call constructor to compare created value with a specialValue because it's AST node, not real constructor

It depends entirely on how you want to use this expression. You can write e.g.
mkCons :: Name -> Q Exp
mkCons ty = do
TyConI (DataD ctx nm tyVars mbKind cs derivs) <- reify ty
let cons = ListE $ map (\(NormalC c _) -> ConE c) cs
[| [c | c <- $(pure cons), c /= specialValue] |]
which is a splice whose result is the constructors of ty except specialValue.
But if you want to manipulate the resulting list within the splice (e.g. generate some code for all constructors except specialValue) then the situation is much more complicated. You'll need to have a nested splice which manipulates the result of the above splice:
mkSome :: Name -> Q Exp
mkSome ty =
[| do e1 <- mapM lift $(mkCons ty)
let mkD (ConE n) = DataD [] (mkName $ "Foo" ++ nameBase n) [] Nothing [] [] -- example function
pure $ map mkD e1
|]
Note also the use of lift; the result of $(mkCons ty) has type [OtherData] (in this case) but lift gives you the TH AST corresponding to those constructors.
Also note that the functions above use the Eq, Lift and MyClass instances of the given type. Due to the stage restriction, you have to define these instances in a seperate module than the use of the splice. So the following won't work:
module A where
import TH (mkSome)
data OtherData = A | B | C deriving (Lift, Eq)
instance MyClass OtherData where
specialValue = C
$( $(mkSome ''OtherData) )
You must use it like so:
-- A.hs
module A where
data OtherData = A | B | C deriving (Lift, Eq)
instance MyClass OtherData where
specialValue = C
-- B.hs
module B where
import TH (mkSome)
import A
$( $(mkSome ''OtherData) )
The result:
mkSome ''OtherData
======>
do { e1_adJ0 <- mapM
lift [c_adJ2 | c_adJ2 <- [A, B, C], (c_adJ2 /= specialValue)];
let mkD_adJ1 (ConE n_adJ3)
= DataD
[] (mkName $ ("Foo" ++ (nameBase n_adJ3))) [] Nothing [] [];
(pure $ (map mkD_adJ1 e1_adJ0)) }
(do { e1_adJ0 <- mapM
lift [c_adJ2 | c_adJ2 <- [A, B, C], (c_adJ2 /= specialValue)];
let mkD_adJ1 (ConE n_adJ3)
= DataD
[] (mkName $ ("Foo" ++ (nameBase n_adJ3))) [] Nothing [] [];
(pure $ (map mkD_adJ1 e1_adJ0)) })
======>
data FooA
data FooB

Related

Traversing a Template Haskell AST

I have gethered that Haskell code in template-haskell is not represented as a single AST, but rather four cross-referencing types of Pat, Exp, Dec and Type. I have also found no traversal facilities within the library, or anywhere else for that matter.
I was initially looking for a unified representation of the four said types:
-- The single representation for Haskell code
data HCode = HE Exp | HD Dec | HP Pat | HT Type
-- And common functions in tree traversal such as:
children :: HCode -> [HCode]
children (HE (VarE _)) = []
children (HE (AppTypeE e t)) = [HE e, HT t]
children c = ...
-- Ultimately a transform function similar to:
-- (Not really arguing about this exact model of tree transformation)
preorder :: (HCode -> HCode) -> HCode -> HCode
preorder f h =
let h' = f h
in rebuildWithChildren h' . fmap (preorder f) . children $ h'
And now I have grown to believe writing it this way, aside from being time-consuming, is wasteful, since traversing/transforming ASTs is common practice, and I figured it might be best to ask what available solution there is among the practitioners.
Generally, I'm not sure that generic traversal of TH is likely to come up very often. (I'm struggling to imagine a useful transformation of a TH AST in a situation where you wouldn't just generate the TH already transformed that way.) I guess there are some situations where you want to perform queries or transformations of user-supplied quasiquotes without parsing the entire AST?
Anyway, if you can find a use for it, you can use SYB generics. For example, here's a query to extract literals from patterns and expressions from an arbitrary TH "thing":
{-# LANGUAGE TemplateHaskell #-}
import Data.Generics
import Language.Haskell.TH
getLiterals :: Data d => d -> [Lit]
getLiterals = everything (++) (mkQ [] litE `extQ` litP)
where litE (LitE l) = [l]
litE _ = []
litP (LitP l) = [l]
litP _ = []
main = do mydec <- runQ [d| foo 4 = "hello" |]
print mydec
print $ getLiterals mydec
myexp <- runQ [| '1' + "sixteen" |]
print myexp
print $ getLiterals myexp
Here's a transformation that commutes all infix operators in patterns, expressions, and types (example for InfixT not shown):
{-# LANGUAGE TemplateHaskell #-}
import Data.Generics
import Language.Haskell.TH
causeChaos :: Data d => d -> d
causeChaos = everywhere (mkT destroyExpressions `extT` manglePatterns `extT` bludgeonTypes)
where destroyExpressions (InfixE l x r) = InfixE r x l
destroyExpressions (UInfixE l x r) = UInfixE r x l
destroyExpressions e = e
manglePatterns (InfixP l x r) = InfixP r x l
manglePatterns (UInfixP l x r) = UInfixP r x l
manglePatterns e = e
bludgeonTypes (InfixT l x r) = InfixT r x l
bludgeonTypes (UInfixT l x r) = UInfixT r x l
bludgeonTypes e = e
main = do mydec <- runQ [d| append :: [a] -> [a] -> [a]
append (x:xs) ys = x : append xs ys
append [] ys = ys
|]
print mydec
print $ causeChaos mydec

Read of types sum

When I want to read string to type A I write read str::A. Consider, I want to have generic function which can read string to different types, so I want to write something like read str::A|||B|||C or something similar. The only thing I could think of is:
{-# LANGUAGE TypeOperators #-}
infixr 9 |||
data a ||| b = A a|B b deriving Show
-- OR THIS:
-- data a ||| b = N | A a (a ||| b) | B b (a ||| b) deriving (Data, Show)
instance (Read a, Read b) => Read (a ||| b) where
readPrec = parens $ do
a <- (A <$> readPrec) <|> (B <$> readPrec)
-- OR:
-- a <- (flip A N <$> readPrec) <|> (flip B N <$> readPrec)
return a
And if I want to read something:
> read "'a'"::Int|||Char|||String
B (A 'a')
But what to do with such weird type? I want to fold it to Int or to Char or to String... Or to something another but "atomic" (scalar/simple). Final goal is to read strings like "1,'a'" to list-like [D 1, D 'a']. And main constraint here is that structure is flexible, so string can be "1, 'a'" or "'a', 1" or "\"xxx\", 1, 2, 'a'". I know how to read something separated with delimiter, but this something should be passed as type, not as sum of types like C Char|I Int|S String|etc. Is it possible? Or no way to accomplish it without sum of types?
There’s no way to do this in general using read, because the same input string might parse correctly to more than one of the valid types. You could, however, do this with a function like Text.Read.readMaybe, which returns Nothing on ambiguous input. You might also return a tuple or list of the valid interpretations, or have a rule for which order to attempt to parse the types in, such as: attempt to parse each type in the order they were declared.
Here’s some example code, as proof of concept:
import Data.Maybe (catMaybes, fromJust, isJust, isNothing)
import qualified Text.Read
data AnyOf3 a b c = FirstOf3 a | SecondOf3 b | ThirdOf3 c
instance (Show a, Show b, Show c) => Show (AnyOf3 a b c) where
show (FirstOf3 x) = show x -- Can infer the type from the pattern guard.
show (SecondOf3 x) = show x
show (ThirdOf3 x) = show x
main :: IO ()
main =
(putStrLn . unwords . map show . catMaybes . map readDBS)
["True", "2", "\"foo\"", "bar"] >>
(putStrLn . unwords . map show . readIID) "100"
readMaybe' :: (Read a, Read b, Read c) => String -> Maybe (AnyOf3 a b c)
-- Based on the function from Text.Read
readMaybe' x | isJust a && isNothing b && isNothing c =
(Just . FirstOf3 . fromJust) a -- Can infer the type of a from this.
| isNothing a && isJust b && isNothing c =
(Just . SecondOf3 . fromJust) b -- Can infer the type of b from this.
| isNothing a && isNothing b && isJust c =
(Just . ThirdOf3 . fromJust) c -- Can infer the type of c from this.
| otherwise = Nothing
where a = Text.Read.readMaybe x
b = Text.Read.readMaybe x
c = Text.Read.readMaybe x
readDBS :: String -> Maybe (AnyOf3 Double Bool String)
readDBS = readMaybe'
readToList :: (Read a, Read b, Read c) => String -> [AnyOf3 a b c]
readToList x = repack FirstOf3 x ++ repack SecondOf3 x ++ repack ThirdOf3 x
where repack constructor y | isJust z = [(constructor . fromJust) z]
| otherwise = []
where z = Text.Read.readMaybe y
readIID :: String -> [AnyOf3 Int Integer Double]
readIID = readToList
The first output line echoes every input that parsed successfully, that is, the Boolean constant, the number and the quoted string, but not bar. The second output line echoes every possible interpretation of the input, that is, 100 as an Int, an Integer and a Double.
For something more complicated, you want to write a parser. Haskell has some very good libraries to build them out of combinators. You might look at one such as Parsec. But it’s still helpful to understand what goes on under the hood.

Arbitrary String generator in Haskell (Test.QuickCheck.Gen)

I am struggling on Real World Haskell Chapter 11 quickCheck generator implementation for a an algebraic data type.
Following the book implementation (which was published in 2008), I came up with the following:
-- file: ch11/Prettify2.hs
module Prettify2(
Doc(..)
) where
data Doc = Empty
| Char Char
| Text String
| Line
| Concat Doc Doc
| Union Doc Doc
deriving (Show, Eq)
And my Arbitrary implementation:
-- file: ch11/Arbitrary.hs
import System.Random
import Test.QuickCheck.Gen
import qualified Test.QuickCheck.Arbitrary
class Arbitrary a where
arbitrary :: Gen a
-- elements' :: [a] => Gen a {- Expected a constraint, but ‘[a]’ has kind ‘*’ -}
-- choose' :: Random a => (a, a) -> Gen a
-- oneof' :: [Gen a] -> a
data Ternary = Yes
| No
| Unknown
deriving(Eq, Show)
instance Arbitrary Ternary where
arbitrary = do
n <- choose (0, 2) :: Gen Int
return $ case n of
0 -> Yes
1 -> No
_ -> Unknown
instance (Arbitrary a, Arbitrary b) => Arbitrary (a, b) where
arbitrary = do
x <- arbitrary
y <- arbitrary
return (x, y)
instance Arbitrary Char where
arbitrary = elements (['A'..'Z'] ++ ['a' .. 'z'] ++ " ~!##$%^&*()")
I tried the two following implementation with no success:
import Prettify2
import Control.Monad( liftM, liftM2 )
instance Arbitrary Doc where
arbitrary = do
n <- choose (1,6) :: Gen Int
case n of
1 -> return Empty
2 -> do x <- arbitrary
return (Char x)
3 -> do x <- arbitrary
return (Text x)
4 -> return Line
5 -> do x <- arbitrary
y <- arbitrary
return (Concat x y)
6 -> do x <- arbitrary
y <- arbitrary
return (Union x y)
instance Arbitrary Doc where
arbitrary =
oneof [ return Empty
, liftM Char arbitrary
, liftM Text arbitrary
, return Line
, liftM2 Concat arbitrary arbitrary
, liftM2 Union arbitrary arbitrary ]
But it doesn't compile since No instance for (Arbitrary String)
I tried then to implement the instance for Arbitrary String in the following ways:
import qualified Test.QuickCheck.Arbitrary but it does not implement Arbitrary String neither
installing Test.RandomStrings hackage link
instance Arbitrary String where
arbitrary = do
n <- choose (8, 16) :: Gen Int
return $ randomWord randomASCII n :: Gen String
With the following backtrace:
$ ghci
GHCi, version 7.10.3: http://www.haskell.org/ghc/ :? for help
Prelude> :l Arbitrary.hs
[1 of 2] Compiling Prettify2 ( Prettify2.hs, interpreted )
[2 of 2] Compiling Main ( Arbitrary.hs, interpreted )
Arbitrary.hs:76:9:
The last statement in a 'do' block must be an expression
return <- randomWord randomASCII n :: Gen String
Failed, modules loaded: Prettify2
Would you have any good suggestion about how to implement this particular generator and - more in general - how to proceed in these cases?
Thank you in advance
Don't define a new Arbitrary type class, import Test.QuickCheck instead. It defines most of these instances for you. Also be careful about the version of quickcheck, RWH assumes version 1.
The resulting full implementation will be:
-- file: ch11/Arbitrary.hs
import Test.QuickCheck
import Prettify2
import Control.Monad( liftM, liftM2 )
data Ternary = Yes
| No
| Unknown
deriving(Eq, Show)
instance Arbitrary Ternary where
arbitrary = do
n <- choose (0, 2) :: Gen Int
return $ case n of
0 -> Yes
1 -> No
_ -> Unknown
instance Arbitrary Doc where
arbitrary =
oneof [ return Empty
, liftM Char arbitrary
, liftM Text arbitrary
, return Line
, liftM2 Concat arbitrary arbitrary
, liftM2 Union arbitrary arbitrary ]

Define a Recursive Function in Template Haskell

I want to implement a generic recursion operator for (at first simple) ADTs.
(Simple means that only with constructors whose argument types are the defined one.) The general idea is to be able to use something as simple as $(recop ''Alg).
It is easy to write down the recursion operator manually for a given type.
data D = E | C D D
recD :: t -> ((D, t) -> (D, t) -> t) -> D -> t
recD rE rC = let r = recD rE rC in \case
E -> rE
C pC0 pC1 -> rC (pC0, r pC0) (pC1, r pC1)
I wanted to use templates for that. My problem is the recursive call e.g. r pC0. I got it working without the recursive call.
newNames :: String -> Int -> Q [Name]
newNames stem n = sequence [ newName (stem ++ show i) | i <- [1::Int .. n] ]
match' :: PatQ -> ExpQ -> MatchQ
match' pat exp = match pat (normalB exp) []
recop :: Name -> ExpQ
recop name = do
TyConI (DataD _ algName [] {-_-} ctors _) <- reify name
let ctorNames = [ ctorName | NormalC ctorName _ <- ctors ] :: [Name]
let ctorTypes = [ [ typ | (_, typ) <- bts ] | NormalC _ bts <- ctors ]
rs <- newNames ("r" ++ nameBase algName) (length ctorNames)
pss <- sequence [ newNames ("p" ++ nameBase algName ++ nameBase ctorName) (length ctorTypes) | (ctorName, ctorTypes) <- zip ctorNames ctorTypes ]
let pats = zipWith conP ctorNames (map varP <$> pss) :: [PatQ]
let prs = zipWith (\p r -> tupE [varE p, r]) ps "recursive calls"
lamE (varP <$> rs) $ lamCaseE [ match' pat $ foldl appE (varE r) prs | (r, pat, ps) <- zip3 rs pats pss ]
I don't know how to get the hole of "recursive calls" filled. I have no idea and suspect that it's not easily doable.
You do it exactly the same way you've done it in your concrete code; you generate let r = .. in .. and refer to that r to construct the recursive calls. Right now, you are just constructing the \case { .. } portion. Keep in mind you can rewrite recD as
recD =
let
recD_ = \rE rC ->
let r = recD_ rE rC
in ...
in recD_
Credit goes to user2407038 who answered the question in a comment.
The general pattern is to use an additional let construct:
recursive = let recursive_ = expression in recursive_
so you can refer to recursive_ in expression.

Custom deriving(Read,Show) for enum type

Let's say I have this enumeration type:
data TVShow = BobsBurgers | MrRobot | BatmanTAS
and I want to define instances for Read and Show with the following behavior:
show BobsBurgers = "Bob's Burgers"
show MrRobot = "Mr. Robot"
show BatmanTAS = "Batman: The Animated Series"
read "Bob's Burgers" = BobsBurgers
read "Mr. Robot" = MrRobot
read "Batman: The Animated Series" = BatmanTAS
There is lots of repetition in these definitions, and so I'd like to associate each type constructor with a string and then generate Show and Read automatically from those associations. Is such a thing possible?
The paper Invertible Syntax Descriptions: Unifying Parsing and Pretty Printing describes one particularly idiomatic solution. Your example looks like this, using the invertible-syntax package based on that paper:
import Prelude hiding (Applicative(..), print)
import Data.Maybe (fromJust)
import Text.Syntax
import Text.Syntax.Parser.Naive
import Text.Syntax.Printer.Naive
data TVShow = BobsBurgers | MrRobot | BatmanTAS deriving (Eq, Ord)
tvShow :: Syntax f => f TVShow
tvShow = pure BobsBurgers <* text "Bob's Burgers"
<|> pure MrRobot <* text "Mr. Robot"
<|> pure BatmanTAS <* text "Batman: The Animated Series"
runParser (Parser p) = p
instance Read TVShow where readsPrec _ = runParser tvShow
instance Show TVShow where show = fromJust . print tvShow
This is designed to be extensible to types more exciting than simple enumerations, as well.
Aha! I found some pre-existing code written by Simon Nicholls. This template haskell can be used to achieve what I wanted:
genData :: Name -> [Name] -> DecQ
genData name keys = dataD (cxt []) name [] cons [''Eq, ''Enum, ''Bounded]
where cons = map (\n -> normalC n []) keys
genShow :: Name -> [(Name, String)] -> DecQ
genShow name pairs =
instanceD (cxt [])
(appT (conT ''Show) (conT name))
[funD (mkName "show") $ map genClause pairs]
where
genClause (k, v) = clause [(conP k [])] (normalB [|v|]) []
mkEnum :: String -> [(String, String)] -> Q [Dec]
mkEnum name' pairs' =
do
ddec <- genData name (map fst pairs)
sdec <- genShow name pairs
rdec <- [d|instance Read $(conT name) where
readsPrec _ value =
case Map.lookup value m of
Just val -> [(val, [])]
Nothing -> []
where
m = Map.fromList $ map (show &&& id) [minBound..maxBound]|]
return $ ddec : sdec : rdec
where name = mkName name'
pairs = map (\(k, v) -> (mkName k, v)) pairs'
Usage:
$(mkEnum "TVShow"
[ ("BobsBurgers", "Bob's Burgers")
, ("MrRobot", "Mr. Robot")
, ("BatmanTAS", "Batman: The Animated Series")
])
(The QuasiQuotes weren't working, so I'll have to investigate that)
I came to this:
data FeedbackType
= Abuse
| AuthFailure
| Fraud
| NotSpam
| Virus
| Other
deriving (Eq)
instance Show FeedbackType where
show Abuse = "abuse"
show AuthFailure = "auth-failure"
show Fraud = "fraud"
show NotSpam = "not-spam"
show Virus = "virus"
show Other = "other"
instance Read FeedbackType where
readsPrec _ s
| s == show Abuse = [(Abuse, "")]
| s == show AuthFailure = [(AuthFailure, "")]
| s == show Fraud = [(Fraud, "")]
| s == show NotSpam = [(NotSpam, "")]
| s == show Virus = [(Virus, "")]
| s == show Other = [(Other, "")]
| otherwise = []

Resources