提升修复单子变换器堆栈的*内部*

3
假设我有一个包含在 StateT MyState 中的 IO Int,那么我就会得到一个值为 State MyState Int 的结果,我想在堆叠的单子中使用它。如何在这种内部情况下将它提升? 如果我得到了一些与内部兼容只需要提升到外部单子的东西,我已经知道要使用 liftliftIO,但现在我有了相反的问题: 值已经在外部单子中,但不在内部单子中。
例如:
checkSame :: State MyState a -> IO a -> StateT MyState IO Bool
checkSame sim real = do
  rres <- liftIO real
  sres <- ??? sim 
  return $ rres == sres

我是否需要手动获取状态,并通过runState进行封装?还是有一种通用的方法可以实现这一点?

顺便说一下,sim参数是一组与IO无关的有状态函数,因此如果可以避免,我不太愿意让它们全部返回StateT MyState IO a


6
你在想要保持类型的精确性方面是正确的。你可以使用mapStateT(return. runIdentity)或者更通用的方式hoist(return. runIdentity)。参见https://dev59.com/fWIj5IYBdhLWcg3wnmXA。 - danidiaz
谢谢!那个有效。 - Adrian May
请注意,mmorph现在具有generalize函数:generalize = return . runIdentity - Gabriella Gonzalez
1个回答

7

您有两个选项:

  1. Find a monad morphism. This is often a matter of finding the right library; in this case hoist and generalize together should get you where you need to go.
  2. Make your State action more polymorphic. This is the commonly used one, and the recommended one; it amounts to pre-applying the morphism from part 1, but has a lot of machinery put in place already in the mtl library to make it easy. The idea here is that if you write your State action just in terms of get, put, and modify, then instead of the type State s a, you can give it the type:

    MonadState s m => m a
    

    Then later, at the call site, you can choose whatever monad is appropriate for this, including both State s a and StateT s IO a. Moreover, since it specializes to the type State s a, you can be sure it doesn't do any IO or anything like that that State s a itself couldn't do, so you get the same behavioral guarantees.


我在下面作为答案进行了评论,以便容纳代码。 - Adrian May
关于第二个选项:需要注意的是,不同的 MonadState 实例具有不同的严格性属性。 - Jeremy List

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接