4

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

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 实例是“透明的”。

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

4

1 回答 1

1

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

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机器都到位(GShow, GShow1,默认签名等),那么最终结果看起来不会太糟糕。

instance (Show1 φ) => Show' (Pat φ)
instance (Show1 φ) => Show (Pat φ) where showsPrec = showsPrec'
instance (Show1 φ) => Show' (Expr φ)
instance (Show1 φ) => Show (Expr φ) where showsPrec = showsPrec'

但是,假装所需的工作量并没有那么糟糕(这很糟糕),某些类型必须手动设置为 forward from showsPrec'to showsPrec( Char,Bool等),并且所有使用的类型都必须成为实例of ,如果是orShow'的实例,虽然微不足道,但肯定不方便。GenericGeneric1

于 2013-04-09T20:57:52.593 回答