Errors in PureScript

module Main where

import Effect.Console (log)
import Effect (Effect)
import Data.Maybe (Maybe(..))
import Control.Monad.Error.Class (class MonadError, throwError)
import Data.Either (Either(..))

-- In PureScript, we typically use the Either type for error handling.
-- The Left constructor represents an error, while Right represents success.
f :: Int -> Either String Int
f arg = 
  if arg == 42
    then Left "can't work with 42"
    else Right (arg + 3)

-- Sentinel errors in PureScript can be represented as simple String values
errOutOfTea :: String
errOutOfTea = "no more tea available"

errPower :: String
errPower = "can't boil water"

-- We can wrap errors by returning a new error message that includes the original
makeTea :: Int -> Either String Unit
makeTea arg = 
  case arg of
    2 -> Left errOutOfTea
    4 -> Left ("making tea: " <> errPower)
    _ -> Right unit

main :: Effect Unit
main = do
  -- Using do notation for sequencing effects
  let nums = [7, 42]
  traverse_ (\i -> 
    case f i of
      Left e -> log $ "f failed: " <> e
      Right r -> log $ "f worked: " <> show r
  ) nums

  traverse_ (\i -> 
    case makeTea i of
      Left err -> 
        if err == errOutOfTea
          then log "We should buy new tea!"
        else if err == errPower || err == ("making tea: " <> errPower)
          then log "Now it is dark."
        else log $ "unknown error: " <> err
      Right _ -> log "Tea is ready!"
  ) [0, 1, 2, 3, 4]

In PureScript, error handling is typically done using the Either type, which is similar to Go’s approach of returning an error as a separate value. The Left constructor represents an error, while Right represents success.

The f function returns an Either String Int, where String is the error type and Int is the success type. This is analogous to Go’s (int, error) return type.

Sentinel errors in PureScript are often represented as simple String values. We define errOutOfTea and errPower as constants.

The makeTea function demonstrates error wrapping by concatenating error messages. This is similar to Go’s fmt.Errorf with the %w verb.

In the main function, we use traverse_ (which is similar to a for loop) to iterate over lists and handle errors. The case expressions in PureScript are used for pattern matching, which is how we check for specific error conditions.

To check for specific errors, we use equality comparison with the error strings. This is analogous to Go’s errors.Is function, although less sophisticated as it doesn’t handle wrapped errors in the same way.

Note that PureScript is a purely functional language, so side effects like printing to the console are wrapped in the Effect monad.

To run this program, you would typically compile it with the PureScript compiler and then run it with Node.js:

$ pulp run
f worked: 10
f failed: can't work with 42
Tea is ready!
Tea is ready!
We should buy new tea!
Tea is ready!
Now it is dark.

This example demonstrates how PureScript handles errors in a functional way, which is different from Go’s approach but achieves similar goals of explicit error handling and propagation.