packages feed

capnp-0.17.0.0: lib/Internal/STE.hs

{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}

-- | Simplified implementation of the monad-ste package,
-- with a few extras.
module Internal.STE
  ( STE,
    throwSTE,
    runSTE,
    liftST,
    steToST,
    steToIO,
  )
where

import Control.Exception
import Control.Monad.Catch (MonadThrow (..))
import Control.Monad.Primitive (PrimMonad (..))
import Control.Monad.ST
import Control.Monad.ST.Unsafe
import Data.Typeable (Typeable)

newtype InternalErr e = InternalErr e
  deriving stock (Typeable)

instance Show (InternalErr e) where
  show _ = "(InternalErr _)"

instance Typeable e => Exception (InternalErr e)

newtype STE e s a = STE (IO a)
  deriving newtype (Functor, Applicative, Monad)

instance PrimMonad (STE e s) where
  type PrimState (STE e s) = s
  primitive = liftST . primitive

liftST :: ST s a -> STE e s a
liftST st = STE (unsafeSTToIO st)

throwSTE :: Exception e => e -> STE e s a
throwSTE e = STE (throwIO (InternalErr e))

runSTE :: Exception e => (forall s. STE e s a) -> Either e a
runSTE ste = runST (steToST ste)

steToST :: Typeable e => STE e s a -> ST s (Either e a)
steToST (STE io) = unsafeIOToST $ do
  res <- try io
  case res of
    Left (InternalErr e) -> pure $ Left e
    Right v -> pure $ Right v

steToIO :: forall e a. Exception e => STE e RealWorld a -> IO a
steToIO (STE io) = do
  res <- try io
  case res of
    Left (InternalErr (e :: e)) -> throwIO e
    Right v -> pure v

instance MonadThrow (STE SomeException s) where
  throwM = throwSTE . toException