我在Template Haskell
中有一个函数,它提取记录构造函数总和的类型信息,如下所示:
listFields :: Name -> Q ([[(String,Name,Type)]])
listFields name = do
TyConI (DataD _ _ _ cons _) <- reify name
let showClause (RecC conName fields) = (map (\(x,_,t) -> (nameBase $ x,x,t)) fields)
return $ map showClause cons
根据字段的类型,如何比较该类型与GHC.Base.String
或Data.Text.Internal.Text
等特定类型的相等性?我在TypeQ
文档中看到了TH
。它构建类型表达式。但是,我找不到有关如何构建特定类型的文档,例如String
或Text
或Int
,以便我可以使用它进行相等性比较?将会了解如何执行此操作的指针,尤其是如何获取特定类型的AST。
这个问题的原因是给定记录构造函数,我们希望将每个字段转换为Text
。但是,对于show
和pack
类型,应String
和Text
的调用方式不同。因此,如果类型为Text
(无转化)或String
(仅调用pack
,请勿调用show
)或其他内容(调用),则需要生成不同的拼接假设pack . show
实例存在,Show
。
答案 0 :(得分:3)
作为另一个答案的后续内容,这里有一些东西可以让你在没有任何重叠实例的情况下编写ToText。它使用我最喜欢的技巧 - 将封闭式家庭混合在数据线上作为选择&#34;具有典型类型类的机制(注意:甚至不使用函数依赖,更不用重叠实例)来合成实际代码:
{-# LANGUAGE TypeFamilies, DataKinds, MultiParamTypeClasses, FlexibleInstances, ScopedTypeVariables, FlexibleContexts #-}
import Data.List
import Data.Text (unpack, pack, Text)
import Data.Proxy
data ToTextMethod = TTMChar | TTMString | TTMText | TTMShow
type family ToTextHow a where
ToTextHow Char = TTMChar
ToTextHow String = TTMString
ToTextHow Text = TTMText
ToTextHow a = TTMShow
class ToTextC a b where
toTextC :: a -> b -> Text
instance Show a => ToTextC a (Proxy TTMShow) where
toTextC a _ = pack (show a)
instance ToTextC Char (Proxy TTMChar) where
toTextC c _ = pack [c]
instance ToTextC String (Proxy TTMString) where
toTextC s _ = pack s
instance ToTextC Text (Proxy TTMText) where
toTextC t _ = t
toText :: forall a. (Show a, ToTextC a (Proxy (ToTextHow a))) => a -> Text
toText x = toTextC x (Proxy :: Proxy (ToTextHow a))
名称可能会使用一些工作,将参数翻转到toTextC
可能会很好,但这一切都适用于ghc 7.8.3。
答案 1 :(得分:1)
根据评论中jozefg
的建议,我通过使用带有类型签名a -> Text
的重载函数解决了这个问题。保持开放几天,看看是否有人有更好的建议。
这是我原来的TH
拼接(ghci
输出):
> runQ [| pack . show $ 1 ::Int|]
SigE (InfixE (Just (InfixE (Just (VarE Data.Text.pack)) (VarE GHC.Base..)
(Just (VarE GHC.Show.show)))) (VarE GHC.Base.$) (Just (LitE (IntegerL 1))))
(ConT GHC.Types.Int)
Int
转换为Text
。但是,在pack . show
或String
上运行Text
会有问题,因为它会在其上添加另一层双引号(并且无论如何都没有意义)。因此,我们需要对Show
,Text
和String
类型的Char
进行特殊处理。因此,解决方案是编写函数toText :: a -> Text
并在codegen中使用它,如下所示:
> runQ [| toText $ 1 ::Int|]
SigE (InfixE (Just (VarE ToText.toText)) (VarE GHC.Base.$) (Just (LitE (IntegerL 1)))) (ConT GHC.Types.Int)
现在,代码生成由toText
本身处理,具体取决于类型。这是我在ghc 7.10.3
中编写它的方式 - 它采用默认代码(来自第一个拼接,如上所示),并为某些类型重载它 - 现在,我们在TH
codegen位置有正确的代码在编译时:
{-# LANGUAGE FlexibleInstances #-}
module ToText
where
import Data.List
import Data.Text (unpack, pack, Text)
class ToText a where
toText :: (Show a) => a -> Text
instance {-# OVERLAPPING #-} ToText a where
toText = pack . show
instance {-# OVERLAPPING #-} ToText Char where
toText c = pack [c]
instance {-# OVERLAPPING #-} ToText String where
toText = pack
instance {-# OVERLAPPING #-} ToText Text where
toText = id