我试图弄清楚如何为Foo类型实现MonadBaseControl实例,该类型是围绕StateT实例的新型包装。您可能会认为它会像this那样实现,但事实并非如此。我假设状态块在这里引起了问题,那么有没有办法删除它?

码:

newtype Foo a = Foo { unFoo :: StateT Int IO a }
                deriving (Monad, Applicative, Functor, MonadBase IO)

instance MonadBaseControl IO Foo where
   type StM Foo a = a
   liftBaseWith f = Foo $ liftBaseWith $ \q -> f (q . unFoo)
   restoreM = Foo . restoreM


错误:

 Couldn't match type ‘a’ with ‘(a, Int)’
 ‘a’ is a rigid type variable bound by
 the type signature for restoreM :: StM Foo a -> Foo a
  Expected type: a -> StateT Int IO a
 Actual type: StM (StateT Int IO) a -> StateT Int IO a
 Relevant bindings include
 restoreM :: StM Foo a -> Foo a
 In the second argument of ‘(.)’, namely ‘restoreM’
  In the expression: Foo . restoreM

最佳答案

为了避免UndecidableInstances,链接的答案扩展了一个类型族,出于人类可读性,它实际上是不应该的。即,他写道

instance MonadBaseControl IO Foo where
    type StM Foo a = a


相反,当人们可能会考虑写作

instance MonadBaseControl IO Foo where
    type StM Foo a = StM (ReaderT Int IO) a


以便更清楚地说明如何为给定的新型包装选择正确的右侧。通过类似的更改(和UndecidableInstances),您的代码可以正常工作。如果要避免使用UndecidableInstances,则可以在链接的答案中执行相同的扩展;询问ghci扩展应为以下示例:

> :kind! forall a. StM (StateT Int IO) a
forall a. StM (StateT Int IO) a :: *
= (a, Int)


所以对于StateTFoo版本,我们也可以这样写:

instance MonadBaseControl IO Foo where
    type StM Foo a = (a, Int)

08-07 03:54