我正在尝试为归纳数据类型创建一个灵活的表示(它描述了具有数据类型和模式匹配的 lambda 演算的一个版本)。这里的灵活性应该意味着可以很容易地在节点上添加额外的数据(自由 comonad 风格)或进行替换(自由 monad 风格)。所以这就是我所拥有的:

type Tie f φ = φ (f φ)

type Id = String
type Var = Id
type Con = Id

data Pat φ = PVar Var
           | PCon Con [Tie Pat φ]
           | PWildcard

data Expr φ = EVar Var
            | ECon Con
            | EApp (Tie Expr φ)
            | ELam (Tie Pat φ) (Tie Expr φ)

当我想派生 Show 实例时,麻烦就来了。当然,我可以这样做:
{-# LANGUAGE FlexibleContexts, UndecidableInstances #-}
{-# LANGUAGE StandaloneDeriving #-}

deriving instance (Show (φ (Pat φ))) => Show (Pat φ)
deriving instance (Show (φ (Expr φ)), Show (φ (Pat φ))) => Show (Expr φ)

但是当归纳结构变得更加复杂时,手写上下文变得笨拙。

理想情况下,我希望能够写出类似的东西
{-# LANGUAGE RankNTypes #-}
deriving instance (forall a. Show (φ a)) => Show (Expr φ)

表示仿函数 φ 在某种意义上应该对 Show 实例“透明”。

有没有办法做这样的事情?

最佳答案

一个可能的解决方案将涉及

class Show1 f where
  showsPrec1 :: Show a => Int -> f a -> ShowS

这个类型类在 prelude-extras 中也有类似的定义。不幸的是,Haskell 的派生机制不会利用它,因此无法使用。一个可能的替代方法可能是使用新的 GHC.GenericsDefaultSignatures 来创建,尽管不是“派生”的,但至少是微不足道的实例。
instance (Show1 φ) => Show (Pat φ)
instance (Show1 φ) => Show (Expr φ)

现在是困难的部分:实际使用 GHC.Generics 。可以使用的是
instance (Show1 f, Show a) => Show (f a) where showsPrec = showsPrec1

但是,这具有需要 OverlappingInstances 的极端缺点(除了其他问题)。一种可能的解决方案是定义一个 shadows Show 类。
class Show' a where showsPrec' ...
instance (Show1 f, Show' a) => Show' (f a) where ...

如果所有 GHC.Generics 机制都到位( GShowGShow1 、默认签名等),那么最终结果看起来不会太糟糕。
instance (Show1 φ) => Show' (Pat φ)
instance (Show1 φ) => Show (Pat φ) where showsPrec = showsPrec'
instance (Show1 φ) => Show' (Expr φ)
instance (Show1 φ) => Show (Expr φ) where showsPrec = showsPrec'

然而,假设所需的工作量并没有那么糟糕(它非常糟糕),必须手动设置某些类型以从 showsPrec' 转发到 showsPrec( CharBool 等),并且所有使用的类型都将具有要成为 Show' 的实例,虽然如果 GenericGeneric1 的实例微不足道,但肯定不方便。

10-07 16:16
查看更多