packages feed

failable (empty) → 0.1.0.0

raw patch · 6 files changed

+225/−0 lines, 6 filesdep +basedep +mtldep +transformerssetup-changed

Dependencies added: base, mtl, transformers

Files

+ ChangeLog.md view
@@ -0,0 +1,3 @@+# Changelog for failable++## Unreleased changes
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Author name here (c) 2019++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Author name here nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,58 @@+# failable+#### Yet another "error" handling monad (class)++This library provides a 'Failable' error monad class to unify failure across monads and+transformers most commonly used to implement pipelines that can fail.++*But.. don't we have 'MonadFail', 'MonadThrow', 'MonadError',.. and the true haskeller should be +using 'Alternative' anyway!*++I am sure a lot of ink has been spilled in forums and around water coolers all around the world, +debating the merits and fallacies of one approach or the other. The reason for this package is not+to participate in this discussion but rather to provide a simple nonsense means of signaling a +computation "failure" in those monads that provide the inherent means to do so, and to do it in a+consistent manner++## Usage++```haskell++data FooError = NotImplemented deriving (Typeable, Show)++instance Exception FooError++foo :: (Failable m) => m Int+foo = failure NotImplemented+```++Now, if one called `foo` in a `Maybe monad`:++>>> foo :: Maybe Int+>>> Nothing++the failure is then conveyed by returning `Nothing` as per definition of the `Maybe` monad. Now in the case of the `Either SomeException` monad:++>>> foo :: Either SomeException Int+>>> Left NotImplemented++but what if we are working in the `IO` monad?++>>> foo :: IO Int+>>> * * * Exception: NotImplemented++In this case, the failure can only be conveyed by throwing an IO exception.++Now, the point where `Failable` diverges from say `MonadThrow` for example is when it comes to monad+transformers. For example:++>>> runMaybeT foo :: IO (Maybe Int)++Would throw an `Exception: NotImplemented` if it was implemented in a `MonadThrow` context. Since+the reason d'etre for the runMaybeT is to provide the underlying monad (transformer) with `Maybe` like behaviour, i.e. have `Nothing` be returned in case of aborting the `Maybe` pipeline so to speak, then throwing an exception defeats IMHO the purpose of using `MaybeT` in the first place. So, in the+case of `Failable`:++>>> runMaybeT foo :: IO (Maybe Int)+>>> Nothing++And the same thing applies to `runExceptT` etc. +
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ failable.cabal view
@@ -0,0 +1,35 @@+cabal-version: 1.12++-- This file has been generated from package.yaml by hpack version 0.31.0.+--+-- see: https://github.com/sol/hpack+--+-- hash: c71b774e75efdc6c11a1cd835a896e69b5b91d60a4e0a8fb6428c65ca6f5b0a1++name:           failable+version:        0.1.0.0+synopsis:       A 'Failable' error monad class to unify failure across monads that can fail+description:    Please see the README on GitHub at <https://github.com/githubuser/failable#readme>+category:       control, exceptions, monad+author:         Erick Gonzalez+maintainer:     erick@codemonkeylabs.de+copyright:      2019 Erick Gonzalez+license:        BSD3+license-file:   LICENSE+build-type:     Simple+extra-source-files:+    README.md+    ChangeLog.md++library+  exposed-modules:+      Control.Monad.Failable+  other-modules:+      Paths_failable+  hs-source-dirs:+      src+  build-depends:+      base >=4.7 && <5+    , mtl+    , transformers+  default-language: Haskell2010
+ src/Control/Monad/Failable.hs view
@@ -0,0 +1,97 @@+{-# LANGUAGE GADTs #-}+{- |+Module: Control.Monad.Failable+Description: Yet another error monad but for people who are not crazy+Copyright: (c) Erick Gonzalez, 2019+License: BSD3+Maintainer: erick@codemonkeylabs.de++This library provides a 'Failable' error monad class to unify errors across monads and+transformers most commonly used to implement pipelines that can fail.++-}++module Control.Monad.Failable (+-- |+-- I am sure a lot of ink has been spilled in forums and around water coolers all around the+-- world, debating the merits and fallacies of one approach or the other. The reason for this+-- package is not to participate in this discussion but rather to provide a simple nonsense+-- means of signaling a computation "failure" in those monads that provide the inherent means+-- to do so, and to do it in a consistent manner.+--+-- When triggering a failure in a monadic context which is an instance of this class, simply+-- define your custom exception type and abort the computation with 'failure'. For example:+--+-- @+-- data MyException = SomeProblem+--                  | AnotherProblem+--                  deriving (Show, Typeable)+--+-- instance Exception MyException+--+-- foo :: (Failable m) => Int -> m Int+-- foo x = do+--   y <- bar x+--   if y < 0+--     then failure SomeProblem+--     else return y+-- @+--+--+-- if foo is then called in a 'Maybe' Monad, it would return @Nothing@ in case of error+-- or @Just ()@ of course if succesful. In an @Either SomeException@ context, it would+-- return @Left SomeProblem@ in case of error or @Right ()@ upon success, etc.+-- When it comes to monad transformers incorporating the concept of failure, such as 'MaybeT' or+-- 'ExceptT', it preserves the expected semantics upon failure of yielding an @m Nothing@ or+-- @m (Either SomeException a)@ when the transformer is "ran", instead of adopting the strategy+-- of passing the failure to the underlying monad (transformer) which might for example, throw+-- an async exception (as is the case of IO). Since the reason d'etre for something like runMaybeT+-- is to provide the underlying monad (transformer) with `Maybe` like behaviour, i.e. have+-- @Nothing@ be returned in case of aborting the 'Maybe' pipeline so to speak, then throwing an+-- exception defeats IMHO the purpose of using 'MaybeT' in the first place.+-- +-- >>> foo 2 :: Maybe Int+-- >>> Nothing+--+-- >>> foo 2 :: Either SomeException Int+-- >>> Left SomeProblem+--+-- >>> foo 2 :: IO Int+-- >>> * * * Exception: SomeProblem+--+-- >>> runMaybeT $ foo 2 :: IO (Maybe Int)+-- >>> Nothing+-- +                               Failable(..)) where++import Control.Exception            (Exception(..), SomeException, throw)+import Control.Monad.Except         (ExceptT, throwError)+import Control.Monad.Trans.Maybe    (MaybeT(..))+++-- | The 'Failable' class. A Monad which is an instance of this class can be used as a context+-- in a function running in one with this class constraint, in order to report error conditions++class (Monad m) => Failable m where+    -- | trigger a failure. It takes an exception value as argument and it returns whatever+    -- might be used to abort a monadic computation in the monad instantiating this class.+    failure :: (Exception e) => e -> m a++instance Failable IO where+    failure = throw++instance Failable [] where+    failure _ = []++instance Failable Maybe where+    failure _ = Nothing++instance e ~ SomeException => Failable (Either e) where+    failure = Left . toException++instance (Monad m) => Failable (MaybeT m) where+    failure _ = MaybeT $ pure Nothing++instance (Monad m, e ~ SomeException) => Failable (ExceptT e m) where+    failure = throwError . toException+