Skip to content

Add checked exceptions for run #3

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion bower.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"dependencies": {
"purescript-prelude": "^3.1.1",
"purescript-transformers": "^3.5.0",
"purescript-variant": "^4.1.0"
"purescript-variant": "^4.1.0",
"purescript-run": "^1.0.1"
}
}
51 changes: 51 additions & 0 deletions src/Run/Except/Checked.purs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
-- | ## Extensible checked exceptions with polymorphic variants
-- |
-- | This module provides helpers for using `Variant` with `Except`. When
-- | combined, we get a mechanism for extensible, checked exceptions. That
-- | is, exceptions can be defined and used anywhere, and handled as needed.
-- | Handling an exception eliminates it from the type, giving us proof
-- | that it no longer occurs.
module Run.Except.Checked
( EXCEPTV
, handleError
, safe
) where

import Prelude

import Data.Either (either)
import Data.Variant (class VariantMatchCases, Variant, case_, onMatch)
import Run (Run, expand)
import Run.Except (EXCEPT, runExcept, throw)
import Type.Row (class RowToList)

type EXCEPTV exc = EXCEPT (Variant exc)

-- | Catches and eliminates exceptions given a record of handlers.
-- | Unhandled exceptions are re-propragated. Record fields map
-- | to the label for the exception being handled.
-- |
-- | An example for handling HTTP exceptions might be:
-- | ```purescript
-- | request # handleError
-- | { httpNotFound: \_ -> ...
-- | , httpServerError: \error -> ...
-- | }
-- | ```
handleError
∷ ∀ m handlers excHandled excIn excOut rl r a
. RowToList handlers rl
⇒ VariantMatchCases rl excHandled (Run (except :: EXCEPTV excOut | r) a)
⇒ Union excHandled excOut excIn
⇒ Union r m (except :: EXCEPTV excOut | r)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm unsure about this bit of the type.

The expand below forced this constraint. Is it right? Maybe there's another way to do the same thing?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the expand necessary if you account for duplicate labels? That is, something like

  ...
  → Run (except :: EXCEPTV excIn, except :: EXCEPTV excOut | r) a
  → Run (except :: EXCEPTV excOut | r) a

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like dropping the expand and changing to the signature you suggest allows dropping the Union r m (except :: EXCEPTV excOut | r) constraint.

Which would you prefer: with expand and constraint, or without?

⇒ { | handlers }
→ Run (except :: EXCEPTV excIn | r) a
→ Run (except :: EXCEPTV excOut | r) a
handleError cases = runExcept >>> expand >=> either (onMatch cases throw) pure

-- | Safely removes the `Except` layer when all exceptions have been handled.
safe
∷ ∀ r a
. Run (except :: EXCEPTV () | r) a
→ Run r a
safe = runExcept >>> map (either case_ id)