MonadZero
The MonadZero
type class extends the Monad
interface to include the concept of a "zero" or "empty" element. It is designed for monads that can represent failure, absence, or emptiness, allowing them to be used in filtering operations.
Purpose and Concept
A Monad
provides a way to sequence computations within a context (flatMap
, map
, of
). A MonadZero
adds one critical operation to this structure:
zero()
: Returns the "empty" or "zero" element for the monad.
This zero
element acts as an absorbing element in a monadic sequence, similar to how multiplying by zero results in zero. If a computation results in a zero
, subsequent operations in the chain are typically skipped.
Key Implementations in this Project:
- For List,
zero()
returns an empty list[]
. - For Maybe,
zero()
returnsNothing
. - For Optional,
zero()
returnsOptional.empty()
.
Primary Uses
The main purpose of MonadZero
is to enable filtering within monadic comprehensions. It allows you to discard results that don't meet a certain criterion.
1. Filtering in For-Comprehensions
The most powerful application in this codebase is within the For
comprehension builder. The builder has two entry points:
For.from(monad, ...)
: For any standardMonad
.For.from(monadZero, ...)
: An overloaded version specifically for aMonadZero
.
Only the version that accepts a MonadZero
provides the .when(predicate)
filtering step. When the predicate in a .when()
clause evaluates to false
, the builder internally calls monad.zero()
to terminate that specific computational path.
2. Generic Functions
It allows you to write generic functions that can operate over any monad that has a concept of "failure" or "emptiness," such as List
, Maybe
, or Optional
.
Code Example: For
Comprehension with ListMonad
The following example demonstrates how MonadZero
enables filtering.
import org.higherkindedj.hkt.Kind;
import org.higherkindedj.hkt.expression.For;
import org.higherkindedj.hkt.list.ListKind;
import org.higherkindedj.hkt.list.ListMonad;
import java.util.Arrays;
import java.util.List;
import static org.higherkindedj.hkt.list.ListKindHelper.LIST;
// 1. Get the MonadZero instance for List
final ListMonad listMonad = ListMonad.INSTANCE;
// 2. Define the initial data sources
final Kind<ListKind.Witness, Integer> list1 = LIST.widen(Arrays.asList(1, 2, 3));
final Kind<ListKind.Witness, Integer> list2 = LIST.widen(Arrays.asList(10, 20));
// 3. Build the comprehension using the filterable 'For'
final Kind<ListKind.Witness, String> result =
For.from(listMonad, list1) // Start with a MonadZero
.from(a -> list2) // Generator (flatMap)
.when(t -> (t._1() + t._2()) % 2 != 0) // Filter: if the sum is odd
.let(t -> "Sum: " + (t._1() + t._2())) // Binding (map)
.yield((a, b, c) -> a + " + " + b + " = " + c); // Final projection
// 4. Unwrap the result
final List<String> narrow = LIST.narrow(result);
System.out.println("Result of List comprehension: " + narrow);
Explanation:
- The comprehension iterates through all pairs of
(a, b)
fromlist1
andlist2
. - The
.when(...)
clause checks if the suma + b
is odd. - If the sum is even, the
monad.zero()
method (which returns an empty list) is invoked for that path, effectively discarding it. - If the sum is odd, the computation continues to the
.let()
and.yield()
steps.
Output:
Result of List comprehension: [1 + 10 = Sum: 11, 1 + 20 = Sum: 21, 3 + 10 = Sum: 13, 3 + 20 = Sum: 23]