Haskell 派生位置显示

Haskell 派生位置显示,haskell,metaprogramming,typeclass,template-haskell,deriving,Haskell,Metaprogramming,Typeclass,Template Haskell,Deriving,注意t5在中的显示方式 > newtype T = T { getT :: Int } deriving Show > T 5 T {getT = 5} 对于使用记录语法声明的类型,是否有方法派生Show的位置、非记录语法变量 (顺便说一句,T只是一个简单的例子来解释这个问题,我正在寻找用记录语法定义的任何类型的一般答案) 我对以下几种选择感到满意: 由图书馆提供的第三代it 基于的泛型派生(其中手动实例引用现有函数) 手动执行显示实例的简单方法/指南 还有什么我没想到的吗 我

注意
t5
在中的显示方式

> newtype T = T { getT :: Int } deriving Show
> T 5
T {getT = 5}
对于使用记录语法声明的类型,是否有方法派生Show的位置、非记录语法变量

(顺便说一句,T只是一个简单的例子来解释这个问题,我正在寻找用记录语法定义的任何类型的一般答案)

我对以下几种选择感到满意:

  • 由图书馆提供的第三代it
  • 基于
    的泛型派生(其中手动实例引用现有函数)
  • 手动执行
    显示
    实例的简单方法/指南
  • 还有什么我没想到的吗
我举了一个更复杂的例子:

实例。。。哪里
showsPrec p(FuncType i o)=
showParen(p>0)
((“FuncType”).showsPrec 1 i.(“).showsPrec 1 o)
我希望答案能够避免这个样板。

实现手工展示 实现
Show
的默认方式需要大量的样板文件。这一点由负责,将所需代码减少到最基本的部分:

instance Show ... where
  showPrec = flip (\(FuncType i o) -> showCon "FuncType" @| i @| o)
我认为这是最简单的解决办法。没有扩展,引擎盖下没有typeclass魔术。只是简单的函数式编程

(免责声明:我写了这篇文章中提到的两个库。)

使用GHC泛型
generic data
中有一个
Show
的通用实现:
gshowsPrec
()。但它将使用记录语法声明的类型显示为记录

重新执行 当然,一种方法是复制实现并删除对记录的特殊处理

{- 1. The usual boilerplate -}

class GShow p f where
  gPrecShows :: p (ShowsPrec a) -> f a -> PrecShowS

instance GShow p f => GShow p (M1 D d f) where
  gPrecShows p (M1 x) = gPrecShows p x

instance (GShow p f, GShow p g) => GShow p (f :+: g) where
  gPrecShows p (L1 x) = gPrecShows p x
  gPrecShows p (R1 y) = gPrecShows p y

{- 2. A simplified instance for (M1 C), that shows all constructors
      using positional syntax. The body mostly comes from the instance
      (GShowC p ('MetaCons s y 'False) f). -}

instance (Constructor c, GShowFields p f) => GShow p (M1 C c f) where
  gPrecShows p x = gPrecShowsC p (conName x) (conFixity x) x
   where
    gPrecShowsC p name fixity (M1 x)
      | Infix _ fy <- fixity, k1 : k2 : ks <- fields =
        foldl' showApp (showInfix name fy k1 k2) ks
      | otherwise = foldl' showApp (showCon cname) fields
      where
        cname = case fixity of
          Prefix -> name
          Infix _ _ -> "(" ++ name ++ ")"
        fields = gPrecShowsFields p x
基本上,从泛型表示中创建数据类型(在某种意义上,这与
generic
的作用相反)。我们可以使用
toData::a->Data(Rep a)p
将正常声明的类型映射到
Data
类型

最后,我们可以直接将
generic data
库中的
gshowsPrec
函数应用于
unsetIsRecord
的输出

instance Show T where
  showsPrec n = gshowsPrec n . unsetIsRecord . toData

UnsetIsRecord
在理想情况下应该位于
通用数据中,但由于还没有,这里有一个可能的实现:

type family UnsetIsRecord (f :: * -> *) :: * -> *
type instance UnsetIsRecord (M1 D m f) = M1 D m (UnsetIsRecord f)
type instance UnsetIsRecord (f :+: g) = UnsetIsRecord f :+: UnsetIsRecord g
type instance UnsetIsRecord (M1 C ('MetaCons s y _isRecord) f) = M1 C ('MetaCons s y 'False) f)

看起来最简单的解决方案是以非记录形式定义数据类型,如
newtype T=T Int派生(Show)
和单独编写记录字段访问器:
getT::T->Int;getT(tn)=n
。谢谢!最后写为
showsPrec p(FuncType i o)=(showCon“FuncType”@|i@|o)p
instance Show T where
  showsPrec n = gshowsPrec n . unsetIsRecord . toData
type family UnsetIsRecord (f :: * -> *) :: * -> *
type instance UnsetIsRecord (M1 D m f) = M1 D m (UnsetIsRecord f)
type instance UnsetIsRecord (f :+: g) = UnsetIsRecord f :+: UnsetIsRecord g
type instance UnsetIsRecord (M1 C ('MetaCons s y _isRecord) f) = M1 C ('MetaCons s y 'False) f)