diff --git a/docs/combinators.md b/docs/combinators.md index de8ddb4..7cdc179 100644 --- a/docs/combinators.md +++ b/docs/combinators.md @@ -35,93 +35,6 @@ $empty = $io->filter(fn($x) => $x < 40); $result = $empty->unsafeRun(); // throws NoSuchElementException ``` -## Sequence and Traverse - -These combinators help you work with collections of effects. - -### Sequence - -`sequence` transforms a collection of effects into an effect of a collection: - -```php -use function Phunkie\Effect\Functions\io\io; - -$ios = [ - io(function() { return 1; }), - io(function() { return 2; }), - io(function() { return 3; }) -]; - -$result = sequence($ios)->unsafeRun(); // [1, 2, 3] -``` - -### Traverse - -`traverse` is like `sequence` but allows you to transform each value before sequencing: - -```php -use function Phunkie\Effect\Functions\io\io; - -$numbers = [1, 2, 3]; - -$result = traverse($numbers, fn($n) => - io(function() use ($n) { - return $n * 2; - }) -)->unsafeRun(); // [2, 4, 6] -``` - -## Race and Timeout - -These combinators help you handle concurrent operations and timeouts. - -### Race - -`race` runs two effects concurrently and returns the result of the first one to complete: - -```php -use function Phunkie\Effect\Functions\io\io; -use function Phunkie\Effect\Functions\blocking\blocking; - -$fast = io(function() { - return blocking(function() { - usleep(100000); // 0.1 seconds - return "fast"; - }); -}); - -$slow = io(function() { - return blocking(function() { - usleep(200000); // 0.2 seconds - return "slow"; - }); -}); - -$result = $fast->race($slow)->unsafeRun(); // "fast" -``` - -### Timeout - -`timeout` adds a timeout to an effect, failing if it takes too long: - -```php -use function Phunkie\Effect\Functions\io\io; -use function Phunkie\Effect\Functions\blocking\blocking; - -$slow = io(function() { - return blocking(function() { - sleep(2); - return "done"; - }); -}); - -try { - $result = $slow->timeout(1000)->unsafeRun(); // throws TimeoutException -} catch (\Exception $e) { - // Handle timeout -} -``` - ## Error Handling Combinators also provide ways to handle errors in effects. @@ -195,67 +108,3 @@ $message = $result->fold(fn($error) => $error->getMessage())(fn($value) => "ok: $result = $value * 2; ``` -2. **Use Sequence for Collections** - ```php - // Good: Using sequence for collections - $results = sequence($ios)->unsafeRun(); - - // Bad: Manual sequencing - $results = []; - foreach ($ios as $io) { - $results[] = $io->unsafeRun(); - } - ``` - -3. **Use Race for Concurrent Operations** - ```php - // Good: Using race for concurrent operations - $result = $operation1->race($operation2)->unsafeRun(); - - // Bad: Manual concurrent execution - $handle1 = $operation1->unsafeRun(); - $handle2 = $operation2->unsafeRun(); - $result = $handle1->await(); // Might wait unnecessarily - ``` - -4. **Use Timeout for Long-Running Operations** - ```php - // Good: Using timeout for long-running operations - $result = $operation->timeout(1000)->unsafeRun(); - - // Bad: No timeout - $result = $operation->unsafeRun(); // Might hang indefinitely - ``` - -5. **Use Error Handling Combinators** - ```php - // Good: Using error handling combinators - $result = $risky->handleError(fn($e) => "fallback")->unsafeRun(); - - // Bad: Try-catch blocks - try { - $result = $risky->unsafeRun(); - } catch (\Exception $e) { - $result = "fallback"; - } - ``` - -Remember that combinators are designed to work together. You can chain them to create complex effect compositions: - -```php -$result = $io - ->map(fn($x) => $x * 2) - ->filter(fn($x) => $x > 10) - ->timeout(1000) - ->handleError(fn($e) => 0) - ->unsafeRun(); -``` - -This composition: -1. Doubles the value -2. Filters out values less than 10 -3. Times out after 1 second -4. Returns 0 if any error occurs -5. Executes the entire chain - -The power of combinators lies in their ability to compose effects in a declarative way, making your code more readable and maintainable. \ No newline at end of file diff --git a/docs/concurrency.md b/docs/concurrency.md index 34e29a3..7959f8e 100644 --- a/docs/concurrency.md +++ b/docs/concurrency.md @@ -207,130 +207,11 @@ Remember that parallel execution is not a silver bullet. Consider: When in doubt, prefer simpler concurrency models like fibers for I/O operations and only use parallel execution for CPU-intensive tasks that benefit from true parallelism. -## Sequence and Traverse - -These combinators help you work with collections of effects: - -### Sequence -```php -use function Phunkie\Effect\Functions\io\io; - -$ios = [ - io(function() { return 1; }), - io(function() { return 2; }), - io(function() { return 3; }) -]; - -$result = sequence($ios)->unsafeRun(); // [1, 2, 3] -``` - -### Traverse -```php -use function Phunkie\Effect\Functions\io\io; - -$numbers = [1, 2, 3]; - -$result = traverse($numbers, fn($n) => - io(function() use ($n) { - return $n * 2; - }) -)->unsafeRun(); // [2, 4, 6] -``` - -## Race, Both, Either - -These combinators help you handle concurrent operations in different ways: - -### Race -```php -$fast = io(function() { - return blocking(function() { - usleep(100000); // 0.1 seconds - return "fast"; - }); -}); - -$slow = io(function() { - return blocking(function() { - usleep(200000); // 0.2 seconds - return "slow"; - }); -}); - -$result = $fast->race($slow)->unsafeRun(); // "fast" -``` - -### Both -```php -$result = $operation1->both($operation2)->unsafeRun(); -// Returns [result1, result2] -``` - -### Either -```php -$result = $operation1->either($operation2)->unsafeRun(); -// Returns Left(result1) or Right(result2) -``` - -## Cancellation - -Cancellation allows you to stop operations that are no longer needed: - -```php -$operation = io(function() { - return blocking(function() { - sleep(10); - return "done"; - }); -}); - -$handle = $operation->unsafeRun(); -// ... some time later ... -$handle->cancel(); -``` - -## Delay - -Delay allows you to schedule operations to run after a specified time: - -```php -$operation = io(function() { - return blocking(function() { - return "delayed"; - }); -}); - -$result = $operation->delay(1000)->unsafeRun(); // Runs after 1 second -``` - -## Channels - -Channels provide a way to communicate between concurrent operations: - -```php -$channel = new Channel(); - -$producer = io(function() use ($channel) { - return blocking(function() use ($channel) { - $channel->send("message"); - }); -}); - -$consumer = io(function() use ($channel) { - return blocking(function() use ($channel) { - return $channel->receive(); - }); -}); - -$result = $producer->both($consumer)->unsafeRun(); -``` - ## Best Practices 1. **Choose the Right Execution Context** - Use Fiber context for I/O operations - Use Parallel context for CPU-intensive tasks - - Use Process context for isolated operations 2. **Handle Resources Properly** ```php @@ -350,29 +231,3 @@ $result = $producer->both($consumer)->unsafeRun(); $result = $operation1->flatMap(fn($a) => $operation2->map(fn($b) => $a + $b)); ``` -4. **Handle Cancellation** - ```php - $handle = $operation->unsafeRun(); - try { - $result = $handle->await(); - } catch (CancellationException $e) { - // Handle cancellation - } - ``` - -5. **Use Error Handling** - ```php - $result = $operation - ->handleError(fn($e) => "fallback") - ->timeout(1000) - ->unsafeRun(); - ``` - -Remember that concurrency is a powerful tool, but it comes with complexity. Always consider: -- Resource management -- Error handling -- Cancellation -- Timeouts -- State management - -The key is to use the right tool for the job and handle all edge cases appropriately. \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index 3eaa174..78e4fa9 100644 --- a/docs/index.md +++ b/docs/index.md @@ -49,18 +49,10 @@ Master the fundamental building blocks: - Blockers - Execution Context - Parallel Execution - - Sequence and Traverse - - Race, Both, Either - - Cancellation - - Delay - - Channels ### 6. [Resources](resources.md) - Brackets - - Resource Combinators - - Files - - URL - - Database + - Files, database and network connections ## Contributing diff --git a/docs/io.md b/docs/io.md index caaafb5..978227d 100644 --- a/docs/io.md +++ b/docs/io.md @@ -131,20 +131,6 @@ $workflow = readConfig('config.json') ->flatMap(fn($users) => sendEmails($users)); ``` -### productR (*>) - Sequence and Discard First - -```php -// Run both effects, keep only the second result -$program = writeLog('Starting...') - ->productR(performOperation()) - ->productR(writeLog('Done')); - -// Equivalent to: -$program = writeLog('Starting...') - ->flatMap(fn($_) => performOperation()) - ->flatMap(fn($result) => writeLog('Done')->map(fn($_) => $result)); -``` - ## Combining IO ### Sequential Composition @@ -318,7 +304,7 @@ plain `for { $x <- io } yield ...` (no guard) works as usual, desugaring to $program = fetchFromApi() ->flatMap(fn($data) => io(fn() => processData($data))) ->handleErrorWith(fn($error) => io(fn() => logError($error)) - ->productR(io(fn() => getDefaultData()))); + ->flatMap(fn() => io(fn() => getDefaultData()))); ``` ## Running IO diff --git a/docs/resources.md b/docs/resources.md index a5429af..5a13cf8 100644 --- a/docs/resources.md +++ b/docs/resources.md @@ -48,41 +48,6 @@ $result = bracket( )->unsafeRun(); ``` -### BracketCase - -`bracketCase` allows different cleanup strategies based on how the resource was used: - -```php -$result = bracketCase( - blocking(function() { return openFile(); }), - function($file) { return readFile($file); }, - function($file, $exitCase) { - return match($exitCase) { - ExitCase::Completed => closeFile($file), - ExitCase::Error => closeAndLogError($file), - ExitCase::Cancelled => closeAndNotify($file) - }; - } -)->unsafeRun(); -``` - -### BracketE - -`bracketE` combines error handling with resource management: - -```php -$result = bracketE( - blocking(function() { return openConnection(); }), - function($conn) { return useConnection($conn); }, - function($conn, $error) { - return match($error) { - null => closeConnection($conn), - default => closeAndLogError($conn, $error) - }; - } -)->unsafeRun(); -``` - ## Common Resource Patterns ### File Operations @@ -159,36 +124,7 @@ $result = bracket( } ``` -2. **Handle Errors in Release** - ```php - $result = bracketE( - blocking(function() { return openResource(); }), - function($r) { return useResource($r); }, - function($r, $error) { - return match($error) { - null => closeResource($r), - default => closeAndLogError($r, $error) - }; - } - ); - ``` - -3. **Use Appropriate Cleanup Strategies** - ```php - $result = bracketCase( - blocking(function() { return openResource(); }), - function($r) { return useResource($r); }, - function($r, $exitCase) { - return match($exitCase) { - ExitCase::Completed => closeResource($r), - ExitCase::Error => closeAndLogError($r), - ExitCase::Cancelled => closeAndNotify($r) - }; - } - ); - ``` - -4. **Compose Resource Operations** +2. **Compose Resource Operations** ```php $result = bracket( blocking(function() { return openResource(); }), @@ -203,13 +139,12 @@ $result = bracket( ); ``` -5. **Handle Resource Limits** +3. **Handle Resource Limits** ```php $result = bracket( blocking(function() { return openResource(); }), function($r) { return $operation - ->timeout(1000) ->handleError(fn($e) => "fallback"); }, function($r) { return closeResource($r); }