Traverse and sequence are powerful operations that allow you to transform and combine effects in a collection. In Phunkie, these operations are primarily implemented for immutable lists.
Traverse is an operation that maps a function over a structure and then sequences the results. It’s particularly useful when working with effects or type constructors.
interface Traversable extends Functor, Monad {
public function traverse(callable $f): Kind;
public function filter(callable $filter): Traversable;
public function withFilter(callable $filter): WithFilter;
public function withEach(callable $block);
}
$numbers = ImmList(1, 2, 3);
// Convert numbers to Options and sequence them
$result = $numbers->traverse(fn($x) => Some($x * 2));
// Some(ImmList(2, 4, 6))
// If any conversion fails, the entire result is None
$result = $numbers->traverse(function($x) {
return $x > 1 ? Some($x) : None();
});
// None()
Sequence is an operation that turns a list of effects into an effect of a list. It’s used to combine multiple effects into a single effect.
// A list of Options
$optionList = ImmList(Some(1), Some(2), Some(3));
$result = $optionList->sequence();
// Some(ImmList(1, 2, 3))
// If any element is None, the entire result is None
$optionList = ImmList(Some(1), None(), Some(3));
$result = $optionList->sequence();
// None()
$validateAge = fn($age) =>
$age >= 0 ? Some($age) : None();
$ages = ImmList(25, 30, -1);
$result = $ages->traverse($validateAge);
// None() because one age is invalid
// Converting a list of IDs to a list of users
$userIds = ImmList(1, 2, 3);
$result = $userIds->traverse(function($id) {
return findUserById($id); // Returns Option<User>
});
// Some(ImmList(User1, User2, User3)) if all users found
// None() if any user not found
$urls = ImmList("url1", "url2", "url3");
$result = $urls->traverse(function($url) {
return fetchAsync($url); // Returns Future<Response>
});
// Future<ImmList<Response>>
The implementation includes type checking to ensure proper usage:
private function guardIsListOfTypeConstructor(): string
{
$listType = showArrayType($this->toArray());
$typeConstructor = substr($listType, 0, strpos($listType, "<"));
if ($typeConstructor == "") {
throw new \TypeError("Cannot find a type constructor in elements");
}
if (!is_callable($typeConstructor)) {
throw new \TypeError("$typeConstructor is not a callable type constructor");
}
return $typeConstructor;
}
Traverse builds on other functional concepts: