在模板haskell中执行类型相等

时间:2016-03-22 17:26:03

标签: haskell template-haskell

我在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.StringData.Text.Internal.Text等特定类型的相等性?我在TypeQ文档中看到了TH。它构建类型表达式。但是,我找不到有关如何构建特定类型的文档,例如StringTextInt,以便我可以使用它进行相等性比较?将会了解如何执行此操作的指针,尤其是如何获取特定类型的AST。

这个问题的原因是给定记录构造函数,我们希望将每个字段转换为Text。但是,对于showpack类型,应StringText的调用方式不同。因此,如果类型为Text(无转化)或String(仅调用pack,请勿调用show)或其他内容(调用),则需要生成不同的拼接假设pack . show实例存在,Show

2 个答案:

答案 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 . showString上运行Text会有问题,因为它会在其上添加另一层双引号(并且无论如何都没有意义)。因此,我们需要对ShowTextString类型的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
相关问题