Suppose I define an instance of the Monad
typeclass for Future
:
val futureMonad = new Monad[Future] {
override def point[A](a: ⇒ A): Future[A] =
Future(a)
override def bind[A, B](fa: Future[A])(f: A => Future[B]): Future[B] =
fa flatMap f
}
Strictly speaking, this is not a monad, since it violates the law of left identity:
futureMonad.point(a) bind f == f(a)
If f
throws an exception, the result of the expression on the left hand side will be a failed Future
, whereas the right hand side will, of course, throw the exception.
But what are the practical implications of this violation? In which ways can a system fail as a result of this "misbehavior"?
Monads such as Try
and Future
trade one monad law for another law which is more useful in the context they are supposed to be used:
An expression composed from (Try
or Future
), flatMap, map will never throw a non-fatal exception. Call this the "bullet-proof" principle.
So actually this approach really protects you against many failures and left-unit law is failed deliberately.
It just means, in terms of for comprehensions, that the following refactoring is not semantics-preserving:
for (fut <- Future(a); x <- f(fut)) yield x ==> f(a)
But that's just another way of writing the left identity law, really.
To explain that invalid refactoring further:
for (fut <- Future(a); x <- f(fut)) yield x
==> for (x <- f(a)) yield x // by left identity law: WRONG, because left identity law does not hold
==> f(a) // by 1st functor law: WRONG, because previous line was wrong
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With