-- | This module is adapted from https://github.com/polysemy-research/polysemy/blob/master/README.md, -- originally BSD3 license, authors Sandy Maguire et al. module Teletype where import Cleff import Cleff.Error import Cleff.Input import Cleff.Mask import Cleff.Output import Cleff.State import Control.Exception (Exception) import Control.Monad (unless) import Data.Maybe (fromMaybe) -- * Effect -- | An effect for reading and writing lines to a tty. data Teletype :: Effect where ReadTTY :: Teletype m String WriteTTY :: String -> Teletype m () -- * Operations makeEffect ''Teletype -- * Interpretations -- | Run 'Teletype' via stdio. runTeletypeIO :: IOE :> es => Eff (Teletype ': es) a -> Eff es a runTeletypeIO = interpretIO \case ReadTTY -> getLine WriteTTY s -> putStrLn s -- | Run 'Teletype' from a fixed input list. runTeletypePure :: [String] -> Eff (Teletype ': es) w -> Eff es [String] runTeletypePure tty = fmap (reverse . snd) . runState [] . outputToListState . runState tty . inputToListState . reinterpret2 \case ReadTTY -> fromMaybe "" <$> input WriteTTY msg -> output msg -- * Examples -- | An echoing program. echo :: Teletype :> es => Eff es () echo = do x <- readTTY unless (null x) $ writeTTY x >> echo -- | The pure interpretation of 'echo', via 'runTeletypePure'. -- >>> echoPure ["abc", "def", "ghci"] -- ["abc","def","ghci"] echoPure :: [String] -> [String] echoPure tty = runPure $ runTeletypePure tty echo -- | The impure interpretation of 'echo', via 'runTeletypeIO'. echoIO :: IO () echoIO = runIOE $ runTeletypeIO echo data CustomException = ThisException | ThatException deriving stock (Show) deriving anyclass (Exception) program :: '[Mask, Teletype, Error CustomException] :>> es => Eff es () program = catchError @CustomException work \e -> writeTTY $ "Caught " ++ show e where work = bracket readTTY (const $ writeTTY "exiting bracket") \next -> do writeTTY "entering bracket" case next of "explode" -> throwError ThisException "weird stuff" -> writeTTY next *> throwError ThatException _ -> writeTTY next *> writeTTY "no exceptions" main :: IO (Either CustomException ()) main = runIOE $ runMask $ runError @CustomException $ runTeletypeIO program