-- | 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 -- 'Data.Function.&' runConsoleIO -- 'Data.Function.&' 'Polysemy.Random.runRandomIO' -- 'Data.Function.&' 'runM' -- -- -- program :: Members \'[Random, ConsoleIO] r => Sem r () -- program :: \'['Polysemy.Random', ConsoleIO] '>@>' () -- program = do -- writeStrLn "It works! Write something:" -- val <- readStrLn -- writeStrLn '$' "Here it is!: " '++' val -- num <- 'Polysemy.Random.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. module Polysemy.Operators ( -- * 'Sem' operators type (@>) , type (@-) , type (@~) -- $SemOperators , -- * 'Member' operators type (>@) , type (-@) , type (~@) -- $MemberOperators , -- * Combined operators type (>@>) , type (-@>) , type (~@>) -- $CombinedOperators ) where import Polysemy -- Miscellaneous ------------------------------------------------------------- -- | Gets list of effects from 'Sem'. type family SemList s where SemList (Sem r _) = r -- Operators ----------------------------------------------------------------- -- $SemOperators -- 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' ('Polysemy.State.State' 'Int' : r) () -- @ -- -- can be written as: -- -- @ -- foo :: 'Polysemy.State.State' 'Int' : r '@>' () -- @ -- -- 'Sem' with list of one effect: -- -- @ -- foo :: 'Sem' \'['Polysemy.State.State' 'Int'] () -- @ -- -- can be written as both (with the latter preferred): -- -- @ -- foo :: \'['Polysemy.State.State' 'Int'] '@>' () -- @ -- -- and: -- -- @ -- foo :: 'Polysemy.State.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. infix 2 @>, @-, @~ type (@>) = Sem type (@-) e = Sem '[e] type (@~) m = Sem '[Lift m] -- $MemberOperators -- 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' \'['Polysemy.State.State' 'Int', 'Polysemy.Input.Input' 'String'] r => 'Sem' ('Polysemy.Output.Output' ['String'] : r) () -> 'Sem' r () -- @ -- -- can be written as: -- -- @ -- foo :: 'Polysemy.Output.Output' ['String'] : r '@>' () -> \'['Polysemy.State.State' 'Int', 'Polysemy.Input.Input' 'String'] '>@' r '@>' () -- @ -- -- One member: -- -- @ -- foo :: 'Member' ('Polysemy.State.State' 'Int') r => 'Sem' ('Polysemy.Output.Output' ['String'] : r) () -> 'Sem' r () -- @ -- -- can be written as both (with the latter preferred): -- -- @ -- foo :: 'Polysemy.Output.Output' ['String'] : r '@>' () -> \'['Polysemy.State.State' 'Int'] '>@' r '@>' () -- @ -- -- and: -- -- @ -- foo :: 'Polysemy.Output.Output' ['String'] : r '@>' () -> 'Polysemy.State.State' 'Int' '-@' r '@>' () -- @ -- -- __Exactly__ one, lifted monad as a member: -- -- @ -- foo :: 'Member' ('Lift' 'IO') r => 'Sem' ('Polysemy.Output.Output' ['String'] : r) () -> 'Sem' r () -- @ -- -- can be written simply as: -- -- @ -- foo :: 'Polysemy.Output.Output' ['String'] : r '@>' () -> 'IO' '~@' r '@>' () -- @ infix 1 >@, -@, ~@ type (>@) es s = Members es (SemList s) => s type (-@) e s = Member e (SemList s) => s type (~@) m s = Member (Lift m) (SemList s) => s -- $CombinedOperators -- 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' \'['Polysemy.State.State' 'String', 'Polysemy.Input.Input' 'Int'] r => 'String' -> 'Int' -> 'Sem' r () -- @ -- -- can be written as: -- -- @ -- foo :: 'String' -> 'Int' -> \'['Polysemy.State.State' 'String', 'Polysemy.Input.Input' 'Int'] '>@>' () -- @ -- -- Single member: -- -- @ -- foo :: 'Member' ('Polysemy.Input.Input' 'Int') r => 'String' -> 'Int' -> 'Sem' r () -- @ -- -- can be written as both (with the latter preferred): -- -- @ -- foo :: 'String' -> 'Int' -> \'['Polysemy.Input.Input' 'Int'] '>@>' () -- @ -- -- and: -- -- @ -- foo :: 'String' -> 'Int' -> 'Polysemy.Input.Input' 'Int' '-@>' () -- @ -- -- __Exactly__ one, lifted monad as a member: -- -- @ -- foo :: 'Member' ('Lift' 'IO') r => 'Sem' r () -- @ -- -- can be written simply as: -- -- @ -- foo :: 'IO' '~@>' () -- @ infix 1 >@>, -@>, ~@> type (>@>) es a = forall r. Members es r => Sem r a type (-@>) e a = forall r. Member e r => Sem r a type (~@>) m a = forall r. Member (Lift m) r => Sem r a