Idris2/docs/source/app/introapp.rst
Edwin Brady 2206692533 Some documentation updates
Remove things from CONTRIBUTING that are done, and initial App
documentation (though it could use more examples).
2020-05-25 09:03:08 +01:00

109 lines
3.2 KiB
ReStructuredText

Introducing App
===============
``App`` is declared as below, in a module ``Control.App``, which is part of
the ``base`` libraries.
It is parameterised by an implicit
``Path`` (which states whether the program's execution path
is linear or might throw
exceptions), which has a ``default`` value that the program
might throw, and an ``Environment``
(which gives a list of exception types which can be thrown, and is
a synonym for ``List Type``):
.. code-block:: idris
data App : {default MayThrow l : Path} ->
(e : Environment) -> Type -> Type
It serves the same purpose as ``IO``, but supports throwing and catching
exceptions, and allows us to define more constrained interfaces parameterised
by the environment ``e``.
e.g. a program which supports console IO:
.. code-block:: idris
hello : Console e => App e ()
hello = putStrLn "Hello, App world!"
We can use this in a complete program as follows:
.. code-block:: idris
module Main
import Control.App
import Control.App.Console
hello : Console e => App e ()
hello = putStrLn "Hello, App world!"
main : IO ()
main = run hello
Or, a program which supports console IO and carries an ``Int`` state,
labelled ``Counter``:
.. code-block:: idris
data Counter : Type where
helloCount : (Console e, State Counter Int e) => App e ()
helloCount = do c <- get Counter
put Counter (c + 1)
putStrLn "Hello, counting world"
c <- get Counter
putStrLn ("Counter " ++ show c)
To run this as part of a complete program, we need to initialise the state.
.. code-block:: idris
main : IO ()
main = run (new 93 helloCount)
For convenience, we can list multiple interfaces in one go, using a function
``Has``, defined in ``Control.App``, to compute the interface constraints:
.. code-block:: idris
helloCount : Has [Console, State Counter Int] e => App e ()
0 Has : List (a -> Type) -> a -> Type
Has [] es = ()
Has (e :: es') es = (e es, Has es' es)
The purpose of ``Path`` is to state whether a program can throw
exceptions, so that we can know where it is safe to reference linear
resources. It is declared as follows:
.. code-block:: idris
data Path = MayThrow | NoThrow
The type of ``App`` states that ``MayThrow`` is the default.
We expect this to be the most
common case. After all, realistically, most operations have possible failure
modes, especially those which interact with the outside world.
The ``0`` on the declaration of ``Has`` indicates that it can only
be run in an erased context, so it will never be run at run-time.
To run an ``App`` inside ``IO``, we use an initial
environment ``Init`` (recall that an ``Environment`` is a
``List Type``):
.. code-block:: idris
Init : Environment
Init = [Void]
run : App {l} Init a -> IO a
Generalising the ``Path`` parameter with ``l``
means that we can invoke ``run`` for any application, whether the ``Path``
is ``NoThrow`` or ``MayThrow``. But, in practice, all applications
given to ``run`` will not throw at the top level, because the only
exception type available is the empty type ``Void``. Any exceptions
will have been introduced and handled inside the ``App``.