Safe Haskell | Trustworthy |
---|---|
Language | Haskell98 |
Synopsis
- newtype Wizard backend a = Wizard (MaybeT (Free backend) a)
- type PromptString = String
- data (f :+: g) w
- class (Functor sub, Functor sup) => sub :<: sup
- inject :: g :<: f => g (Free f a) -> Free f a
- class Run a b where
- runAlgebra :: b (a v) -> a v
- run :: (Functor f, Monad b, Run b f) => Wizard f a -> b (Maybe a)
- data Output w = Output String w
- data OutputLn w = OutputLn String w
- data Line w = Line PromptString (String -> w)
- data LinePrewritten w = LinePrewritten PromptString String String (String -> w)
- data Password w = Password PromptString (Maybe Char) (String -> w)
- data Character w = Character PromptString (Char -> w)
- data ArbitraryIO w = ArbitraryIO (IO a) (a -> w)
Documentation
newtype Wizard backend a Source #
A Wizard b a
is a conversation with the user via back-end b
that will result in a data type a
, or may fail.
A Wizard
is made up of one or more "primitives" (see below), composed using the Applicative
,
Monad
and Alternative
instances. The Alternative
instance is, as you might expect, a maybe-style cascade.
If the first wizard fails, the next one is tried. mzero
can be used to induce failure directly.
The Wizard
constructor is exported here for use when developing backends, but it is better for end-users to
simply pretend that Wizard
is an opaque data type. Don't depend on this unless you have no other choice.
Wizard
s are, internally, just a maybe transformer over a free monad built from some coproduct of functors,
each of which is a primitive action.
Instances
Functor backend => Monad (Wizard backend) Source # | |
Functor backend => Functor (Wizard backend) Source # | |
Functor backend => Applicative (Wizard backend) Source # | |
Defined in System.Console.Wizard.Internal pure :: a -> Wizard backend a # (<*>) :: Wizard backend (a -> b) -> Wizard backend a -> Wizard backend b # liftA2 :: (a -> b -> c) -> Wizard backend a -> Wizard backend b -> Wizard backend c # (*>) :: Wizard backend a -> Wizard backend b -> Wizard backend b # (<*) :: Wizard backend a -> Wizard backend b -> Wizard backend a # | |
ArbitraryIO :<: b => MonadIO (Wizard b) Source # | |
Defined in System.Console.Wizard | |
Functor backend => Alternative (Wizard backend) Source # | |
Functor backend => MonadPlus (Wizard backend) Source # | |
type PromptString = String Source #
A string for a prompt
data (f :+: g) w infixr 9 Source #
Coproduct of two functors
Instances
(Run b f, Run b g) => Run b (f :+: g) Source # | |
Defined in System.Console.Wizard.Internal runAlgebra :: (f :+: g) (b v) -> b v Source # | |
(Functor f, Functor g, Functor h, f :<: g) => f :<: (h :+: g) Source # | |
Defined in System.Console.Wizard.Internal | |
(Functor f, Functor g) => f :<: (f :+: g) Source # | |
Defined in System.Console.Wizard.Internal | |
(Functor f, Functor g) => Functor (f :+: g) Source # | |
class (Functor sub, Functor sup) => sub :<: sup Source #
Subsumption of two functors. You shouldn't define any of your own instances of this when writing back-ends, rely only on GeneralizedNewtypeDeriving.
inj
Instances
inject :: g :<: f => g (Free f a) -> Free f a Source #
Injection function for free monads, see "Data Types a la Carte" from Walter Swierstra, http://www.cs.ru.nl/~W.Swierstra/Publications/DataTypesALaCarte.pdf
A class for implementing actions on a backend. E.g Run IO Output provides an interpreter for the Output action in the IO monad.
runAlgebra :: b (a v) -> a v Source #
Instances
run :: (Functor f, Monad b, Run b f) => Wizard f a -> b (Maybe a) Source #
Run a wizard using some back-end.
Each of the following functors is a primitive action. A back-end provides interpreters for these actions using the Run
class,
Instances
Functor Output Source # | |
Run IO Output Source # | |
Defined in System.Console.Wizard.BasicIO | |
Output :<: Haskeline Source # | |
Defined in System.Console.Wizard.Haskeline | |
Output :<: BasicIO Source # | |
Defined in System.Console.Wizard.BasicIO | |
Output :<: Pure Source # | |
Defined in System.Console.Wizard.Pure | |
Run (InputT IO) Output Source # | |
Defined in System.Console.Wizard.Haskeline | |
Run (State PureState) Output Source # | |
Defined in System.Console.Wizard.Pure |
Instances
Functor OutputLn Source # | |
Run IO OutputLn Source # | |
Defined in System.Console.Wizard.BasicIO | |
OutputLn :<: Haskeline Source # | |
Defined in System.Console.Wizard.Haskeline | |
OutputLn :<: BasicIO Source # | |
Defined in System.Console.Wizard.BasicIO | |
OutputLn :<: Pure Source # | |
Defined in System.Console.Wizard.Pure | |
Run (InputT IO) OutputLn Source # | |
Defined in System.Console.Wizard.Haskeline | |
Run (State PureState) OutputLn Source # | |
Defined in System.Console.Wizard.Pure |
Line PromptString (String -> w) |
Instances
Functor Line Source # | |
Run IO Line Source # | |
Defined in System.Console.Wizard.BasicIO | |
Line :<: Haskeline Source # | |
Defined in System.Console.Wizard.Haskeline | |
Line :<: BasicIO Source # | |
Defined in System.Console.Wizard.BasicIO | |
Line :<: Pure Source # | |
Defined in System.Console.Wizard.Pure | |
Run (InputT IO) Line Source # | |
Defined in System.Console.Wizard.Haskeline | |
Run (State PureState) Line Source # | |
Defined in System.Console.Wizard.Pure |
data LinePrewritten w Source #
LinePrewritten PromptString String String (String -> w) |
Instances
Functor LinePrewritten Source # | |
Defined in System.Console.Wizard.Internal fmap :: (a -> b) -> LinePrewritten a -> LinePrewritten b # (<$) :: a -> LinePrewritten b -> LinePrewritten a # | |
LinePrewritten :<: Haskeline Source # | |
Defined in System.Console.Wizard.Haskeline inj :: LinePrewritten a -> Haskeline a | |
Run (InputT IO) LinePrewritten Source # | |
Defined in System.Console.Wizard.Haskeline runAlgebra :: LinePrewritten (InputT IO v) -> InputT IO v Source # |
Password PromptString (Maybe Char) (String -> w) |
Character PromptString (Char -> w) |
Instances
Functor Character Source # | |
Run IO Character Source # | |
Defined in System.Console.Wizard.BasicIO | |
Character :<: Haskeline Source # | |
Defined in System.Console.Wizard.Haskeline | |
Character :<: BasicIO Source # | |
Defined in System.Console.Wizard.BasicIO | |
Character :<: Pure Source # | |
Defined in System.Console.Wizard.Pure | |
Run (InputT IO) Character Source # | |
Defined in System.Console.Wizard.Haskeline | |
Run (State PureState) Character Source # | |
Defined in System.Console.Wizard.Pure |
data ArbitraryIO w Source #
ArbitraryIO (IO a) (a -> w) |
Instances
Functor ArbitraryIO Source # | |
Defined in System.Console.Wizard.Internal fmap :: (a -> b) -> ArbitraryIO a -> ArbitraryIO b # (<$) :: a -> ArbitraryIO b -> ArbitraryIO a # | |
Run IO ArbitraryIO Source # | |
Defined in System.Console.Wizard.BasicIO runAlgebra :: ArbitraryIO (IO v) -> IO v Source # | |
ArbitraryIO :<: Haskeline Source # | |
Defined in System.Console.Wizard.Haskeline inj :: ArbitraryIO a -> Haskeline a | |
ArbitraryIO :<: BasicIO Source # | |
Defined in System.Console.Wizard.BasicIO inj :: ArbitraryIO a -> BasicIO a | |
Run (InputT IO) ArbitraryIO Source # | |
Defined in System.Console.Wizard.Haskeline runAlgebra :: ArbitraryIO (InputT IO v) -> InputT IO v Source # |
A short tutorial on writing backends.
Backends consist of two main components:
- A monad,
M
, in which the primitive actions are interpreted.Run
instances specify an interpreter for each supported action, e.gRun M Output
will specify an interpreter for theOutput
primitive action in the monad M. - A newtype, e.g
Backend a
, which is a functor, usually implemented by wrapping a coproduct of all supported features. '(:<:)' instances, theFunctor
instance, and theRun
instance are provided by generalized newtype deriving.
As an example, suppose I am writing a back-end to IO
, like System.Console.Wizard.BasicIO. I want to support basic input and output,
and arbitrary IO, so I declare instances for Run
for the IO
monad:
instance Run IO Output where runAlgebra (Output s w) = putStr s >> w instance Run IO OutputLn where runAlgebra (OutputLn s w) = putStrLn s >> w instance Run IO Line where runAlgebra (Line s w) = getLine >>= w instance Run IO Character where runAlgebra (Character s w) = getChar >>= w instance Run IO ArbitraryIO where runAlgebra (ArbitraryIO iov f) = iov >>= f
And then I would define the newtype for the backend, which we can call MyIOBackend
:
newtype MyIOBackend a = MyIOBackend ((Output :+: OutputLn :+: Line :+: Character :+: ArbitraryIO) a) deriving ( Functor, Run IO , (:<:) Output , (:<:) OutputLn , (:<:) Line , (:<:) Character , (:<:) ArbitraryIO )
A useful convenience is to provide a simple identity function to serve as a type coercion:
myIOBackend :: Wizard MyIOBackend a -> Wizard MyIOBackend a myIOBackend = id
One additional primitive action that I might want to include is the ability to clear the screen at a certain point. So, we define a new data type for the action:
data ClearScreen w = ClearScreen w deriving Functor -- via -XDeriveFunctor
And a "smart" constructor for use by the user:
clearScreen :: (ClearScreen :<: b) => Wizard b () clearScreen = Wizard $ lift $ inject (ClearScreen (Pure ()))
(These smart constructors all follow a similar pattern. See the source of System.Console.Wizard for more examples)
And then we define an interpreter for it:
instance Run IO ArbitraryIO where runAlgebra (ClearScreen f) = clearTheScreen >> f
Now, we can use this as-is simply by directly extending our back-end:
foo :: Wizard (ClearScreen :+: MyIOBackend) foo = clearScreen >> output "Hello World!"
Or, we could modify MyIOBackend
to include the extension directly.
For custom actions that return output, the definition looks slightly different. Here is the definition of Line:
data Line w = Line (PromptString) (String -> w) deriving Functor -- via -XDeriveFunctor
And the smart constructor looks like this:
line :: (Line :<: b) => PromptString -> Wizard b String line s = Wizard $ lift $ inject (Line s Pure)