develop7
2/13/2016 - 9:22 PM

Outline of a typelevel Haskell API for defining declarative, typesafe command line interfaces such that we could generate usage, man pages,

Outline of a typelevel Haskell API for defining declarative, typesafe command line interfaces such that we could generate usage, man pages, stub commands to test existing scripts without effects taking place of external dependencies, shell autocompletion, other documentation, or anything else you could image.

{-# LANGUAGE DataKinds, PolyKinds, TypeOperators #-}
{-# LANGUAGE TypeFamilies, FlexibleInstances, ScopedTypeVariables #-}
{-# LANGUAGE InstanceSigs #-}

module CmdLiner where

import GHC.TypeLits

-- Grammar
-- I care about the following:
-- * required/optional command line options
-- * required/optional envvars
-- * command line flags (not values necessary)
-- * positional arguments
-- * format of stdin capture (if relevant)
-- * format of stdout/stderr
-- * signals
data RequiredOpt name t     = RequiredOpt Symbol t
data RequiredEnv name t     = RequiredEnv Symbol t
data OptionalOpt name t def = OpionalOpt Symbol t def
data OptionalEnv name t def = OptionalEnv Symbol t def
data Flag name def          = Flag Symbol Bool
data Positional n v t       = Positional Nat Symbol t
data CaptureInput format t
data RenderOutput outstream format t
-- much like ^T in ping etc.
data Signal name = Signal Symbol

-- Combinators

data a :|| b    = a :|| b
data (a :: k) :>> (b :: *)

infixr 9 :>>
infixr 8 :||

-- Data types, type classes, families, etc.

data Proxy a = Proxy
class HasCli segments
type family Command segments :: *

-- TODO: define type instances for each part of possible type expression

-- Public interface

usage :: HasCli segments
      => Proxy segments
      -> Command segments
      -> [String]
      -> IO String
usage = undefined

-- TODO: validator, documenter, completer, ...

-- Example Declaration

-- My usage of API

data Format = JSON | YAML

type MyCli0 = RequiredOpt "output" Format
type MyCli1 = MyCli0 :>> Flag "no-ssl-verify" False
type MyCli2 = MyCli1 :>> Positional 1 "ARG1" Int