在实例上下文中需要泛型实例(对于更高级的类型构造函数)

时间:2012-08-09 03:20:33

标签: haskell typeclass higher-rank-types

我正在尝试为归纳数据类型(描述具有数据类型和模式匹配的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实例“透明”。

有没有办法做类似的事情?

1 个答案:

答案 0 :(得分: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(除了其他问题)。一种可能的解决方案是定义一个阴影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({{1 }},Char等),并且所有使用的类型都必须是Bool的实例,如果是Show'Generic的实例,则会很简单,当然不方便。