-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | An alternative to monads -- -- This "control-dsl" package is a toolkit to create extensible Domain -- Specific Languages in do-notation. -- -- See Control.Dsl for more information. @package control-dsl @version 0.2.0.2 module Control.Dsl.PolyCont -- | A use case of an ad-hoc polymorphic delimited continuation. -- -- Note that a PolyCont is not a polymorphic delimited -- continuation, since a PolyCont does not support answer type -- modification. class PolyCont k r a -- | Run as a CPS function . runPolyCont :: PolyCont k r a => k r' a -> (a -> r) -> r module Control.Dsl.Monadic -- | This Monadic keyword extracts the monadic value of a monadic -- expression. newtype Monadic m r a Monadic :: (m a) -> Monadic m r a instance GHC.Base.Monad m => Control.Dsl.PolyCont.PolyCont (Control.Dsl.Monadic.Monadic m) (m b) a module Control.Dsl.Empty data Empty r a [Empty] :: Empty r Void -- | Return an empty a, similar to empty. -- -- This empty function aims to be used as the last statement of a -- do block. empty :: PolyCont Empty a Void => a instance GHC.Base.Alternative m => Control.Dsl.PolyCont.PolyCont Control.Dsl.Empty.Empty (m a) Data.Void.Void module Control.Dsl.Return data Return r' r a [Return] :: r' -> Return r' r Void -- | Lift r to the answer type, similar to return. -- -- This return function aims to be used as the last statement of a -- do block. -- -- When return is present in a nested do block for -- when or unless, if the r is not -- (), it will create a Cont that performs early -- return, skipping the rest statements of the outer do -- notation. -- --
-- >>> :set -XTypeOperators -- -- >>> :set -XRebindableSyntax -- -- >>> import Prelude hiding ((>>), (>>=), return, fail) -- -- >>> import Control.Dsl -- -- >>> import Control.Dsl.Return -- -- >>> import Control.Dsl.Yield -- -- >>> import Control.Dsl.Cont -- -- >>> import Control.Dsl.Empty ---- --
-- >>> :{
-- earlyGenerator :: Bool -> Cont [String] Integer
-- earlyGenerator earlyReturn = do
-- Yield "inside earlyGenerator"
-- when earlyReturn $ do
-- Yield "early return"
-- return 1
-- Yield "normal return"
-- return 0
-- :}
--
--
--
-- >>> :{
-- earlyGeneratorTest :: [String]
-- earlyGeneratorTest = do
-- Yield "before earlyGenerator"
-- i <- earlyGenerator True
-- Yield "after earlyGenerator"
-- Yield $ "the return value of earlyGenerator is " ++ show i
-- empty
-- :}
--
--
-- -- >>> earlyGeneratorTest -- ["before earlyGenerator","inside earlyGenerator","early return","after earlyGenerator","the return value of earlyGenerator is 1"] --return :: PolyCont Return r' r Void => r' -> r -- | Lift an IOError to the answer type, similar to fail. -- -- This fail function aims to be used as the last statement of a -- do block. fail :: PolyCont Return IOError r Void => String -> r instance Control.Dsl.PolyCont.PolyCont (Control.Dsl.Return.Return r) r Data.Void.Void instance GHC.Base.Applicative m => Control.Dsl.PolyCont.PolyCont (Control.Dsl.Return.Return r) (m r) Data.Void.Void module Control.Dsl.Cont -- | A type alias to Cont for a deeply nested delimited -- continuation. -- --
-- >>> :set -XTypeOperators
--
-- >>> :set -XRebindableSyntax
--
-- >>> import Prelude hiding ((>>), (>>=), return, fail)
--
-- >>> import Control.Dsl
--
-- >>> import Control.Dsl.Yield
--
-- >>> import Control.Dsl.Empty
--
-- >>> :{
-- f :: IO () !! [Integer] !! [String] !! [Double]
-- f = do
-- Yield "foo"
-- Yield 0.5
-- Yield 42
-- empty
-- :}
--
type !! = Cont
newtype Cont r a
Cont :: (a -> r) -> r -> Cont r a
[runCont] :: Cont r a -> (a -> r) -> r
when :: Bool -> Cont r () -> Cont r ()
unless :: () => Bool -> Cont r () -> Cont r ()
guard :: PolyCont Empty r Void => Bool -> Cont r ()
instance Control.Dsl.PolyCont.PolyCont k r a => Control.Dsl.PolyCont.PolyCont k (Control.Dsl.Cont.Cont r a') a
instance Control.Dsl.PolyCont.PolyCont (Control.Dsl.Return.Return r) (Control.Dsl.Cont.Cont r' r) Data.Void.Void
instance Control.Dsl.PolyCont.PolyCont Control.Dsl.Empty.Empty r Data.Void.Void => Control.Dsl.PolyCont.PolyCont Control.Dsl.Empty.Empty (Control.Dsl.Cont.Cont r a) Data.Void.Void
module Control.Dsl.Shift
-- | A keyword to extract the value of a CPS function .
newtype Shift r' r a
Shift :: ((a -> r') -> r') -> Shift r' r a
instance Control.Dsl.PolyCont.PolyCont (Control.Dsl.Shift.Shift r) r a
module Control.Dsl.State.Put
data Put s r a
[Put] :: s -> Put s r ()
instance Control.Dsl.PolyCont.PolyCont (Control.Dsl.State.Put.Put s) (Control.Dsl.State.State.State s r) ()
module Control.Dsl.State.Get
data Get r a
[Get] :: forall s r. Get r s
instance Control.Dsl.PolyCont.PolyCont Control.Dsl.State.Get.Get (Control.Dsl.State.State.State s r) s
-- | This module provides keywords to Put and Get the value
-- of multiple mutable variables in a do block.
module Control.Dsl.State
-- | The type that holds states, which is defined as a plain function.
--
-- -- >>> :set -XFlexibleContexts -- -- >>> :set -XTypeApplications -- -- >>> :set -XRebindableSyntax -- -- >>> import Prelude hiding ((>>), (>>=), return, fail) -- -- >>> import Control.Dsl -- -- >>> import Control.Dsl.Cont -- -- >>> import Control.Dsl.Shift -- -- >>> import Control.Dsl.State -- -- >>> import Data.Sequence (Seq, (|>)) -- -- >>> import qualified Data.Sequence -- -- >>> import Data.Foldable ---- -- The following append function Gets a Seq -- String state, appends s to the Seq, and -- Puts the new Seq to the updated state. -- --
-- >>> :{
-- append s = do
-- buffer <- Get @(Seq String)
-- Put $ buffer |> s
-- Cont ($ ())
-- :}
--
--
-- ($ ()) creates a CPS function , which can be then converted
-- to Conts.
--
-- A formatter appends String to its internal
-- buffer, and return the concatenated buffer.
--
--
-- >>> :{
-- formatter = do
-- append "x="
-- d <- Get @Double
-- append $ show d
-- append ",y="
-- i <- Get @Integer
-- append $ show i
-- buffer <- Get @(Seq String)
-- return $ concat buffer
-- :}
--
--
-- -- >>> x = 0.5 :: Double -- -- >>> y = 42 :: Integer -- -- >>> initialBuffer = Data.Sequence.empty :: Seq String -- -- >>> formatter x y initialBuffer :: String -- "x=0.5,y=42" ---- -- Note that formatter accepts arbitrary order of the -- parameters, or additional unused parameters. -- --
-- >>> formatter "unused parameter" initialBuffer y x :: String -- "x=0.5,y=42" --type State = (->) -- | This module contains the Yield data type and related -- Dsl instances. -- -- The Yield data type can be used to create generators, similar -- to the yield keyword in C#, Python, and ECMAScript. module Control.Dsl.Yield -- | This Yield keyword produces an element in a list generator -- --
-- >>> :set -XTypeApplications
--
-- >>> :set -XRebindableSyntax
--
-- >>> import Prelude hiding ((>>), (>>=), return, fail)
--
-- >>> import Control.Dsl
--
-- >>> import Data.Word
--
-- >>> import Data.Bits
--
-- >>> :{
-- randomGenerator :: Word32 -> [Word32]
-- randomGenerator seed =
-- do let tmp1 = xor seed $ shiftL seed 13
-- let tmp2 = xor tmp1 $ shiftR tmp1 17
-- let tmp3 = xor tmp2 $ shiftL tmp2 5
-- Yield tmp3
-- randomGenerator tmp3
-- :}
--
--
-- -- >>> take 5 $ randomGenerator 2463534242 -- [723471715,2497366906,2064144800,2008045182,3532304609] --data Yield x r a [Yield] :: x -> Yield x r () instance Control.Dsl.PolyCont.PolyCont (Control.Dsl.Yield.Yield x) [x] () instance Control.Dsl.PolyCont.PolyCont (Control.Dsl.Yield.Yield x) (Control.Dsl.Cont.Cont r [x]) () -- | This Control.Dsl module and its submodules provide a toolkit to -- create extensible Domain Specific Languages in do-notation. -- -- A DSL do block contains heterogeneous statements from -- different vendors. A statement can be defined as a GADT, interpreted -- by a Dsl type class instance, either effectfully or purely. -- -- A DSL do block is abstract. When creating the block, the type -- class requirements is automatically inferred. Therefore, the data -- structures and implementation of interpreters can be switched by -- providing different instances. -- --
-- >>> :set -XRebindableSyntax -- -- >>> import Prelude hiding ((>>), (>>=), return, fail) -- -- >>> import Control.Dsl ---- --
-- >>> data MaxLengthConfig r a where MaxLengthConfig :: MaxLengthConfig r Int -- -- >>> data GetLine r a where GetLine :: GetLine r String -- -- >>> data PutStrLn r a where PutStrLn :: String -> PutStrLn r () ---- --
-- >>> :{
-- dslBlock = do
-- maxLength <- MaxLengthConfig
-- line1 <- GetLine
-- line2 <- GetLine
-- when (length line1 + length line2 > maxLength) $ do
-- PutStrLn "The input is too long"
-- fail "Illegal input"
-- PutStrLn ("The input is " ++ line1 ++ " and " ++ line2)
-- return ()
-- :}
--
--
-- The above dslBlock function creates an abstract code block of
-- DSL from keywords and some built-in control flow functions.
--
-- Keywords and the result statement return and fail are
-- ad-hoc polymorphic delimited continuations, interpreted by
-- PolyCont, which can be automatically inferred:
--
-- -- >>> :type dslBlock -- dslBlock -- :: (PolyCont (Return IOError) r Void, PolyCont (Return ()) r Void, -- PolyCont MaxLengthConfig r Int, PolyCont GetLine r [Char], -- PolyCont PutStrLn r ()) => -- r ---- --
-- >>> type PureInterpreter = Int -> [String] -> Cont [String] IOError ---- --
-- >>> :{
-- instance PolyCont MaxLengthConfig PureInterpreter Int where
-- runPolyCont MaxLengthConfig = runPolyCont Get
-- :}
--
--
--
-- >>> :{
-- instance PolyCont PutStrLn PureInterpreter () where
-- runPolyCont (PutStrLn s) = runPolyCont (Yield s)
-- :}
--
--
--
-- >>> :{
-- instance PolyCont (Return ()) PureInterpreter Void where
-- runPolyCont (Return ()) = runPolyCont Empty
-- :}
--
--
-- The above three PolyCont instances are implemented as
-- forwarders to other existing keywords.
--
--
-- >>> :{
-- instance PolyCont GetLine PureInterpreter String where
-- runPolyCont k = runCont $ do
-- x : xs <- Get @[String]
-- Put xs
-- return x
-- :}
-- ...
--
--
-- The PolyCont instance for GetLine is implemented as a
-- Cont that contains a DSL do block of atomic
-- statements.
--
-- -- >>> runPurely = dslBlock :: PureInterpreter ---- --
-- >>> errorHandler e = ["(handled) " ++ show e] -- -- >>> runCont (runPurely 80 ["LINE_1", "LINE_2"]) errorHandler -- ["The input is LINE_1 and LINE_2"] ---- --
-- >>> longInput = [replicate 40 '*', replicate 41 '*'] -- -- >>> runCont (runPurely 80 longInput) errorHandler -- ["The input is too long","(handled) user error (Illegal input)"] ---- --
-- >>> runCont (runPurely 80 ["ONE_LINE"]) errorHandler -- ["(handled) user error (Pattern match failure in do expression at <interactive>..."] ---- --
-- >>> type EffectfulInterpreter = Handle -> IO () ---- --
-- >>> :{
-- instance PolyCont GetLine EffectfulInterpreter String where
-- runPolyCont GetLine = runCont $ do
-- h <- Get
-- line <- Monadic (hGetLine h)
-- return line
-- :}
--
--
-- Monadic is a built-in keyword to perform old-fashioned monadic
-- action in a DSL do block.
--
-- Other keywords can be used together with Monadic. No monad
-- transformer is required.
--
--
-- >>> :{
-- instance PolyCont MaxLengthConfig (IO ()) Int where
-- runPolyCont MaxLengthConfig f = f 80
-- :}
--
--
--
-- >>> :{
-- instance PolyCont PutStrLn (IO ()) () where
-- runPolyCont (PutStrLn s) = (Prelude.>>=) (putStrLn s)
-- :}
--
--
--
-- >>> :{
-- instance PolyCont (Return IOError) (IO ()) Void where
-- runPolyCont (Return e) _ = hPutStrLn stderr (show e)
-- :}
--
--
-- -- >>> runEffectfully = dslBlock :: EffectfulInterpreter ---- --
-- >>> :{
-- withSystemTempFile "tmp-input-file" $ \_ -> \h -> do
-- Monadic $ hPutStrLn h "LINE_1"
-- Monadic $ hPutStrLn h "LINE_2"
-- Monadic $ hSeek h AbsoluteSeek 0
-- runEffectfully h
-- :}
-- The input is LINE_1 and LINE_2
--
module Control.Dsl
-- | Witnesses a use case of a statement in a do block.
--
-- -- >>> :set -XGADTs -- -- >>> :set -XMultiParamTypeClasses -- -- >>> :set -XFlexibleInstances -- -- >>> :set -XFlexibleContexts -- -- >>> :set -XRebindableSyntax -- -- >>> :set -XTypeApplications -- -- >>> import qualified Prelude -- -- >>> import Prelude hiding ((>>), (>>=), return, fail) -- -- >>> import Control.Dsl -- -- >>> import Control.Dsl.State.Get -- -- >>> import Control.Dsl.Yield -- -- >>> import Control.Dsl.Return -- -- >>> import Data.Void ---- --
-- >>> :{
-- f = do
-- Yield "foo"
-- config <- Get @Bool
-- when config $ do
-- Yield "bar"
-- return ()
-- return "baz"
-- :}
--
--
-- f is a do block that contains keywords of
-- Get, Yield, and return. With the help of built-in
-- PolyCont instances for those keywords, f can be used
-- as a function that accepts a boolean parameter.
--
-- -- >>> f False :: [String] -- ["foo","baz"] ---- --
-- >>> f True :: [String] -- ["foo","bar","baz"] ---- -- In fact, f can be any type as long as PolyCont -- instances for involved keywords are provided. -- --
-- >>> :type f -- f :: (PolyCont (Yield [Char]) r (), -- PolyCont (Return [Char]) r Void, PolyCont Get r Bool) => -- r ---- -- For example, f can be interpreted as an impure IO -- (), providing the following instances: -- --
-- >>> :{
-- instance PolyCont (Yield String) (IO ()) () where
-- runPolyCont (Yield a) = (Prelude.>>=) (putStrLn $ "Yield " ++ a)
-- instance PolyCont Get (IO ()) Bool where
-- runPolyCont Get f = putStrLn "Get" Prelude.>> f False
-- instance PolyCont (Return String) (IO ()) Void where
-- runPolyCont (Return r) _ = putStrLn $ "Return " ++ r
-- :}
--
--
-- -- >>> f :: IO () -- Yield foo -- Get -- Return baz --class Dsl k r a -- | The implementation of <- statements in a do -- block, which forwards to runCont if k is Cont, -- otherwise forwards to runPolyCont from PolyCont. (>>=) :: Dsl k r a => k r a -> a -> r -> r (=<<) :: Dsl k r a => a -> r -> k r a -> r (>=>) :: Dsl k r a => t -> k r a -> a -> r -> t -> r (<=<) :: Dsl k r a => t -> k r a -> a -> r -> t -> r -- | The implementation of statements with no value in a do block. (>>) :: Dsl k r b => k r b -> r -> r forever :: Dsl k r a => k r a -> r ifThenElse :: () => Bool -> p -> p -> p -- | Lift r to the answer type, similar to return. -- -- This return function aims to be used as the last statement of a -- do block. -- -- When return is present in a nested do block for -- when or unless, if the r is not -- (), it will create a Cont that performs early -- return, skipping the rest statements of the outer do -- notation. -- --
-- >>> :set -XTypeOperators -- -- >>> :set -XRebindableSyntax -- -- >>> import Prelude hiding ((>>), (>>=), return, fail) -- -- >>> import Control.Dsl -- -- >>> import Control.Dsl.Return -- -- >>> import Control.Dsl.Yield -- -- >>> import Control.Dsl.Cont -- -- >>> import Control.Dsl.Empty ---- --
-- >>> :{
-- earlyGenerator :: Bool -> Cont [String] Integer
-- earlyGenerator earlyReturn = do
-- Yield "inside earlyGenerator"
-- when earlyReturn $ do
-- Yield "early return"
-- return 1
-- Yield "normal return"
-- return 0
-- :}
--
--
--
-- >>> :{
-- earlyGeneratorTest :: [String]
-- earlyGeneratorTest = do
-- Yield "before earlyGenerator"
-- i <- earlyGenerator True
-- Yield "after earlyGenerator"
-- Yield $ "the return value of earlyGenerator is " ++ show i
-- empty
-- :}
--
--
-- -- >>> earlyGeneratorTest -- ["before earlyGenerator","inside earlyGenerator","early return","after earlyGenerator","the return value of earlyGenerator is 1"] --return :: PolyCont Return r' r Void => r' -> r -- | Lift an IOError to the answer type, similar to fail. -- -- This fail function aims to be used as the last statement of a -- do block. fail :: PolyCont Return IOError r Void => String -> r when :: Bool -> Cont r () -> Cont r () unless :: () => Bool -> Cont r () -> Cont r () guard :: PolyCont Empty r Void => Bool -> Cont r ()