Safe Haskell | None |
---|---|

Language | Haskell2010 |

- eve_ :: App () -> IO ()
- type App a = AppT AppState IO a
- type Action state a = ActionT AppState state IO a
- runApp :: Monad m => AppT base m a -> ActionT base zoomed m a
- runAction :: (HasStates t, Functor (Zoomed m c), Default s, Typeable s, Zoom m n s t) => m c -> n c
- exit :: (Monad m, HasStates s) => ActionT s zoomed m ()
- dispatchEvent :: forall result eventType m base zoomed. (HasEvents base, Monoid result, Monad m, Typeable m, Typeable eventType, Typeable result) => eventType -> ActionT base zoomed m result
- dispatchEvent_ :: forall eventType m base zoomed. (HasEvents base, Monad m, Typeable m, Typeable eventType) => eventType -> ActionT base zoomed m ()
- addListener :: forall result eventType m base zoomed. (HasEvents base, Monad m, Typeable m, Typeable eventType, Typeable result, Monoid result) => (eventType -> AppT base m result) -> ActionT base zoomed m ListenerId
- addListener_ :: forall result eventType m base zoomed. (HasEvents base, Monad m, Typeable m, Typeable eventType, Typeable result, Monoid result) => (eventType -> AppT base m result) -> ActionT base zoomed m ()
- removeListener :: (HasEvents base, Monad m) => ListenerId -> ActionT base zoomed m ()
- data Listener
- data ListenerId
- asyncEventProvider :: (HasEvents base, MonadIO m, Typeable m) => (EventDispatcher -> IO ()) -> ActionT base zoomed m ()
- type EventDispatcher = forall event. Typeable event => event -> IO ()
- afterInit :: forall base m a. (Monad m, HasEvents base, Typeable m) => AppT base m a -> AppT base m ()
- beforeEvent :: forall base zoomed m a. (Monad m, HasEvents base, Typeable m) => AppT base m a -> ActionT base zoomed m ListenerId
- beforeEvent_ :: (Monad m, HasEvents base, Typeable m) => AppT base m a -> ActionT base zoomed m ()
- afterEvent :: forall base zoomed m a. (Monad m, HasEvents base, Typeable m) => AppT base m a -> ActionT base zoomed m ListenerId
- afterEvent_ :: (Monad m, HasEvents base, Typeable m) => AppT base m a -> ActionT base zoomed m ()
- onExit :: forall base zoomed m a. (HasEvents base, Typeable m, Monad m) => AppT base m a -> ActionT base zoomed m ()
- makeStateLens :: (HasStates s, Typeable myState, Default myState) => Lens' myState a -> Lens' s a
- data AppState
- eve :: (MonadIO m, Typeable m) => AppT AppState m () -> m AppState
- type AppT s m a = ActionT s s m a
- data ActionT base zoomed m a
- runActionOver :: Zoom m n s t => LensLike' (Zoomed m c) t s -> m c -> n c
- class HasStates s where
- type States = Map TypeRep StateWrapper
- stateLens :: forall a e. (Typeable a, Default a, HasStates e) => Lens' e a
- class (Typeable s, HasStates s) => HasEvents s
- dispatchLocalEvent :: forall result eventType m s. (MonadState s m, HasEvents s, Monoid result, Typeable m, Typeable eventType, Typeable result) => eventType -> m result
- dispatchLocalEvent_ :: forall eventType m s. (MonadState s m, HasEvents s, Typeable m, Typeable eventType) => eventType -> m ()
- addLocalListener :: forall result eventType m s. (MonadState s m, HasEvents s, Typeable m, Typeable eventType, Typeable result, Monoid result) => (eventType -> m result) -> m ListenerId
- addLocalListener_ :: forall result eventType m s. (MonadState s m, HasEvents s, Typeable m, Typeable eventType, Typeable result, Monoid result) => (eventType -> m result) -> m ()
- removeLocalListener :: (MonadState s m, HasEvents s) => ListenerId -> m ()
- asyncActionProvider :: (MonadIO m, HasStates base, Typeable m, Typeable base) => ((AppT base m () -> IO ()) -> IO ()) -> ActionT base zoomed m ()
- dispatchEventAsync :: (Typeable event, MonadIO m, Typeable m, HasEvents base) => IO event -> ActionT base zoomed m ()
- dispatchActionAsync :: (MonadIO m, HasStates base, Typeable m, Typeable base) => IO (AppT base m ()) -> ActionT base zoomed m ()

# Documentation

This documentation is split into parts based on complexity. For most applications you'll need only the Simple section. You'll find useful tools in the Advanced section once you've got a simple app up and running.

# Simple

