为什么 StateT 的这个 Applicative 实例有效?

Why is this Applicative instance for StateT working?

我是 Haskell(和 CS)初学者。我正在努力完成 haskellbook。我正在为 StateT 实现 Applicative 实例,其中 StateT 定义为:

newtype StateT s m a = StateT { runState :: s -> m (a, s) }

书中提到,要为 StateT s m 创建一个 Applicative 实例,我们需要 m 上的 Monad 约束,而不是 Applicative正如人们所期望的那样。在阅读书中引用的 SO 答案的公认 answer 时,我也得出了相同的结论。 但是,为了更好地理解,我尝试创建一个 Applicative 实例,并在 m 上使用 Applicative 约束,并成功编译。我还在几个例子中尝试过它,它似乎工作正常。有人可以解释一下,这里有什么问题吗?

instance (Applicative m) => Applicative (StateT s m) where
  pure a = StateT $ \s -> pure $ (a, s)
  (<*>) :: (StateT s m (a -> b)) -> (StateT s m a) -> (StateT s m b)
  (StateT smf) <*> (StateT sma) = StateT $ \s -> (f) <$> (smf s) <*> (sma s)
                                  where
                                    f :: (a -> b, s) -> (a, s) -> (b, s)
                                    f (ff, s) = \(a, s) -> (ff a,s)

*StateT> s1 = StateT (\s -> return (4, s))
*StateT> s2 = map (+) s1
*StateT> s3 = StateT (\s -> return (20, s))
*StateT> runState (s2 <*> s3) * 10
(24,10)
*StateT>

EDIT :@Koterpillar 建议我尝试使用状态也被修改的示例进行尝试。我尝试使用 this example. Also, hereMonad 约束版本,我认为它的行为也不尽如人意。我认为问题在于各州没有以某种方式联系在一起。如果有人能阐明这个话题,我将不胜感激。

这是 <*> 对于 StateT 应该做的:

  1. 运行 smf 与初始状态
  2. 运行 sma 状态来自 smf
  3. Return这个最终状态

这就是您的代码所做的:

  1. 运行 smf 与初始状态
  2. 运行sma初始状态
  3. Return这个最终状态

换句话说,这个bug就是丢弃smf引起的状态变化

我们可以用修改 smf 中的状态的代码来演示这个问题。例如:

s1 = StateT $ \s -> return (const (), s + 1)
s2 = StateT $ \s -> return ((), s)

然后 runState (s1 <*> s2) 0 将 return ((), 1) 与标准实施,但 ((), 0) 与你的。