packages feed

commander-cli 0.6.2.0 → 0.7.0.0

raw patch · 4 files changed

+169/−132 lines, 4 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Options.Commander: instance Control.Monad.IO.Class.MonadIO m => Control.Monad.IO.Class.MonadIO (Options.Commander.CommanderT state m)
- Options.Commander: instance Control.Monad.Trans.Class.MonadTrans (Options.Commander.CommanderT state)
- Options.Commander: instance GHC.Base.Functor m => GHC.Base.Functor (Options.Commander.CommanderT state m)
- Options.Commander: instance GHC.Base.Monad m => GHC.Base.Alternative (Options.Commander.CommanderT state m)
- Options.Commander: instance GHC.Base.Monad m => GHC.Base.Applicative (Options.Commander.CommanderT state m)
- Options.Commander: instance GHC.Base.Monad m => GHC.Base.Monad (Options.Commander.CommanderT state m)
- Options.Commander: instance Options.Commander.Unrender a => Options.Commander.Unrender (GHC.Maybe.Maybe a)
+ Control.Monad.Commander: Action :: (state -> m (CommanderT state m a, state)) -> CommanderT state m a
+ Control.Monad.Commander: Defeat :: CommanderT state m a
+ Control.Monad.Commander: Victory :: a -> CommanderT state m a
+ Control.Monad.Commander: data CommanderT state m a
+ Control.Monad.Commander: instance Control.Monad.IO.Class.MonadIO m => Control.Monad.IO.Class.MonadIO (Control.Monad.Commander.CommanderT state m)
+ Control.Monad.Commander: instance Control.Monad.Trans.Class.MonadTrans (Control.Monad.Commander.CommanderT state)
+ Control.Monad.Commander: instance GHC.Base.Functor m => GHC.Base.Functor (Control.Monad.Commander.CommanderT state m)
+ Control.Monad.Commander: instance GHC.Base.Monad m => GHC.Base.Alternative (Control.Monad.Commander.CommanderT state m)
+ Control.Monad.Commander: instance GHC.Base.Monad m => GHC.Base.Applicative (Control.Monad.Commander.CommanderT state m)
+ Control.Monad.Commander: instance GHC.Base.Monad m => GHC.Base.Monad (Control.Monad.Commander.CommanderT state m)
+ Control.Monad.Commander: runCommanderT :: Monad m => CommanderT state m a -> state -> m (Maybe a)

Files

