packages feed

caledon-3.2.0.0: Choice.hs

{-# LANGUAGE
 DeriveFunctor,
 FlexibleContexts,
 TypeSynonymInstances,
 FlexibleInstances,
 MultiParamTypeClasses
 #-}

module Choice where

import Control.Monad
import Data.Functor
import Control.Applicative
import Control.Monad.Error.Class (catchError, throwError, MonadError)
import Control.Monad.Cont

data Choice a = Choice a :<|>: Choice a
              | Fail String
              | Success a
              deriving (Functor)

instance Monad Choice where
  fail = Fail
  return = Success
  Fail a >>= _ = Fail a
  (m :<|>: m') >>= f = (m >>= f) :<|>: (m' >>= f)
  Success a >>= f = f a

instance Applicative Choice where
  pure = Success
  mf <*> ma = mf >>= (<$> ma)

--determine a b = appendErr "" $ (:<|>:) (appendErr "" a) (appendErr "" b)
determine = (:<|>:)

instance Alternative Choice where
  empty = Fail ""
  (<|>) = determine

instance MonadPlus Choice where
  mzero = Fail ""
  mplus = determine

class RunChoice m where
  runError :: m a -> Either String a

instance RunChoice Choice where
  runError chs = case dropWhile notSuccess lst of
    [] -> case dropWhile notFail lst of
      Fail a:_ -> Left a
      _ -> error "this result makes no sense"
    Success a : _ -> Right a
    _ -> error "this result makes no sense"
    where lst = chs:queue lst 1

          queue _ 0 = []
          queue [] _ = error "queue size should be empty when queue is empty"
          queue ((a :<|>: b):l) q = a:b:queue l (q + 1)
          queue (_:l) q = queue l (q - 1)

          notFail (Fail _) = False
          notFail _ = True

          notSuccess (Success _) = False
          notSuccess _ = True

appendErr :: (MonadError String m) => String -> m a -> m a
appendErr s m = catchError m $ \s' -> throwError $ s' ++ "\n" ++ s

instance MonadError String Choice where
  throwError = Fail
  catchError try1 foo_try2 = case runError try1 of
    Left s -> foo_try2 s
    Right a -> Success a

type CONT_T a m c = ((c -> m a) -> m a)

-- why doesn't this exist in the standard library?
instance (Monad m, Alternative m) => Alternative (ContT a m) where
  empty = lift $ empty
  c1 <|> c2 = ContT $ \cont -> m1f cont <|> m2f cont
    where m1f = runContT c1
          m2f = runContT c2