我正在尝试为归纳数据类型(描述具有数据类型和模式匹配的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实例“透明”。
有没有办法做类似的事情?
答案 0 :(得分:1)
可能的解决方案涉及
class Show1 f where
showsPrec1 :: Show a => Int -> f a -> ShowS
此类型类在prelude-extras
中的定义类似。不幸的是,Haskell的派生机制不会利用它,因此不能使用它。可能的替换可能是使用新的GHC.Generics
和DefaultSignatures
来创建(尽管不是“派生”的)至少是微不足道的实例。
instance (Show1 φ) => Show (Pat φ)
instance (Show1 φ) => Show (Expr φ)
现在困难的部分:实际使用GHC.Generics
。可以使用的是
instance (Show1 f, Show a) => Show (f a) where showsPrec = showsPrec1
然而,这有一个极端的缺点,即需要OverlappingInstances
(除了其他问题)。一种可能的解决方案是定义一个阴影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'
但是,假装所需的工作量并不是那么糟糕(非常糟糕),某些类型必须手动设置为从showsPrec'
转发到showsPrec
({{1 }},Char
等),并且所有使用的类型都必须是Bool
的实例,如果是Show'
或Generic
的实例,则会很简单,当然不方便。