summaryrefslogtreecommitdiff
path: root/README.md
diff options
context:
space:
mode:
Diffstat (limited to 'README.md')
-rw-r--r--README.md104
1 files changed, 104 insertions, 0 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..671acbc
--- /dev/null
+++ b/README.md
@@ -0,0 +1,104 @@
+# fallible
+
+[![Build Status](https://travis-ci.com/matsubara0507/fallible.svg?branch=master)](https://travis-ci.com/matsubara0507/fallible)
+
+An interface for fallible data types like Maybe and Either.
+
+## Example
+
+Dealing with Maybe and Either gets a bit annoying when monadic actions are
+involved, often resulting in deep nests like this:
+
+```Haskell
+run :: String -> Token -> Bool -> IO ()
+run targetName token verbose = do
+ users <- getUsers token
+ case users of
+ Left err -> logDebug' err
+ Right us -> do
+ case userId <$> L.find isTarget us of
+ Nothing -> logDebug' emsg
+ Just tid -> do
+ channels <- getChannels token
+ case channels of
+ Left err -> logDebug' err
+ Right chs -> do
+ let chs' = filter (elem tid . channelMembers) chs
+ mapM_ (logDebug' . channelName) chs'
+```
+
+This package offers you several combinators to tidy up this eyesore. See
+[example/Main.hs](example/Main.hs):
+
+```Haskell
+import Data.Fallible
+import qualified Data.List as L
+
+run :: String -> Token -> Bool -> IO ()
+run targetName token verbose = evalContT $ do
+ users <- lift (getUsers token) !?= exit . logDebug'
+ targetId <- userId <$> L.find isTarget users ??? exit (logDebug' emsg)
+ channels <- lift (getChannels token) !?= exit . logDebug'
+ lift $ mapM_ (logDebug' . channelName) $
+ filter (elem targetId . channelMembers) channels
+ where
+ logDebug' = logDebug verbose
+ emsg = "user not found: " ++ targetName
+ isTarget user = userName user == targetName
+
+logDebug :: Bool -> String -> IO ()
+logDebug verbose msg = if verbose then putStrLn msg else pure ()
+```
+
+Notice the couple of operators, `(!?=)` and `(???)`:
+
+```haskell
+(!?=) :: Monad m => m (Either e a) -> (e -> m a) -> m a
+(!??) :: Monad m => m (Maybe a) -> m a -> m a
+(??=) :: Applicative f => Either e a -> (e -> f a) -> f a
+(???) :: Applicative f => Maybe a -> f a -> f a
+```
+
+As you can guess from their type signatures, they run the right side when the
+first arguments returns `Left` or `Nothing`. Then you can use whatever you like
+to handle failures -- exception, MaybeT, ExceptT, ContT etc.
+
+exec using ghci:
+
+```
+$ stack ghci
+>>> :l example/Main.hs
+*Main> run "Alice" "dummy" True
+general
+random
+secret
+*Main> run "Curry" "dummy" True
+general
+random
+owners
+*Main> run "Haskell" "dummy" True
+user not found: Haskell
+*Main> run "Haskell" "" True
+invalid token
+```
+
+## Become a beta tester
+
+### Stack
+
+write to stack.yaml:
+
+```yaml
+extra-deps:
+ github: matsubara0507/fallible
+ commit: XXXXXXX
+```
+
+### cabal
+
+```
+source-repository-package
+ type: git
+ location: https://github.com/matsubara0507/fallible
+ tag: XXXXXXX
+```