ℹ️ Select 'Choose Exercise', or randomize 'Next Random Exercise' in selected language.

Choose Exercise:
Timer 00:00
WPM --
Score --
Acc --
Correct chars --

Monadic Error Handling: Either for Division

Scala

Goal -- WPM

Ready
Exercise Algorithm Area
1import scala.util.control.NonFatal
2
3object MonadicErrorHandling {
4
5/**
6* Safely divides two numbers, returning Either[String, Double].
7* Returns Left(error message) if division by zero occurs.
8* Returns Right(result) otherwise.
9*/
10def safeDivide(numerator: Double, denominator: Double): Either[String, Double] = {
11if (denominator == 0.0) {
12Left("Division by zero")
13} else {
14Right(numerator / denominator)
15}
16}
17
18/**
19* Performs a sequence of safe divisions using flatMap.
20* Example: (a / b) / c
21*
22* @param a Numerator for the first division.
23* @param b Denominator for the first division.
24* @param c Denominator for the second division.
25* @return Either[String, Double] representing the final result or an error.
26*/
27def chainedSafeDivision(a: Double, b: Double, c: Double): Either[String, Double] = {
28safeDivide(a, b).flatMap {
29intermediateResult => {
30// If the first division was successful (Right), proceed to the second division.
31// If it failed (Left), flatMap will short-circuit and propagate the error.
32safeDivide(intermediateResult, c)
33}
34}
35// Further chaining example: ((a / b) / c) / d
36/*
37safeDivide(a, b).flatMap {
38res1 => safeDivide(res1, c).flatMap {
39res2 => safeDivide(res2, d)
40}
41}
42*/
43}
44
45/**
46* A more complex sequence of operations involving safe division and other transformations.
47* Computes: ((a / b) + c) / d
48*
49* @param a Numerator for the first division.
50* @param b Denominator for the first division.
51* @param c Value to add after the first division.
52* @param d Denominator for the second division.
53* @return Either[String, Double] representing the final result or an error.
54*/
55def complexOperation(a: Double, b: Double, c: Double, d: Double): Either[String, Double] = {
56safeDivide(a, b).flatMap {
57res1 => {
58// Add c to the result of the first division.
59val sumResult = res1 + c
60// Then divide the sum by d.
61safeDivide(sumResult, d)
62}
63}.recoverWith {
64// Example of handling specific errors or transforming them.
65// This is more advanced and often done with specific error types.
66// For simplicity here, we just re-throw non-fatal exceptions if they occur during addition.
67case NonFatal(e) => Left(s"An unexpected error occurred during addition: ${e.getMessage}")
68}
69}
70
71def main(args: Array[String]): Unit = {
72// Test cases for safeDivide
73println("--- Testing safeDivide ---")
74println(s"10.0 / 2.0 = ${safeDivide(10.0, 2.0)}") // Expected: Right(5.0)
75println(s"10.0 / 0.0 = ${safeDivide(10.0, 0.0)}") // Expected: Left(Division by zero)
76println(s"-5.0 / 2.0 = ${safeDivide(-5.0, 2.0)}") // Expected: Right(-2.5)
77
78// Test cases for chainedSafeDivision
79println("\n--- Testing chainedSafeDivision ---")
80println(s"(100.0 / 10.0) / 2.0 = ${chainedSafeDivision(100.0, 10.0, 2.0)}") // Expected: Right(5.0)
81println(s"(100.0 / 0.0) / 2.0 = ${chainedSafeDivision(100.0, 0.0, 2.0)}") // Expected: Left(Division by zero)
82println(s"(100.0 / 10.0) / 0.0 = ${chainedSafeDivision(100.0, 10.0, 0.0)}") // Expected: Left(Division by zero)
83
84// Test cases for complexOperation
85println("\n--- Testing complexOperation ---")
86println(s"((100.0 / 10.0) + 5.0) / 2.0 = ${complexOperation(100.0, 10.0, 5.0, 2.0)}") // Expected: Right(7.5)
87println(s"((100.0 / 0.0) + 5.0) / 2.0 = ${complexOperation(100.0, 0.0, 5.0, 2.0)}") // Expected: Left(Division by zero)
88println(s"((100.0 / 10.0) + 5.0) / 0.0 = ${complexOperation(100.0, 10.0, 5.0, 0.0)}") // Expected: Left(Division by zero)
89
90// Edge case: very large numbers (potential for Double precision issues, not explicit error handling here)
91println(s"Large numbers: ${safeDivide(Double.MaxValue, 2.0)}")
92// Edge case: NaN/Infinity inputs (handled by Double arithmetic, but could be explicitly checked)
93println(s"NaN input: ${safeDivide(Double.NaN, 2.0)}")
94println(s"Infinity input: ${safeDivide(Double.PositiveInfinity, 2.0)}")
95}
96}
Algorithm description viewbox

Monadic Error Handling: Either for Division

Algorithm description:

This Scala code demonstrates monadic error handling using the `Either` type. The `safeDivide` function encapsulates a division operation, returning `Left[String]` for errors (like division by zero) and `Right[Double]` for successful results. The `flatMap` method of `Either` is then used to chain multiple `safeDivide` operations. If any operation in the chain returns `Left`, `flatMap` short-circuits the computation and propagates the error. This pattern provides a robust way to handle potential failures in sequential operations without resorting to exceptions for control flow. It's widely used in functional programming for managing operations that can fail.

Algorithm explanation:

The `safeDivide` function returns `Either[String, Double]`. `Left` signifies an error (division by zero), and `Right` signifies success. The `flatMap` method on `Either` is crucial for chaining operations. If the `Either` is `Left`, `flatMap` does nothing and returns the `Left` value. If it's `Right(value)`, it applies the function provided to `flatMap` to `value` and returns the resulting `Either`. This allows for sequential execution where any failure immediately halts the chain. The `chainedSafeDivision` function shows a simple two-step division. The `complexOperation` demonstrates adding a value before the second division. The `recoverWith` is an advanced feature to handle potential exceptions during other operations (like addition) and convert them into `Either[String, Double]`. Time complexity for `safeDivide` is O(1). For chained operations, it's O(k) where k is the number of operations in the chain. Space complexity is O(1) for each operation.

Pseudocode:

function safeDivide(numerator, denominator):
  if denominator is 0:
    return Left("Division by zero")
  else:
    return Right(numerator / denominator)

function chainedSafeDivision(a, b, c):
  result1 = safeDivide(a, b)
  if result1 is Left:
    return result1
  else:
    intermediateValue = get value from result1
    return safeDivide(intermediateValue, c)

function complexOperation(a, b, c, d):
  result1 = safeDivide(a, b)
  if result1 is Left:
    return result1
  else:
    intermediateValue = get value from result1
    sum = intermediateValue + c
    return safeDivide(sum, d)

main function:
  test safeDivide with various inputs
  test chainedSafeDivision with successful and failing inputs
  test complexOperation with successful and failing inputs