polysemy-zoo-0.4.0.1: Experimental, user-contributed effects and interpreters for polysemy

Safe HaskellNone
LanguageHaskell2010

Polysemy.Operators

Contents

Description

Operators meant as replacements for traditional Sem type and Member / Members constraints, that allow you to specify types of your actions and interpreters in more concise way, without mentioning unnecessary details:

foo :: Member (Lift IO) r => String -> Int -> Sem r ()

can be written simply as:

foo :: String -> Int -> IO ~@> ()

Working example with operators:

import Data.Function
import Polysemy
import Polysemy.Operators
import Polysemy.Random

data ConsoleIO m a where
  WriteStrLn ::           String -> ConsoleIO m ()
  ReadStrLn  ::                     ConsoleIO m String
  ShowStrLn  :: Show a => a      -> ConsoleIO m ()

makeSem ''ConsoleIO

-- runConsoleIO :: Member (Lift IO) r => Sem (ConsoleIO : r) a -> Sem r a
runConsoleIO :: ConsoleIO : r @> a -> IO ~@ r @> a
runConsoleIO = interpret \case
  WriteStrLn s -> sendM $ putStrLn s
  ReadStrLn    -> sendM   getLine
  ShowStrLn  v -> sendM $ print v

main :: IO ()
main = program
     & runConsoleIO
     & runRandomIO
     & runM

-- program :: Members '[Random, ConsoleIO] r => Sem r ()
program :: '[Random, ConsoleIO] >@> ()
program = do
  writeStrLn "It works! Write something:"
  val <- readStrLn
  writeStrLn $ "Here it is!: " ++ val
  num <- random @Int
  writeStrLn $ "Some random number:"
  showStrLn num

Please keep in mind that constraints created through these operators are limited to the action they are being used on, for example:

foo :: (forall x. r @> x -> IO x)
    -> IO (forall a. Foo : r @> a -> IO ~@ r @> a)

The first argument in the signature above won't have access to the (IO ~@) constraint in the result - in such cases, use a normal constraint instead:

foo :: Member (Lift IO) r
    => (forall x. r @> x -> IO x)
    -> IO (forall a. Foo : r @> a -> r @> a)

See the documentation of specific operators for more details.

Synopsis

Sem operators

type (@>) = Sem infix 2 Source #

type (@-) e = Sem '[e] infix 2 Source #

type (@~) m = Sem '[Lift m] infix 2 Source #

Infix equivalents of Sem with versions for specifiying list of effects (@>), single effect (@-) and single monad (@~) as effects of union. Use (>@>), (-@>) or (~@>) instead if you are not making any transformations on union and just want to use some members instead.

Examples:

Sem with list of multiple effects:

foo :: Sem (State Int : r) ()

can be written as:

foo :: State Int : r @> ()

Sem with list of one effect:

foo :: Sem '[State Int] ()

can be written as both (with the latter preferred):

foo :: '[State Int] @> ()

and:

foo :: State Int @- ()

where effect without list gets put into one automatically.

Sem with exactly one, lifted monad:

foo :: Sem '[Lift IO] ()

can be written simply as:

foo :: IO @~ ()

and will be automatically lifted and put into list.

Member operators

type (>@) es s = Members es (SemList s) => s infix 1 Source #

type (-@) e s = Member e (SemList s) => s infix 1 Source #

type (~@) m s = Member (Lift m) (SemList s) => s infix 1 Source #

Infix equivalents of Member(s) constraint used directly in return type, specifiying list of members (>@), single member (-@) or single monad (~@), meant to be paired with some of the Sem operators ((@>), (@-) and (@~)). Use (>@>), (-@>) or (~@>) instead if you are not making any transformations on union and just want to use some members instead.

Examples:

List of multiple members:

foo :: Members '[State Int, Input String] r => Sem (Output [String] : r) () -> Sem r ()

can be written as:

foo :: Output [String] : r @> () -> '[State Int, Input String] >@ r @> ()

One member:

foo :: Member (State Int) r => Sem (Output [String] : r) () -> Sem r ()

can be written as both (with the latter preferred):

foo :: Output [String] : r @> () -> '[State Int] >@ r @> ()

and:

foo :: Output [String] : r @> () -> State Int -@ r @> ()

Exactly one, lifted monad as a member:

foo :: Member (Lift IO) r => Sem (Output [String] : r) () -> Sem r ()

can be written simply as:

foo :: Output [String] : r @> () -> IO ~@ r @> ()

Combined operators

type (>@>) es a = forall r. Members es r => Sem r a infix 1 Source #

type (-@>) e a = forall r. Member e r => Sem r a infix 1 Source #

type (~@>) m a = forall r. Member (Lift m) r => Sem r a infix 1 Source #

Joined versions of one of (>@), (-@), (~@) and (@>) with implicit, hidden list of effects in union --- suited for actions that only use one Sem in their type.

Examples:

List of members over some Sem:

foo :: Members '[State String, Input Int] r => String -> Int -> Sem r ()

can be written as:

foo :: String -> Int -> '[State String, Input Int] >@> ()

Single member:

foo :: Member (Input Int) r => String -> Int -> Sem r ()

can be written as both (with the latter preferred):

foo :: String -> Int -> '[Input Int] >@> ()

and:

foo :: String -> Int -> Input Int -@> ()

Exactly one, lifted monad as a member:

foo :: Member (Lift IO) r => Sem r ()

can be written simply as:

foo :: IO ~@> ()