Small helpers for writing CLIs with effectful: effect constraints, concurrency, prompts, sleep/time, optparse-applicative helpers and a monad-logger bridge (stderr or OpenTelemetry).
Add the dependencies to package.yaml:
dependencies:
- effectful
- effectful-cli
- http-client-effectful
- monad-logger
- optparse-applicative
- textMinimal Main.hs, one hello subcommand logging to stderr:
{-# LANGUAGE OverloadedStrings #-}
import Control.Monad.Logger (logInfoN)
import Data.CLI.Class
import Data.CLI.Impl (execCLIParser, mkCommand, usage)
import Data.Error.CLI (CLIError, displayCLIError)
import Data.Text qualified as T
import Data.Text.IO qualified as T
import Effectful
import Effectful.Error.Static (runErrorNoCallStack)
import Effectful.HttpClient (runHttpClientTls)
import Effectful.Logger (runStderrLogger)
import Effectful.Reader.Static (runReader)
import Options.Applicative (hsubparser, strArgument)
data Config = Config
instance CLIConfig Config where
autoAccept _ = False
autoDecline _ = False
newtype Hello = Hello String
instance Command Config CLIError Hello where
cmdname _ = "hello"
doc _ = "greet someone"
parser _ = Hello <$> strArgument mempty
runner (Hello name) = logInfoN ("hello " <> T.pack name)
main :: IO ()
main = do
cmd <- execCLIParser
(usage "hello" "0.1.0" "greeter" (hsubparser (mkCommand (Hello ""))))
res <-
runEff .
runHttpClientTls .
runStderrLogger .
runReader Config .
runErrorNoCallStack @CLIError .
runErrorNoCallStack @CLIError $
runner cmd
either (T.putStrLn . displayCLIError) pure (res >>= id)stack run -- hello worldSee the Effectful.CLI module docs for the walkthrough.
stack build --testRequires just and a Hackage token in HACKAGE_KEY.
just docs # build Haddock tarball
just candidate # upload package + docs as Hackage candidate
just release # publish package + docs to Hackage