Safe Haskell | None |
---|---|
Language | Haskell2010 |
Support for building command-line programs, ranging from simple tools to long-running daemons.
This is intended to be used directly:
import Core.Program
the submodules are mostly there to group documentation.
Synopsis
- data Datum = Datum {}
- emptyDatum :: Datum
- newtype Trace = Trace Rope
- unTrace :: Trace -> Rope
- newtype Span = Span Rope
- unSpan :: Span -> Rope
- data Context τ = Context {
- programNameFrom :: MVar Rope
- terminalWidthFrom :: Int
- terminalColouredFrom :: Bool
- versionFrom :: Version
- initialConfigFrom :: Config
- initialExportersFrom :: [Exporter]
- commandLineFrom :: Parameters
- exitSemaphoreFrom :: MVar ExitCode
- startTimeFrom :: MVar Time
- verbosityLevelFrom :: MVar Verbosity
- outputChannelFrom :: TQueue (Maybe Rope)
- telemetryChannelFrom :: TQueue (Maybe Datum)
- telemetryForwarderFrom :: Maybe Forwarder
- currentDatumFrom :: MVar Datum
- applicationDataFrom :: MVar τ
- handleCommandLine :: Context τ -> IO (Context τ)
- handleVerbosityLevel :: Context τ -> IO (MVar Verbosity)
- handleTelemetryChoice :: Context τ -> IO (Context τ)
- data Exporter = Exporter {
- codenameFrom :: Rope
- setupConfigFrom :: Config -> Config
- setupActionFrom :: forall τ. Context τ -> IO Forwarder
- data Forwarder = Forwarder {
- telemetryHandlerFrom :: [Datum] -> IO ()
- data None = None
- isNone :: None -> Bool
- configure :: Version -> τ -> Config -> IO (Context τ)
- data Verbosity
- newtype Program τ α = Program (ReaderT (Context τ) IO α)
- unProgram :: Program τ α -> ReaderT (Context τ) IO α
- getContext :: Program τ (Context τ)
- fmapContext :: (τ1 -> τ2) -> Context τ1 -> IO (Context τ2)
- subProgram :: Context τ -> Program τ α -> IO α
- module Core.Program.Execute
- module Core.Program.Threads
- module Core.Program.Unlift
- module Core.Program.Metadata
- module Core.Program.Exceptions
- module Core.Program.Arguments
- module Core.Program.Logging
- module Core.Program.Notify
Executing a program
A top-level Program type giving you unified access to logging, concurrency, and more.
Carrier for spans and events while their data is being accumulated, and later sent down the telemetry channel. There is one of these in the Program monad's Context.
emptyDatum :: Datum Source #
Unique identifier for a trace. If your program is the top of an service stack
then you can use beginTrace
to generate a new
idenfifier for this request or iteration. More commonly, however, you will
inherit the trace identifier from the application or service which invokes
this program or request handler, and you can specify it by using
usingTrace
.
Unique identifier for a span. This will be generated by
encloseSpan
but for the case where you are
continuing an inherited trace and passed the identifier of the parent span you
can specify it using this constructor.
Internal context for a running program. You access this via actions in the
Program
monad. The principal item here is the user-supplied top-level
application data of type τ
which can be retrieved with
getApplicationState
and updated with
setApplicationState
.
handleCommandLine :: Context τ -> IO (Context τ) Source #
Process the command line options and arguments. If an invalid option is encountered or a [mandatory] argument is missing, then the program will terminate here.
Exporter | |
|
Implementation of a forwarder for structured logging of the telemetry channel.
Forwarder | |
|
A Program
with no user-supplied state to be threaded throughout the
computation.
The Core.Program.Execute framework makes your top-level application state available at the outer level of your process. While this is a feature that most substantial programs rely on, it is not needed for many simple tasks or when first starting out what will become a larger project.
This is effectively the unit type, but this alias is here to clearly signal a user-data type is not a part of the program semantics.
configure :: Version -> τ -> Config -> IO (Context τ) Source #
Initialize the programs's execution context. This takes care of various administrative actions, including setting up output channels, parsing command-line arguments (according to the supplied configuration), and putting in place various semaphores for internal program communication. See Core.Program.Arguments for details.
This is also where you specify the initial {blank, empty, default) value for
the top-level user-defined application state, if you have one. Specify None
if you aren't using this feature.
The verbosity level of the output logging subsystem. You can override the
level specified on the command-line by calling
setVerbosityLevel
from within the Program
monad.
The type of a top-level program.
You would use this by writing:
module Main where import Core.Program main ::IO
() main =execute
program
and defining a program that is the top level of your application:
program ::Program
None
()
Such actions are combinable; you can sequence them (using bind in do-notation) or run them in parallel, but basically you should need one such object at the top of your application.
Type variables
A Program
has a user-supplied application state and a return type.
The first type variable, τ
, is your application's state. This is an object
that will be threaded through the computation and made available to your code
in the Program
monad. While this is a common requirement of the outer code
layer in large programs, it is often not necessary in small programs or when
starting new projects. You can mark that there is no top-level application
state required using None
and easily change it later if your needs evolve.
The return type, α
, is usually unit as this effectively being called
directly from main
and Haskell programs have type
. That is, they
don't return anything; I/O having already happened as side effects.IO
()
Programs in separate modules
One of the quirks of Haskell is that it is difficult to refer to code in the
Main module when you've got a number of programs kicking around in a project
each with a main
function. One way of dealing with this is to put your
top-level Program
actions in a separate modules so you can refer to them
from test suites and example snippets.
Interoperating with the rest of the Haskell ecosystem
The Program
monad is a wrapper over IO
; at any point when you need to move
to another package's entry point, just use liftIO
. It's re-exported by
Core.System.Base for your convenience. Later, you might be interested in
unlifting back to Program; see Core.Program.Unlift.
Instances
Monad (Program τ) Source # | |
Functor (Program τ) Source # | |
MonadFail (Program τ) Source # | |
Defined in Core.Program.Context | |
Applicative (Program τ) Source # | |
MonadIO (Program τ) Source # | |
Defined in Core.Program.Context | |
MonadThrow (Program τ) Source # | |
Defined in Core.Program.Context | |
MonadCatch (Program τ) Source # | |
MonadMask (Program t) Source # | |
Defined in Core.Program.Context | |
MonadReader (Context τ) (Program τ) Source # | |
getContext :: Program τ (Context τ) Source #
Get the internal Context
of the running Program
. There is ordinarily no
reason to use this; to access your top-level application data τ
within the
Context
use getApplicationState
.
fmapContext :: (τ1 -> τ2) -> Context τ1 -> IO (Context τ2) Source #
Map a function over the underlying user-data inside the Context
, changing
it from typeτ1
to τ2
.
subProgram :: Context τ -> Program τ α -> IO α Source #
Run a subprogram from within a lifted IO
block.
module Core.Program.Execute
module Core.Program.Threads
module Core.Program.Unlift
module Core.Program.Metadata
module Core.Program.Exceptions
Command-line argument parsing
Including declaring what options your program accepts, generating help, and for more complex cases [sub]commands, mandatory arguments, and environment variable handling.
module Core.Program.Arguments
Logging facilities
Facilities for noting events through your program and doing debugging.
module Core.Program.Logging
There are a few common use cases which require a bit of wrapping to use effectively. Watching files for changes and taking action in the event of a change is one.
module Core.Program.Notify