Test.hs view
@@ -29,7 +29,7 @@   >> envTest   >> envOptTest   >> envOptDefTest-+  >> eitherSwitchTest rawProg :: ProgramT Raw IO Bool rawProg = raw (pure True) @@ -128,3 +128,24 @@   testMaybeBool =<< fmap not <$> test (envOptDefProg "POP" (== ("POOP" :: String))) (State mempty mempty mempty)   setEnv "CORPUS" "POOP"   testMaybeBool =<< test (envOptDefProg "POP" (== ("POOP" :: String))) (State mempty mempty mempty)++eitherSwitchProg :: (x -> Bool) -> (y -> Bool) -> ProgramT (Arg "xy" (Either x y) & Raw) IO (Either Bool Bool)+eitherSwitchProg xpred ypred = arg \case { Left x -> raw (pure (Left $ xpred x)); Right y -> raw (pure (Right $ ypred y)) }++eitherSwitchTest  :: IO ()+eitherSwitchTest = do+  let example = eitherSwitchProg (== (10 :: Int)) (== ("Hello" :: String))+  let ploof a c = runCommanderT (run example) (State a mempty mempty) >>= c+  ploof ["10"] \case+    Just (Left True) -> pure ()+    _ -> exitFailure+  ploof ["Hello"] \case+    Just (Right True) -> pure ()+    _ -> exitFailure+  ploof ["Poop"] \case+    Just (Right False) -> pure ()+    _ -> exitFailure+  ploof [] \case+    Nothing -> pure ()+    _ -> exitFailure+
commander-cli.cabal view
@@ -1,7 +1,7 @@ cabal-version:       2.4  name:                commander-cli-version:             0.6.2.0+version:             0.7.0.0 synopsis:            A command line argument/option parser library built around a monadic metaphor description:         A command line argument/option parser library built around a monadic metaphor. homepage:            https://github.com/SamuelSchlesinger/commander-cli@@ -15,7 +15,7 @@ extra-source-files:  CHANGELOG.md, README.md  library-  exposed-modules:     Options.Commander+  exposed-modules:     Options.Commander, Control.Monad.Commander   other-extensions:    ViewPatterns,                        DerivingVia,                        StandaloneDeriving,
+ src/Control/Monad/Commander.hs view
@@ -0,0 +1,141 @@+{-# LANGUAGE DeriveFunctor #-}+{- |+Module: Control.Monad.Commander+Description: A monad for stateful, backtracking computations+Copyright: (c) Samuel Schlesinger 2020+License: MIT+Maintainer: sgschlesinger@gmail.com+Stability: experimental+Portability: POSIX, Windows+-}+module Control.Monad.Commander (+  -- ** The CommanderT Monad+  {- |+    The 'CommanderT' monad is how your CLI programs are interpreted by 'run'.+    It has the ability to backtrack and it maintains some state.+  -}+  CommanderT(Action, Defeat, Victory), runCommanderT,+) where++import Control.Monad (ap)+import Control.Monad.Trans (MonadTrans, lift, liftIO, MonadIO)+import Control.Applicative (Alternative(empty, (<|>)))++-- | A 'CommanderT' action is a metaphor for a military commander. At each+-- step, we have a new 'Action' to take, or we could have experienced+-- 'Defeat', or we can see 'Victory'. While a real life commander+-- worries about moving his troops around in order to achieve a victory in+-- battle, a 'CommanderT' worries about iteratively transforming a state +-- to find some value. We will deal with the subset of these actions where+-- every function must decrease the size of the state, as those are the+-- actions for which this is a monad.+data CommanderT state m a+  = Action (state -> m (CommanderT state m a, state))+  | Defeat+  | Victory a+  deriving Functor++-- | We can run a 'CommanderT' action on a state and see if it has+-- a successful campaign.+runCommanderT :: Monad m +              => CommanderT state m a +              -> state +              -> m (Maybe a)+runCommanderT (Action action) state = do+  (action', state') <- action state+  m <- runCommanderT action' state'+  return m+runCommanderT Defeat _ = return Nothing+runCommanderT (Victory a) _ = return (Just a)++instance (Monad m) => Applicative (CommanderT state m) where+  (<*>) = ap+  pure = Victory++instance MonadTrans (CommanderT state) where+  lift ma = Action $ \state -> do+    a <- ma+    return (pure a, state)++instance MonadIO m => MonadIO (CommanderT state m) where+  liftIO ma = Action $ \state -> do+    a <- liftIO ma+    return (pure a, state)++-- Return laws:+-- Goal: return a >>= k = k a+-- Proof: return a >>= k +--      = Victory a >>= k +--      = k a +--      = k a+-- Goal: m >>= return = m+-- Proof:+--   Case 1: Defeat >>= return = Defeat+--   Case 2: Victory a >>= return +--         = Victory a+--   Case 3: Action action >>= return+--         = Action $ \state -> do+--             (action', state') <- action state+--             return (action' >>= return, state')+--+-- Case 3 serves as an inductive proof only if action' is a strictly smaller action+-- than action!+--+--  Bind laws:+--  Goal: m >>= (\x -> k x >>= h) = (m >>= k) >>= h+--  Proof: +--    Case 1: Defeat >>= _ = Defeat+--    Case 2: Victory a >>= (\x -> k x >>= f)+--          = k a >>= f+--          = (Victory a >>= k) >>= f+--    Case 3: Action action >>= (\x -> k x >>= h)+--          = Action $ \state -> do+--              (action', state') <- action state+--              return (action' >>= (\x -> k x >>= h), state')+--          = Action $ \state -> do+--              (action', state') <- action state+--              return ((action' >>= k) >>= h, state') -- by IH+--    On the other hand,+--            (Action action >>= k) >>= h+--          = Action (\state -> do+--              (action', state') <- action state+--              return (action' >>= k, state') >>= h+--          = Action $ \state -> do+--              (action', state') <- action state+--              return ((action' >>= k) >>= h, state')+--               +--   This completes our proof for the case when these are finite.+--   Basically, we require that the stream an action produces is strictly+--   smaller than any other streams, for all state inputs. The ways that we+--   use this monad transformer satisify this constraint. If this+--   constraint is not met, many of our functions will return bottom.+--+--   We can certainly have functions that operate on these things and+--   change them safely, without violating this constraint. All of the+--   functions that we define on CommanderT programs preserve this+--   property.+--+--   An example of a violating term might be:+--+--   violator :: CommanderT state m+--   violator = Action (\state -> return (violator, state))+--+--   The principled way to include this type would be to parameterize it by+--   a natural number and have that natural number decrease over time, but+--   to enforce that in Haskell we couldn't have the monad instance+--   anyways. This is the way to go for now, despite the type violating the+--   monad laws potentially for infinite inputs. +instance Monad m => Monad (CommanderT state m) where+  Defeat >>= _ = Defeat+  Victory a >>= f = f a+  Action action >>= f = Action $ \state -> do+    (action', state') <- action state+    return (action' >>= f, state')++instance Monad m => Alternative (CommanderT state m) where+  empty = Defeat +  Defeat <|> a = a +  v@(Victory _) <|> _ = v+  Action action <|> p = Action $ \state -> do+    (action', state') <- action state +    return (action' <|> p, state')
src/Options/Commander.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE BlockArguments #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE DeriveGeneric #-}@@ -14,7 +15,6 @@ {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE RankNTypes #-}-{-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE GADTs #-}@@ -146,6 +146,7 @@ import qualified Data.ByteString as SBS import qualified Data.ByteString.Char8 as BS8 import qualified Data.ByteString.Lazy as LBS+import Control.Monad.Commander  -- | A class for interpreting command line arguments into Haskell types. class Typeable t => Unrender t where@@ -170,17 +171,10 @@ instance Unrender () where   unrender = unrenderSmall -instance Unrender a => Unrender (Maybe a) where-  unrender x = justCase x <|> nothingCase x where-    justCase x' = do-      x'' <- stripPrefix "Just " x'-      return (unrender x'')-    nothingCase x' = if x' == "Nothing" then return Nothing else Nothing- instance (Unrender a, Unrender b) => Unrender (Either a b) where   unrender x = leftCase x <|> rightCase x where-    leftCase  = fmap Left  . unrender <=< stripPrefix "Left "-    rightCase = fmap Right . unrender <=< stripPrefix "Right "+    leftCase  = fmap Left  . unrender+    rightCase = fmap Right . unrender  instance Unrender Bool where   unrender = unrenderSmall@@ -258,125 +252,6 @@ -- options or arguments fails, runs the second, otherwise failing. data a + b infixr 2 +---- | A 'CommanderT' action is a metaphor for a military commander. At each--- step, we have a new 'Action' to take, or we could have experienced--- 'Defeat', or we can see 'Victory'. While a real life commander--- worries about moving his troops around in order to achieve a victory in--- battle, a 'CommanderT' worries about iteratively transforming a state --- to find some value. We will deal with the subset of these actions where--- every function must decrease the size of the state, as those are the--- actions for which this is a monad.-data CommanderT state m a-  = Action (state -> m (CommanderT state m a, state))-  | Defeat-  | Victory a-  deriving Functor---- | We can run a 'CommanderT' action on a state and see if it has--- a successful campaign.-runCommanderT :: Monad m -              => CommanderT state m a -              -> state -              -> m (Maybe a)-runCommanderT (Action action) state = do-  (action', state') <- action state-  m <- runCommanderT action' state'-  return m-runCommanderT Defeat _ = return Nothing-runCommanderT (Victory a) _ = return (Just a)--instance (Monad m) => Applicative (CommanderT state m) where-  (<*>) = ap-  pure = Victory--instance MonadTrans (CommanderT state) where-  lift ma = Action $ \state -> do-    a <- ma-    return (pure a, state)--instance MonadIO m => MonadIO (CommanderT state m) where-  liftIO ma = Action $ \state -> do-    a <- liftIO ma-    return (pure a, state)---- Return laws:--- Goal: return a >>= k = k a--- Proof: return a >>= k ---      = Victory a >>= k ---      = k a ---      = k a--- Goal: m >>= return = m--- Proof:---   Case 1: Defeat >>= return = Defeat---   Case 2: Victory a >>= return ---         = Victory a---   Case 3: Action action >>= return---         = Action $ \state -> do---             (action', state') <- action state---             return (action' >>= return, state')------ Case 3 serves as an inductive proof only if action' is a strictly smaller action--- than action!------  Bind laws:---  Goal: m >>= (\x -> k x >>= h) = (m >>= k) >>= h---  Proof: ---    Case 1: Defeat >>= _ = Defeat---    Case 2: Victory a >>= (\x -> k x >>= f)---          = k a >>= f---          = (Victory a >>= k) >>= f---    Case 3: Action action >>= (\x -> k x >>= h)---          = Action $ \state -> do---              (action', state') <- action state---              return (action' >>= (\x -> k x >>= h), state')---          = Action $ \state -> do---              (action', state') <- action state---              return ((action' >>= k) >>= h, state') -- by IH---    On the other hand,---            (Action action >>= k) >>= h---          = Action (\state -> do---              (action', state') <- action state---              return (action' >>= k, state') >>= h---          = Action $ \state -> do---              (action', state') <- action state---              return ((action' >>= k) >>= h, state')---               ---   This completes our proof for the case when these are finite.---   Basically, we require that the stream an action produces is strictly---   smaller than any other streams, for all state inputs. The ways that we---   use this monad transformer satisify this constraint. If this---   constraint is not met, many of our functions will return bottom.------   We can certainly have functions that operate on these things and---   change them safely, without violating this constraint. All of the---   functions that we define on CommanderT programs preserve this---   property.------   An example of a violating term might be:------   violator :: CommanderT state m---   violator = Action (\state -> return (violator, state))------   The principled way to include this type would be to parameterize it by---   a natural number and have that natural number decrease over time, but---   to enforce that in Haskell we couldn't have the monad instance---   anyways. This is the way to go for now, despite the type violating the---   monad laws potentially for infinite inputs. -instance Monad m => Monad (CommanderT state m) where-  Defeat >>= _ = Defeat-  Victory a >>= f = f a-  Action action >>= f = Action $ \state -> do-    (action', state') <- action state-    return (action' >>= f, state')--instance Monad m => Alternative (CommanderT state m) where-  empty = Defeat -  Defeat <|> a = a -  v@(Victory _) <|> _ = v-  Action action <|> p = Action $ \state -> do-    (action', state') <- action state -    return (action' <|> p, state')  -- | This is the 'State' that the 'CommanderT' library uses for its role in -- this library. It is not inlined, because that does nothing but obfuscate