Haskell中Monad函数dist与join的关系及Annotated类型等式问题
问题原因
问题出在你joinAnnotated实现里的Semigroup运算顺序和distAnnotated里的顺序不匹配,因为Semigroup的<>默认不满足交换律,两边计算出来的注释拼接顺序相反,我们可以直接展开等式右侧的表达式验证:
我们取两个具体的Annotated值做测试:
p = 1 :# ["p的注释"] q = 2 :# ["q的注释"]
按你给出的三个函数实现,逐步计算等式右侧:
- 先计算最内层的
mapAnnotated (\b -> (a, b)) q:按mapAnnotated的实现,保留q的注释,得到结果(a, 2) :# ["q的注释"] - 再计算外层的
mapAnnotated (\a -> 上述结果) p:保留p的注释,得到结果((1, 2) :# ["q的注释"]) :# ["p的注释"] - 最后用
joinAnnotated处理上述结果:按你的实现,注释拼接为内层注释加外层注释,得到最终结果(1, 2) :# ["q的注释", "p的注释"]
而你实现的distAnnotated计算distAnnotated (p, q)得到的结果是 (1, 2) :# ["p的注释", "q的注释"],两边注释顺序相反,自然不满足等式。
解决方法
你可以任选一种修改方式对齐顺序,通常Writer类带注释的Monad会遵循「先产生的注释在前」的规则,也就是外层计算的注释在前,内层计算的在后,更推荐修改joinAnnotated的拼接顺序:
joinAnnotated :: Semigroup e => Annotated e (Annotated e a) -> Annotated e a joinAnnotated ((b :# m) :# n) = b :# n <> m
修改后join得到的注释顺序会变成外层的p注释在前,内层的q注释在后,和你现有的distAnnotated结果完全一致。
如果你不想修改joinAnnotated的实现,也可以调整distAnnotated的拼接顺序:
distAnnotated :: Semigroup e => (Annotated e a, Annotated e b) -> Annotated e (a, b) distAnnotated (x :# m, y :# n) = (x, y) :# n <> m
内容的提问来源于stack exchange,提问作者Dmitry Ivanov
相关产品推荐
相关产品推荐

