为什么不能为MaybeT派生Show实例?

rob*_*oby 6 haskell

如果我定义monad变换器类型Identity,它就能够派生Show实例.

newtype IdentityT f a =
  IdentityT { runIdentityT :: f a }
  deriving (Show)
Run Code Online (Sandbox Code Playgroud)

将衍生出来

instance Show (f a) => Show (IdentityT f a)
Run Code Online (Sandbox Code Playgroud)

但是,如果我定义monad变压器类型 Maybe

newtype MaybeT m a =
  MaybeT { runMaybeT :: m (Maybe a) }
  deriving (Show)
Run Code Online (Sandbox Code Playgroud)

我收到了错误

• No instance for (Show (m (Maybe a)))
        arising from the first field of ‘MaybeT’ (type ‘m (Maybe a)’)
Run Code Online (Sandbox Code Playgroud)

既然Maybe a有一个Show实例,我希望它可以工作和派生

instance Show (m (Maybe a)) => Show (MaybeT m a)
Run Code Online (Sandbox Code Playgroud)

为什么不能呢?

jbe*_*man 5

我想我们可以通过遵循GHC的建议(我正在使用8.2.1)看到这个问题,直到我们走到尽头:

Prelude> :{
Prelude| newtype MaybeT m a =
Prelude|   MaybeT { runMaybeT :: m (Maybe a) }
Prelude|   deriving (Show)
Prelude| :}

<interactive>:12:13: error:
    • No instance for (Show (m (Maybe a)))
        arising from the first field of ‘MaybeT’ (type ‘m (Maybe a)’)
      Possible fix:
        use a standalone 'deriving instance' declaration,
          so you can specify the instance context yourself
    • When deriving the instance for (Show (MaybeT m a))
Prelude> :set -XStandaloneDeriving 
Prelude> deriving instance Show (m (Maybe a)) =>  Show (MaybeT m a)

<interactive>:17:19: error:
    • The constraint ‘Show (m (Maybe a))’
        is no smaller than the instance head
      (Use UndecidableInstances to permit this)
    • In the stand-alone deriving instance for
        ‘Show (m (Maybe a)) => Show (MaybeT m a)’
Run Code Online (Sandbox Code Playgroud)

好吧,因为这种约束可能是不允许的,所以Show不可能导出,MaybeT因为这是类型检查器无法证明终止的那种约束.您可以在此答案中详细了解"不小于实例头"的含义:https://stackoverflow.com/a/17866970/176841


chi*_*chi 5

GHC 使用启发式方法来确定实例是否保证搜索终止。这里的终止意味着,当搜索实例时,我们不会永远循环。具体来说,这是必须禁止的

instance Show a => Show a where ...
Run Code Online (Sandbox Code Playgroud)

还有这个

instance Show [a] => Show a where ...
Run Code Online (Sandbox Code Playgroud)

GHC 粗略地要求实例上下文中的约束( 之前的部分=>)必须比头部中的约束( 之后=>)“小”。所以,它接受这个:

instance Show a => Show [a] where ...
Run Code Online (Sandbox Code Playgroud)

因为a包含一个小于 的类型构造函数[a]

它还接受这一点:

instance Show (f a) => Show (IdentityT f a) where ...
Run Code Online (Sandbox Code Playgroud)

因为f a包含一个小于 的类型构造函数IdentityT f a

然而,

instance Show (f (Maybe a)) => Show (MaybeT f a) where ...
Run Code Online (Sandbox Code Playgroud)

使用相同数量的构造函数!因此,不能确定它不会导致环路。毕竟以后我们可能会见面

instance Show (MaybeT f a)) => Show (f (Maybe a)) where ...
Run Code Online (Sandbox Code Playgroud)

很明显,必须拒绝这两种情况中的至少一种才能保证终止。GHC 选择拒绝他们两个。

UndecidableInstances放宽了这一限制。GHC 将接受这两个实例,现在我们有责任避免循环。