module Util.Enum (toEnumMay, fromEnum, predMay, succMay) where import Prelude hiding (filter, fromEnum, head, tail) import qualified Prelude import Control.Applicative import Control.Monad import Control.Exception import Data.Bool (bool) import Data.Maybe (fromMaybe) import Data.Semigroup (Semigroup (..), stimes) import System.IO.Unsafe toEnumMay :: Enum a => Integer -> Maybe a toEnumMay n = opMay (toEnum . fromIntegral) n <|> toEnumSafe n toEnumSafe :: Enum a => Integer -> Maybe a toEnumSafe n = stimes (abs n) (EndoM $ bool succMay predMay $ n < 0) `endoM` toEnum 0 fromEnum :: Enum a => a -> Integer fromEnum = fromMaybe <$> fromEnumSafe <*> opMay (fromIntegral . Prelude.fromEnum) fromEnumSafe :: Enum a => a -> Integer fromEnumSafe a = head $ f `mapMaybe` let go n = n :. negate n :. go (n+1) in 0 :. go 1 where f n = n <$ (guard . (== 0) =<< opMay Prelude.fromEnum =<< stimes (abs n) (EndoM $ bool succMay predMay $ n < 0) `endoM` a) predMay, succMay :: Enum a => a -> Maybe a predMay = opMay pred succMay = opMay succ opMay :: (a -> b) -> a -> Maybe b opMay f a = unsafePerformIO $ (Just <$> evaluate (f a)) `catches` handlers Nothing handlers :: a -> [Handler a] handlers a = [Handler $ \ (_ :: ArithException) -> pure a, Handler $ \ (_ :: ArrayException) -> pure a, Handler $ \ (_ :: AssertionFailed) -> pure a, Handler $ \ (_ :: NonTermination) -> pure a, Handler $ \ (_ :: NoMethodError) -> pure a, Handler $ \ (_ :: PatternMatchFail) -> pure a, Handler $ \ (_ :: RecConError) -> pure a, Handler $ \ (_ :: RecSelError) -> pure a, Handler $ \ (_ :: RecUpdError) -> pure a, Handler $ \ (_ :: ErrorCall) -> pure a] newtype EndoM m a = EndoM { endoM :: a -> m a } instance Monad m => Semigroup (EndoM m a) where EndoM f <> EndoM g = EndoM (f >=> g) instance Monad m => Monoid (EndoM m a) where mappend = (<>) mempty = EndoM pure infixr 5 :. data Stream a = (:.) { head :: a, tail :: Stream a } deriving (Functor, Foldable) instance Applicative Stream where pure a = a :. pure a f :. fs <*> x :. xs = f x :. (fs <*> xs) instance Monad Stream where xs >>= f = join (f <$> xs) where join (a :. as) = head a :. join (tail <$> as) mapMaybe :: (a -> Maybe b) -> Stream a -> Stream b mapMaybe f (a :. as) = maybe id (:.) (f a) $ mapMaybe f as