我试图弄清楚如何为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)
所以对于
StateT
的Foo
版本,我们也可以这样写:instance MonadBaseControl IO Foo where
type StM Foo a = (a, Int)