在 haskell IO 类型中有 Monoid 的实例:
instance Monoid a => Monoid (IO a) where
mempty = pure empty
如果我有三个 Action 共享一些状态,并通过副作用改变彼此的行为,从 IO 类型的角度来看,这可能会导致违反关联律:
a1:: IO String
a2:: IO String
a3:: IO String
(a1 mappend
a2) mappend
a3/= a1 mappend
(a2 mappend
a3)
例如,如果 a1、a2、a3 以字符串形式请求当前时间,或者 IO 包含一些计算请求编号的 DB。这意味着它可以:
(a1 `mappend` a2) `mappend` a3 == "1"++"2"++"3"
a1 `mappend` (a2 `mappend` a3) == "3"++"1"++"2"
编辑:
我想我不应该给出一个数据库的例子,它很困惑, 更优选的例子:
a1 = show <$> getUnixTime
a2 = show <$> getUnixTime
a3 = show <$> getUnixTime
l = (a1 `mappend` a2) `mappend` a3
r = a1 `mappend` (a2 `mappend` a3)
liftA2 (==) l r
**False**
那么,如果 IO 类型可以打破结合律,为什么它是幺半群呢?还是我遗漏了什么?
最佳答案
a1 `mappend` (a2 `mappend` a3)
未按 a2
顺序运行, a3
和 a1
.例如,与 Python 这样的命令式语言相比,Haskell 中的 IO a
不是计算的某些结果,它是产生a
值的配方 .你实际上可以看到 IO
更像是 Python 中的延续,您传递一个最终可以调用它的函数,但您不直接调用它。
mappend
函数实现为 liftA2 (<>)
对于 Semigroup a => Semigroup (IO a)
例如,正如我们在 source code 中看到的那样:
instance Semigroup a => Semigroup (IO a) where (<>) = liftA2 (<>)
因此这意味着 mappend
实现为:
mappendIO :: Semigroup a => IO a -> IO a -> IO a
mappendIO f g = do
x <- <b>f</b>
y <- <b>g</b>
pure (x <> y)
所以它运行f
之前g
.
如果我们现在看(a1 `mappend` a2) `mappend` a3
,我们看到:
(a1 `mappend` a2) `mappend` a3 = do
x <- do
x1 <- a1
x2 <- a2
pure (x1 <> x2)
y <- a3
pure (x <> y)
相当于:
(a1 `mappend` a2) `mappend` a3 = do
x1 <- a1
x2 <- a2
x3 <- a3
pure ((x1 <> x2) <> x3)
如果我们再看看 a1 `mappend` (a2 `mappend` a3)
那么这相当于:
a1 `mappend` (a2 `mappend` a3) = do
x <- a1
y <- do
y1 <- a2
y2 <- a2
pure (y1 <> y2)
pure (x <> y)
相当于:
a1 `mappend` (a2 `mappend` a3) = do
x1 <- a1
x2 <- a2
x3 <- a2
pure (x1 <> (x2 <> x3))
自 x1 <> (x2 <> x3)
相当于(x1 <> x2) <> x3
,这将因此在两个项目中返回相同的结果。
至于你的测试:
l = (a1 `mappend` a2) `mappend` a3
r = a1 `mappend` (a2 `mappend` a3)
liftA2 (==) l r
False
请注意 liftA2 (==)
again 将定义一个序列,所以这意味着你的liftA2 (==) l r
定义为:
liftA2 (==) l r = do
x1 <- a1
x2 <- a2
x3 <- a3
y1 <- a1
y2 <- a2
y3 <- a3
pure ((x1 <> x2) <> x3) == (y1 <> (y2 <> y3))
您因此运行 r
之后 l
.
如果您使用 State
monad,你可以更清楚地知道会发生什么,并验证规则是否被应用。您需要重置 l
之间的状态和 r
然而。
关于haskell,IO Monoid 关联性被打破了吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64948364/