如果我定义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)
为什么不能呢?
我想我们可以通过遵循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
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 将接受这两个实例,现在我们有责任避免循环。