monad函数dist和join in Haskell有什么关系?
What is the relationship between monad functions dist and join in Haskell?
我正在做函数式编程课程的作业之一,发现在 Haskell 中理解 monad 有一些问题。
所以,我们得到了一个类型:
data Annotated e a = a :# e
infix 0 :#
任务是用给定的类型签名实现一些函数,我做到了。他们通过了所需的测试(分别):
mapAnnotated :: (a -> b) -> (Annotated e a -> Annotated e b)
mapAnnotated f (x :# w) = f x :# w
joinAnnotated :: Semigroup e => Annotated e (Annotated e a) -> Annotated e a
joinAnnotated ((b :# m) :# n) = b :# m <> n
distAnnotated :: Semigroup e => (Annotated e a, Annotated e b) -> Annotated e (a, b)
distAnnotated (x :# m, y :# n) = (x, y) :# m <> n
然而,我们还被要求满足以下等式:
distAnnotated (p, q) = joinAnnotated (mapAnnotated (\a -> mapAnnotated (\b -> (a, b)) q) p)
我不太了解这么多功能应用程序,所以对于其他具有类似任务的类型,我只是做了看起来“自然”的事情并且它有效,但在这里它没有而且我看不到为什么,因为我什至没有看到其他方法来实现这些功能。我错过了什么?
让我们从麻烦的方程式开始,系统地替换定义,由内而外计算:
-- Given
mapAnnotated f (x :# w) = f x :# w
joinAnnotated ((b :# m) :# n) = b :# m <> n
distAnnotated (x :# m, y :# n) = (x, y) :# m <> n
p = x :# m
q = y :# n
-- Goal
distAnnotated (p, q) = joinAnnotated (mapAnnotated (\a -> mapAnnotated (\b -> (a, b)) q) p)
-- Right-hand side
joinAnnotated (mapAnnotated (\a -> mapAnnotated (\b -> (a, b)) q) p)
joinAnnotated (mapAnnotated (\a -> mapAnnotated (\b -> (a, b)) (y :# n)) (x :# m))
joinAnnotated (mapAnnotated (\a -> (\b -> (a, b)) y :# n) (x :# m))
joinAnnotated (mapAnnotated (\a -> (a, y) :# n) (x :# m))
joinAnnotated (mapAnnotated (\a -> (a, y) :# n) (x :# m))
joinAnnotated ((\a -> (a, y) :# n) x :# m)
joinAnnotated (((x, y) :# n) :# m)
(x, y) :# n <> m
-- Left-hand side
distAnnotated (p, q)
distAnnotated (x :# m, y :# n)
(x, y) :# m <> n
-- LHS /= RHS
因此,问题在于 distAnnotated
以与 joinAnnotated
不同的顺序组合注释(m <> n
与 n <> m
)。让他们同意的通常方法是更改 joinAnnotated
以便外部注释先出现:
joinAnnotated ((b :# m) :# n) = b :# n <> m
这符合 monadic 绑定中的自然计算顺序 (m >>= f = joinAnnotated (mapAnnotated f m)
) 和传统的从左到右的应用效果顺序 (p <*> q = ap p q = mapAnnotated (\(f, a) -> f a) (distAnnotated (p, q))
)。
我正在做函数式编程课程的作业之一,发现在 Haskell 中理解 monad 有一些问题。
所以,我们得到了一个类型:
data Annotated e a = a :# e
infix 0 :#
任务是用给定的类型签名实现一些函数,我做到了。他们通过了所需的测试(分别):
mapAnnotated :: (a -> b) -> (Annotated e a -> Annotated e b)
mapAnnotated f (x :# w) = f x :# w
joinAnnotated :: Semigroup e => Annotated e (Annotated e a) -> Annotated e a
joinAnnotated ((b :# m) :# n) = b :# m <> n
distAnnotated :: Semigroup e => (Annotated e a, Annotated e b) -> Annotated e (a, b)
distAnnotated (x :# m, y :# n) = (x, y) :# m <> n
然而,我们还被要求满足以下等式:
distAnnotated (p, q) = joinAnnotated (mapAnnotated (\a -> mapAnnotated (\b -> (a, b)) q) p)
我不太了解这么多功能应用程序,所以对于其他具有类似任务的类型,我只是做了看起来“自然”的事情并且它有效,但在这里它没有而且我看不到为什么,因为我什至没有看到其他方法来实现这些功能。我错过了什么?
让我们从麻烦的方程式开始,系统地替换定义,由内而外计算:
-- Given
mapAnnotated f (x :# w) = f x :# w
joinAnnotated ((b :# m) :# n) = b :# m <> n
distAnnotated (x :# m, y :# n) = (x, y) :# m <> n
p = x :# m
q = y :# n
-- Goal
distAnnotated (p, q) = joinAnnotated (mapAnnotated (\a -> mapAnnotated (\b -> (a, b)) q) p)
-- Right-hand side
joinAnnotated (mapAnnotated (\a -> mapAnnotated (\b -> (a, b)) q) p)
joinAnnotated (mapAnnotated (\a -> mapAnnotated (\b -> (a, b)) (y :# n)) (x :# m))
joinAnnotated (mapAnnotated (\a -> (\b -> (a, b)) y :# n) (x :# m))
joinAnnotated (mapAnnotated (\a -> (a, y) :# n) (x :# m))
joinAnnotated (mapAnnotated (\a -> (a, y) :# n) (x :# m))
joinAnnotated ((\a -> (a, y) :# n) x :# m)
joinAnnotated (((x, y) :# n) :# m)
(x, y) :# n <> m
-- Left-hand side
distAnnotated (p, q)
distAnnotated (x :# m, y :# n)
(x, y) :# m <> n
-- LHS /= RHS
因此,问题在于 distAnnotated
以与 joinAnnotated
不同的顺序组合注释(m <> n
与 n <> m
)。让他们同意的通常方法是更改 joinAnnotated
以便外部注释先出现:
joinAnnotated ((b :# m) :# n) = b :# n <> m
这符合 monadic 绑定中的自然计算顺序 (m >>= f = joinAnnotated (mapAnnotated f m)
) 和传统的从左到右的应用效果顺序 (p <*> q = ap p q = mapAnnotated (\(f, a) -> f a) (distAnnotated (p, q))
)。