在模板中执行类型相等 haskell

Performing type equality in 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 等特定类型的相等性?我在 TH 文档中看到 TypeQ。它构建类型表达式。但是,我找不到任何关于如何构建特定类型(如 StringTextInt 以便我可以将其用于相等比较的文档?将感谢有关如何执行此操作的指示,尤其是如何获取特定类型的 AST。

这个问题的原因是给定的记录构造函数,我们想将每个字段转换为Text。但是,对于 StringText 类型,showpack 应该有不同的调用方式。因此,如果类型是Text(不转换)或String(只调用pack,不调用show)或其他类型(调用pack . show 假设存在 Show 个实例)。

根据评论中 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。但是,StringText 上的 运行 pack . show 会出现问题,因为它会在其上添加另一层双引号(无论如何都没有意义) .因此,我们需要对 TextStringChar 类型的 Show 进行特殊处理。因此,解决方案是编写一个函数 toText :: a -> Text 并在代码生成器中使用它,如下所示:

> 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

作为另一个答案的后续,这里有一些东西可以让你在没有任何重叠实例的情况下编写 ToText。它使用了我最喜欢的新技巧——将封闭类型族作为 "choice" 机制与典型类型 类 混合使用数据种类(注意:甚至不使用函数依赖性,更不用说重叠实例)来合成实际代码:

{-# 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 中也能正常工作。