验证对象是否为Monad为何需检查全部三条单子律?
单子律与违反单一条律的Monad实例
三条单子律(Java风格表述)
- 左单位律:
Monad.of(x).flatMap(y -> f(y))等价于f(x) - 右单位律:
monad.flatMap(y -> Monad.of(y))等价于monad - 结合律:
monad.flatMap(x -> f(x)).flatMap(x -> g(x))等价于monad.flatMap(x -> f(x).flatMap(x -> g(x)))
下面分别给出仅违反其中一条律则、同时满足另外两条的Java实例:
1. 仅违反右单位律(用户提供的Counter实例)
代码实现
import java.util.function.Function; class Counter<T> { private final T val; private final int count; private Counter(T val, int count) { this.val = val; this.count = count; } public static <T> Counter<T> of(T val) { return new Counter<>(val, 1); } public <R> Counter<R> map(Function<T, R> fn) { return new Counter<>(fn.apply(this.val), this.count + 1); } public <R> Counter<R> flatMap(Function<T, Counter<R>> fn) { Counter<R> tmp = fn.apply(this.val); return new Counter<>(tmp.val, tmp.count); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof Counter<?>)) { return false; } Counter<?> ctx = (Counter<?>) obj; return this.val.equals(ctx.val) && this.count == ctx.count; } }
律则验证
- 满足左单位律:
Counter.of(x).flatMap(f)直接返回f(x)的结果,与f(x)完全等价。 - 违反右单位律:若有
Counter<String> c = new Counter<>("test", 3),调用c.flatMap(y -> Counter.of(y))会返回Counter.of("test")(count=1),与原实例的count=3不相等。 - 满足结合律:两边flatMap最终都会执行
f(x)获取中间实例,再执行g处理其值,返回结果的val和count完全一致。
2. 仅违反左单位律的实例
代码实现
import java.util.function.Function; import java.util.Objects; class LeftBrokenMonad<T> { private final T val; private final int n; private LeftBrokenMonad(T val, int n) { this.val = val; this.n = n; } public static <T> LeftBrokenMonad<T> of(T val) { return new LeftBrokenMonad<>(val, 0); } public <R> LeftBrokenMonad<R> flatMap(Function<T, LeftBrokenMonad<R>> fn) { LeftBrokenMonad<R> res = fn.apply(this.val); return new LeftBrokenMonad<>(res.val, this.n); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof LeftBrokenMonad<?>)) return false; LeftBrokenMonad<?> other = (LeftBrokenMonad<?>) obj; return Objects.equals(val, other.val) && n == other.n; } }
律则验证
- 违反左单位律:假设
f(x) = new LeftBrokenMonad<>(x, 5),LeftBrokenMonad.of(x).flatMap(f)返回LeftBrokenMonad(x, 0),与f(x)的LeftBrokenMonad(x,5)不相等。 - 满足右单位律:任意
monad = new LeftBrokenMonad<>(x, 3)调用monad.flatMap(y -> LeftBrokenMonad.of(y)),会返回LeftBrokenMonad(x, 3),与原实例完全等价。 - 满足结合律:左右两边的flatMap最终都会保留原实例的
n值,仅传递val的处理结果,结果完全一致。
3. 仅违反结合律的实例
代码实现
import java.util.function.Function; import java.util.Objects; enum Dir { E, A, B; public Dir multiply(Dir other) { if (this == E) return other; if (other == E) return this; if (this == A) { return other == A ? E : A; } return other == A ? B : E; } } class AssociativeBrokenMonad<T> { private final T val; private final Dir dir; private AssociativeBrokenMonad(T val, Dir dir) { this.val = val; this.dir = dir; } public static <T> AssociativeBrokenMonad<T> of(T val) { return new AssociativeBrokenMonad<>(val, Dir.E); } public <R> AssociativeBrokenMonad<R> flatMap(Function<T, AssociativeBrokenMonad<R>> fn) { AssociativeBrokenMonad<R> res = fn.apply(this.val); return new AssociativeBrokenMonad<>(res.val, this.dir.multiply(res.dir)); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof AssociativeBrokenMonad<?>)) return false; AssociativeBrokenMonad<?> other = (AssociativeBrokenMonad<?>) obj; return Objects.equals(val, other.val) && dir == other.dir; } }
律则验证
- 满足左单位律:
of(x).flatMap(f)会返回(E * res.dir, res.val),与f(x)的(res.dir, res.val)完全等价。 - 满足右单位律:任意
monad调用monad.flatMap(y -> of(y)),会返回(s * E, x),与原实例(s,x)完全等价。 - 违反结合律:取
monad = new AssociativeBrokenMonad<>("test", Dir.A),f(x) = new AssociativeBrokenMonad<>(x, Dir.B),g(x) = new AssociativeBrokenMonad<>(x, Dir.A):- 左边:
monad.flatMap(f).flatMap(g)最终返回(E, "test") - 右边:
monad.flatMap(x -> f(x).flatMap(g))最终返回(A, "test")
两者结果不相等,违反结合律。
- 左边:
内容的提问来源于stack exchange,提问作者teckorjanin7
相关产品推荐
相关产品推荐