Eve allows you to build your applications incrementally, adding more complexity as you need it. For this reason, many of the types are more general than you'll likely need. This can be a bit confusing, but here's a few tips:

- Both
`Action`

and`App`

unify with`ActionT`

. You may use them in place of`ActionT`

. When in doubt, use`App`

. - When you see vague references to monads
`m`

or`n`

, you can use`App`

or`Action`

in its place. - Simple Apps assume that you use the provided
`AppState`

and it is "baked in" to the`Action`

and`App`

types. Wherever you see

you can mentally replace`HasStates`

s`s`

with`AppState`

.

## Running your App

eve_ :: App () -> IO () Source #

This runs your application. It accepts an initialization block (which
is the same as any other `App`

or `Action`

block, which
registers event listeners and event providers. Note that nothing in this
block should use `dispatchEvent`

since it is possible that not all listeners
have yet been registered. You can use the `afterInit`

trigger to dispatch
any events you'd like to run at start-up.
Here's a simple example:

import Eve initialize = App () initialize = do addListener_ myListener asyncEventProvider myProvider startApp :: IO () startApp = eve_ initialize

## Working with Actions

runApp :: Monad m => AppT base m a -> ActionT base zoomed m a Source #

Allows you to run an `App`

inside of an `Action`

runAction :: (HasStates t, Functor (Zoomed m c), Default s, Typeable s, Zoom m n s t) => m c -> n c Source #

This runs an `Action MyState a` over the MyState which is
stored in the currently focused state and returns the result.
Use `runActionOver`

if you'd like to specify a particular MyState
which is accessed by a Lens or Traversal.

exit :: (Monad m, HasStates s) => ActionT s zoomed m () Source #

Tells the application to quit. This triggers `onExit`

listeners
following the current event loop.

## Dispatching Events

dispatchEvent :: forall result eventType m base zoomed. (HasEvents base, Monoid result, Monad m, Typeable m, Typeable eventType, Typeable result) => eventType -> ActionT base zoomed m result Source #

Runs any listeners registered for the provided event with the provided event;

You can also `query`

listeners and receive a (`Monoid`

al) result.

data RequestNames = GetFirstName | GetLastName provideName1, provideName2 :: RequestNames -> App [String] provideName1 GetFirstNames = return ["Bob"] provideName1 GetLastNames = return ["Smith"] provideName2 GetFirstNames = return ["Sally"] provideName2 GetLastNames = return ["Jenkins"] -- Note that if we registered an action of type 'GetFirstName -> ()' it would NOT -- be run in response to the following 'dispatchEvent', since it's type doesn't match. greetNames :: App [String] greetNames = do addListener_ provideName1 addListener_ provideName2 firstNames <- dispatchEvent GetFirstName lastNames <- dispatchEvent GetLastName liftIO $ print firstNames -- ["Bob", "Sally"] liftIO $ print lastNames -- ["Smith", "Jenkins"]

dispatchEvent_ :: forall eventType m base zoomed. (HasEvents base, Monad m, Typeable m, Typeable eventType) => eventType -> ActionT base zoomed m () Source #

## Event Listeners

addListener :: forall result eventType m base zoomed. (HasEvents base, Monad m, Typeable m, Typeable eventType, Typeable result, Monoid result) => (eventType -> AppT base m result) -> ActionT base zoomed m ListenerId Source #

Registers an `Action`

or `App`

to respond to an event.

For a given use: `addListener myListener`

, `myListener`

might have the type `MyEvent -> App a`

it will register the function `myListener`

to be run in response to a `dispatchEvent (MyEvent eventInfo)`

and will be provided `(MyEvent eventInfo)`

as an argument.

This returns a `ListenerId`

which corresponds to the registered listener for use with `removeListener`

addListener_ :: forall result eventType m base zoomed. (HasEvents base, Monad m, Typeable m, Typeable eventType, Typeable result, Monoid result) => (eventType -> AppT base m result) -> ActionT base zoomed m () Source #

removeListener :: (HasEvents base, Monad m) => ListenerId -> ActionT base zoomed m () Source #

Unregisters a listener referred to by the provided `ListenerId`

data ListenerId Source #

An opaque reverence to a specific registered event-listener.
A ListenerId is used only to remove listeners later with `removeListener`

.

## Asynchronous Helpers

asyncEventProvider :: (HasEvents base, MonadIO m, Typeable m) => (EventDispatcher -> IO ()) -> ActionT base zoomed m () Source #

This allows long-running IO processes to provide Events to the application asyncronously.

Don't let the type signature confuse you; it's much simpler than it seems.

Let's break it down:

Using the `EventDispatcher`

type with asyncEventProvider requires the `RankNTypes`

language pragma.

This type as a whole represents a function which accepts an `EventDispatcher`

and returns an `IO`

;
the dispatcher itself accepts data of ANY `Typeable`

type and emits it as an event.

When you call `asyncEventProvider`

you pass it a function which accepts a `dispatch`

function as an argument
and then calls it with various events within the resulting `IO`

.

Note that this function calls forkIO internally, so there's no need to do that yourself.

Here's an example which fires a `Timer`

event every second.

{-# language RankNTypes #-} data Timer = Timer myTimer :: EventDispatcher -> IO () myTimer dispatch = forever $ dispatch Timer >> threadDelay 1000000 myInit :: App () myInit = asyncEventProvider myTimer

type EventDispatcher = forall event. Typeable event => event -> IO () Source #

This is a type alias to make defining your functions for use with `asyncEventProvider`

easier;
It represents the function your event provider function will be passed to allow dispatching
events. Using this type requires the `RankNTypes`

language pragma.

## Built-in Event Listeners

afterInit :: forall base m a. (Monad m, HasEvents base, Typeable m) => AppT base m a -> AppT base m () Source #

Registers an action to be performed directly following the Initialization phase.

At this point any listeners in the initialization block have run, so you may `dispatchEvent`

s here.

beforeEvent :: forall base zoomed m a. (Monad m, HasEvents base, Typeable m) => AppT base m a -> ActionT base zoomed m ListenerId Source #

Registers an action to be performed BEFORE each async event is processed phase.

beforeEvent_ :: (Monad m, HasEvents base, Typeable m) => AppT base m a -> ActionT base zoomed m () Source #

afterEvent :: forall base zoomed m a. (Monad m, HasEvents base, Typeable m) => AppT base m a -> ActionT base zoomed m ListenerId Source #

Registers an action to be performed AFTER each event phase.

afterEvent_ :: (Monad m, HasEvents base, Typeable m) => AppT base m a -> ActionT base zoomed m () Source #

onExit :: forall base zoomed m a. (HasEvents base, Typeable m, Monad m) => AppT base m a -> ActionT base zoomed m () Source #

Registers an action to be run before shutdown. Any asynchronous combinators used in this block will NOT be run.

## Working with State

All application-provided states are stored in the same
Map; keyed by their `TypeRep`

. This means that if more than one state
uses the same type then they'll conflict and overwrite each-other (this is less of a
problem than you're probably thinking). This is easily solved by simply
using a newtype around any types you haven't defined yourself.
For example if your application stores a counter as an Int, wrap it in your own custom
`Counter`

newtype when storing it. If you wish to store multiple copies of a given state
simply store them in a list or map, then store that container as your state.

Because states are stored by their `TypeRep`

, they must
define an instance of `Typeable`

, In most cases it's
unnecessary, but GHC can derive this for you with `deriving Typeable`

.

It is also required for all states to define an instance of
`Default`

, this is because accessing an extension which has not
yet been stored will result in the default value.

If there's no default value that makes sense for your type, you can define
a default of `Nothing`

and pattern-match on its value when you
access it.

Here's an example of defining your own state:

data SimpleState = SimpleState { _myString :: String } makeLenses ''SimpleState instance Default SimpleState where def = SimpleState "default"

makeStateLens :: (HasStates s, Typeable myState, Default myState) => Lens' myState a -> Lens' s a Source #

A utility which creates a state-nested version of a lens.
If you pass this function a lens from your state to one of its fields,
it will return a lens which can be used within an `App`

or `Action`

.

The resulting lens will be of type: `newLens :: HasStates s => Lens' s MyState`

Or if you prefer, you may wish to specify the state it operates over more specifically
to prevent using the lens where it was not originally planned. For instance:
`newLens :: Lens' AppState MyState`

data SimpleState = SimpleState { _myString :: String } makeLenses ''SimpleState instance Default SimpleState where def = SimpleState "default" myStringStateLens :: HasStates s => Lens' s String myStringStateLens = makeStateLens myString myAction :: App () myAction = do myStringStateLens .= "Hi!" str <- use myStringStateLens liftIO $ print str -- "Hi!"

For more complex Prisms or Traversals you can write your own using
`stateLens`

# Advanced

This section provides tools which become relevant when working on more complex apps. You can customize which states you operate over, embed events in nested states, and choose a custom base monad for the mtl stack.

## Actions

data ActionT base zoomed m a Source #

Base Action type. Allows paramaterization over application state, zoomed state and underlying monad.

Monad m => MonadState zoomed (ActionT base zoomed m) Source # | |

MonadTrans (ActionT base zoomed) Source # | |

Monad n => MonadFree (AppF base n) (ActionT base zoomed n) Source # | |

Monad m => Monad (ActionT base zoomed m) Source # | |

Monad m => Functor (ActionT base zoomed m) Source # | |

Monad m => Applicative (ActionT base zoomed m) Source # | |

MonadIO m => MonadIO (ActionT base zoomed m) Source # | |

Monad m => Zoom (ActionT base s m) (ActionT base t m) s t Source # | |

(Monoid a, Monad m) => Monoid (ActionT base zoomed m a) Source # | |

type Zoomed (ActionT base zoomed m) Source # | |

runActionOver :: Zoom m n s t => LensLike' (Zoomed m c) t s -> m c -> n c Source #

Given a `Lens`

or `Traversal`

or LensLike from Control.Lens
which focuses the state (t) of an `Action`

from a base state (s),
this will convert `Action t a -> Action s a`

so that it may be run
in an `Action s a`

## States

stateLens :: forall a e. (Typeable a, Default a, HasStates e) => Lens' e a Source #

A polymorphic lens which accesses stored states.
It returns the default value (`def`

) if a state has not yet been set.

## Local Events

The local versions of the event functions are the same as the others (`dispatchEvent`

,
`addListener`

, `removeListener`

) however they operate on a per-state basis.
This means that if you define a custom state which implements `HasEvents`

then you may use these functions inside an `Action CustomState` to dispatch events
to ONLY the listners within that specific instance of that state. Note that
these listeners and events are distinct on the value level, not just the type level,
so if you have multiple copies of CustomState in your app, they each have their
own disjoint event listeners.

class (Typeable s, HasStates s) => HasEvents s Source #

A typeclass to ensure people don't dispatch events to states which shouldn't accept them.

To allow dispatching events in an action over your state simply define the empty instance:

instance HasEvents MyState where -- Don't need anything here.

dispatchLocalEvent :: forall result eventType m s. (MonadState s m, HasEvents s, Monoid result, Typeable m, Typeable eventType, Typeable result) => eventType -> m result Source #

A local version of `dispatchEvent`

.
The local version dispatches the event in the context of the current `Action`

,
If you don't know what this means, you probably want `dispatchEvent`

instead

dispatchLocalEvent_ :: forall eventType m s. (MonadState s m, HasEvents s, Typeable m, Typeable eventType) => eventType -> m () Source #

addLocalListener :: forall result eventType m s. (MonadState s m, HasEvents s, Typeable m, Typeable eventType, Typeable result, Monoid result) => (eventType -> m result) -> m ListenerId Source #

The local version of `addListener`

. It will register a listener within an `Action`

s local event
context. If you don't know what this means you probably want `addListener`

instead.

addLocalListener_ :: forall result eventType m s. (MonadState s m, HasEvents s, Typeable m, Typeable eventType, Typeable result, Monoid result) => (eventType -> m result) -> m () Source #

removeLocalListener :: (MonadState s m, HasEvents s) => ListenerId -> m () Source #

The local version of `removeListener`

.
This removes a listener from an `Action`

s event context. If you don't
know what this means you probably want `removeListener`

instead.

## Async

asyncActionProvider :: (MonadIO m, HasStates base, Typeable m, Typeable base) => ((AppT base m () -> IO ()) -> IO ()) -> ActionT base zoomed m () Source #

This allows long-running IO processes to provide `Action`

s to the application asyncronously.

`asyncEventProvider`

is simpler to use, however `asyncActionProvider`

provides
more power and expressivity. When in doubt, `asyncEventProvider`

probably meets
your needs.

Don't let the type signature confuse you; it's much simpler than it seems.

Let's break it down:

When you call `asyncActionProvider`

you pass it a function which accepts a `dispatch`

function as an argument
and then calls it with various `Action`

s within the resulting `IO`

. The
`dispatch`

function it is passed will have type `(App () -> IO ())`

Note that this function calls forkIO internally, so there's no need to do that yourself.

Here's an example:

data Timer = Timer myTimer :: (App () -> IO ()) -> IO () myTimer dispatch = forever $ dispatch (myInt += 1) >> threadDelay 1000000 myInit :: App () myInit = asyncActionProvider myTimer

dispatchEventAsync :: (Typeable event, MonadIO m, Typeable m, HasEvents base) => IO event -> ActionT base zoomed m () Source #

This function takes an IO which results in some event, it runs the IO asynchronously, THEN dispatches the event. Note that only the code which generates the event is asynchronous, not any responses to the event itself.

dispatchActionAsync :: (MonadIO m, HasStates base, Typeable m, Typeable base) => IO (AppT base m ()) -> ActionT base zoomed m () Source #

Dispatch an action which is generated by some IO. Note that state of the application may have changed
between calling `dispatchActionAsync`

and running the resulting `Action`