packages feed

interact 0.1.0.0 → 0.2.0.0

raw patch · 5 files changed

+284/−93 lines, 5 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ System.IO.Interact: pRepl :: Repl a b => String -> (a -> b) -> IO ()
+ System.IO.Interact: pRepl' :: forall a b. (Eq a, Read a, Show b) => String -> a -> (a -> b) -> IO ()
+ System.IO.Interact: pReplFold :: (Read a, Show b) => String -> (b -> a -> b) -> b -> IO ()
+ System.IO.Interact: pReplFold' :: (Eq a, Read a, Show b) => String -> a -> (b -> a -> b) -> b -> IO ()
+ System.IO.Interact: pReplState :: ReplState a b s => String -> (a -> b) -> s -> IO ()
+ System.IO.Interact: pReplState' :: forall a b s. (Eq a, Read a, Show b) => String -> a -> (a -> State s b) -> s -> IO ()
- System.IO.Interact: replFold :: forall a b. (Read a, Show b) => (b -> a -> b) -> b -> IO ()
+ System.IO.Interact: replFold :: (Read a, Show b) => (b -> a -> b) -> b -> IO ()
- System.IO.Interact: replFold' :: forall a b. (Eq a, Read a, Show b) => a -> (b -> a -> b) -> b -> IO ()
+ System.IO.Interact: replFold' :: (Eq a, Read a, Show b) => a -> (b -> a -> b) -> b -> IO ()
- System.IO.Interact: replState' :: forall a b s. (Eq a, Read a, Show b) => a -> (a -> State s b) -> s -> IO ()
+ System.IO.Interact: replState' :: (Eq a, Read a, Show b) => a -> (a -> State s b) -> s -> IO ()

Files

CHANGELOG.md view
@@ -1,3 +1,6 @@+# 0.2.0.0+- Added REPLs with prompt: `pRepl`, `pReplState` and `pReplFold`+ # 0.1.0.0  - Initial release
README.md view
@@ -5,4 +5,4 @@ - REPL with state from plain state function or with State monad - REPL-fold from two-arguments functions, with the accumulator in the first argument -See docs and usage examples on hackage: http://hackage.haskell.org/package/interact+See [docs and usage examples on hackage](http://hackage.haskell.org/package/interact/docs/System-IO-Interact.html).
interact.cabal view
@@ -1,13 +1,13 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.31.2.+-- This file has been generated from package.yaml by hpack version 0.33.0. -- -- see: https://github.com/sol/hpack ----- hash: e748dd99c68b9226eb30badff724ac9fa45b88d6da96bd80cd0f7b4c8a715aa3+-- hash: bb6a95dd5b3a368a73376daef53e8c118da2814ceeb01a4ea04afd7bbf80c90d  name:           interact-version:        0.1.0.0+version:        0.2.0.0 synopsis:       instantly create REPL from any function description:    This module provides functions to create interactive REPLs:                 .
src/System/IO/Interact.hs view
@@ -26,21 +26,28 @@     Repl,     repl,     repl',+    pRepl,+    pRepl',      -- * REPL with state     ReplState,     replState,     replState',+    pReplState,+    pReplState',      -- * REPL-fold     replFold,     replFold',+    pReplFold,+    pReplFold',   ) where +import Control.Exception (bracket) import Control.Monad.State-import Data.Either import Data.Maybe+import System.IO import Text.Read (readMaybe)  -- | 'Repl' typeclass with polymorphic stateless function 'repl' to interactively@@ -71,73 +78,121 @@   -- > sqrSqrt (x:xs) = x^2 : sqrt x : sqrSqrt xs   -- > repl sqrSqrt   repl :: (a -> b) -> IO ()+  repl = pRepl "" +  -- | 'pRepl' is 'repl' with prompt+  --+  -- __Example__:+  --+  -- > pRepl ">" (sqrt :: Double -> Double)+  pRepl :: String -> (a -> b) -> IO ()+ -- | 'stdin'/'stdout' 'String's as lazy lists instance {-# OVERLAPPING #-} Repl [String] [String] where-  repl :: ([String] -> [String]) -> IO ()-  repl f = interact $ unlines . f . lines+  pRepl :: String -> ([String] -> [String]) -> IO ()+  pRepl "" f = interact $ unlines . f . lines+  pRepl p f =+    noBuffering . interact $+      (p ++) . concatMap (++ '\n' : p) . f . lines +noBuffering :: IO a -> IO a+noBuffering = withBufferMode NoBuffering stdout++withBufferMode :: BufferMode -> Handle -> IO a -> IO a+withBufferMode mode h act =+  bracket+    (hGetBuffering h <* hSetBuffering h mode)+    (hSetBuffering h)+    (const act)+ -- | 'stdin'/'stdout' values as lazy lists instance {-# OVERLAPPING #-} (Read a, Show b) => Repl [a] [b] where-  repl :: ([a] -> [b]) -> IO ()-  repl f = repl $ map show . f . mapMaybe readMaybe+  pRepl :: String -> ([a] -> [b]) -> IO ()+  pRepl p f = pRepl p $ map show . f . mapMaybe readMaybe  -- | Ctrl-D to exit instance (Read a, Show b) => Repl a b where-  repl :: (a -> b) -> IO ()-  repl f = repl $ maybe invalid show . fmap f . readMaybe+  pRepl :: String -> (a -> b) -> IO ()+  pRepl p = pRepl p . readShow +readShowFunc ::+  (Read a, Show b) =>+  (String -> fs) ->+  ((b -> String) -> fb -> fs) ->+  (a -> fb) ->+  (String -> fs)+readShowFunc pr fm f = maybe (pr invalid) (fm show) . fmap f . readMaybe++readShow ::+  (Read a, Show b) => (a -> b) -> (String -> String)+readShow = readShowFunc id id++readShowA ::+  (Applicative f, Read a, Show b) => (a -> f b) -> (String -> f String)+readShowA = readShowFunc pure fmap++readShowAA ::+  (Applicative g, Applicative f, Read a, Show b) =>+  (a -> g (f b)) ->+  (String -> g (f String))+readShowAA = readShowFunc (pure . pure) (fmap . fmap)+ invalid :: String invalid = "Invalid input"  -- | 'String's do not use 'read'/'show' instance {-# OVERLAPPING #-} Repl String String where-  repl :: (String -> String) -> IO ()-  repl f = repl $ map f+  pRepl :: String -> (String -> String) -> IO ()+  pRepl p = pRepl p . map  instance {-# OVERLAPPING #-} Repl String (Maybe String) where-  repl :: (String -> Maybe String) -> IO ()-  repl f = repl $ whileJust . map f+  pRepl :: String -> (String -> Maybe String) -> IO ()+  pRepl p f = pRepl p $ whileJust . map f  whileJust :: [Maybe String] -> [String] whileJust = map fromJust . takeWhile isJust  instance {-# OVERLAPPING #-} Repl String (Either String String) where-  repl :: (String -> Either String String) -> IO ()-  repl f = repl $ whileRight . map f+  pRepl :: String -> (String -> Either String String) -> IO ()+  pRepl p f = pRepl p $ whileRight . map f  whileRight :: [Either String String] -> [String]-whileRight = rights . rightsAndLeft . span isRight-  where-    rightsAndLeft ([], (Left l : _)) = [Right l]-    rightsAndLeft ([], _) = []-    rightsAndLeft (r : rs, ls) = r : rightsAndLeft (rs, ls)+whileRight (Right x : xs) = x : whileRight xs+whileRight (Left x : _) = [x]+whileRight [] = []  -- | return 'Nothing' to exit instance {-# OVERLAPPING #-} (Read a, Show b) => Repl a (Maybe b) where-  repl :: (a -> Maybe b) -> IO ()-  repl f = repl $ readShow f+  pRepl :: String -> (a -> Maybe b) -> IO ()+  pRepl p = pRepl p . readShowA  -- | return 'Left' to exit, string in 'Left' is printed instance {-# OVERLAPPING #-} (Read a, Show b) => Repl a (Either String b) where-  repl :: (a -> Either String b) -> IO ()-  repl f = repl $ readShow f--readShow :: (Applicative f, Read a, Show b) => (a -> f b) -> String -> f String-readShow f = maybe (pure invalid) (fmap show) . fmap f . readMaybe+  pRepl :: String -> (a -> Either String b) -> IO ()+  pRepl p = pRepl p . readShowA  -- | Same as 'repl' with @(a -> b)@ function but the first argument is -- the value that will cause 'repl'' to exit. repl' :: (Eq a, Read a, Show b) => a -> (a -> b) -> IO ()-repl' stop f = repl f'-  where-    f' :: String -> Maybe String-    f' s = case readMaybe s of-      Nothing -> Just invalid-      Just x-        | x == stop -> Nothing-        | otherwise -> Just . show $ f x+repl' = pRepl' "" +pRepl' ::+  forall a b.+  (Eq a, Read a, Show b) =>+  -- | prompt+  String ->+  -- | value to stop+  a ->+  -- | function to transform the input+  (a -> b) ->+  IO ()+pRepl' p stop = pRepl p . readShowA . checkEq stop++checkEq :: Eq a => a -> (a -> b) -> a -> Maybe b+checkEq stop f x+  | x == stop = Nothing+  | otherwise = Just $ f x+ -- | 'ReplState' typeclass with polymorphic stateful function 'replState' -- to interactively evaluate input lines and print responses (see below). class ReplState a b s | b -> s where@@ -167,95 +222,117 @@   -- > replFold (+) 0   --   -- but replState is more flexible - state and output can be different types.-  replState :: (a -> b) -> s -> IO ()+  replState ::+    -- | state function (type defined by the instances)+    (a -> b) ->+    -- | initial state+    s ->+    IO ()+  replState = pReplState "" +  -- | 'replState' with prompt defined by the first argument+  pReplState :: String -> (a -> b) -> s -> IO ()+ -- | plain state function with 'String's as argument and result instance {-# OVERLAPPING #-} ReplState String (s -> (String, s)) s where-  replState :: (String -> s -> (String, s)) -> s -> IO ()-  replState f s0 = repl $ g s0-    where-      g _ [] = []-      g s (x : xs) = let (x', s') = f x s in x' : g s' xs+  pReplState :: String -> (String -> s -> (String, s)) -> s -> IO ()+  pReplState p = pReplState p . toState  -- | plain state function with argument and result of any 'Read'/'Show' types instance (Read a, Show b) => ReplState a (s -> (b, s)) s where-  replState :: (a -> s -> (b, s)) -> s -> IO ()-  replState f = replState f'-    where-      f' s st = case readMaybe s of-        Just x ->-          let (x', st') = f x st-           in (show x', st')-        Nothing -> (invalid, st)+  pReplState :: String -> (a -> s -> (b, s)) -> s -> IO ()+  pReplState p = pReplState p . toState +toState :: (a -> s -> (b, s)) -> (a -> State s b)+toState f = state . f+ -- | 'stdin'/'stdout' 'String's as lazy lists instance {-# OVERLAPPING #-} ReplState [String] (State s [String]) s where-  replState :: ([String] -> State s [String]) -> s -> IO ()-  replState f s0 = interact linesWithState-    where-      linesWithState str = unlines $ evalState (f $ lines str) s0+  pReplState :: String -> ([String] -> State s [String]) -> s -> IO ()+  pReplState p f s0 = pRepl p $ (`evalState` s0) . f  -- | Ctrl-D to exit instance (Read a, Show b) => ReplState a (State s b) s where-  replState :: (a -> State s b) -> s -> IO ()-  replState f = replState $ readShow f+  pReplState :: String -> (a -> State s b) -> s -> IO ()+  pReplState p = pReplState p . readShowA  -- | 'String's do not use 'read'/'show' instance {-# OVERLAPPING #-} ReplState String (State s String) s where-  replState :: (String -> State s String) -> s -> IO ()-  replState f = replState @[String] $ mapM f+  pReplState :: String -> (String -> State s String) -> s -> IO ()+  pReplState p = pReplState @[String] p . mapM  instance {-# OVERLAPPING #-} ReplState String (State s (Maybe String)) s where-  replState :: (String -> State s (Maybe String)) -> s -> IO ()-  replState f = replState $ fmap whileJust . mapM f+  pReplState ::+    String -> (String -> State s (Maybe String)) -> s -> IO ()+  pReplState p f = pReplState p $ fmap whileJust . mapM f  instance {-# OVERLAPPING #-} ReplState String (State s (Either String String)) s where-  replState :: (String -> State s (Either String String)) -> s -> IO ()-  replState f = replState $ fmap whileRight . mapM f+  pReplState ::+    String -> (String -> State s (Either String String)) -> s -> IO ()+  pReplState p f = pReplState p $ fmap whileRight . mapM f  -- | return 'Nothing' to exit instance {-# OVERLAPPING #-} (Read a, Show b) => ReplState a (State s (Maybe b)) s where-  replState :: (a -> State s (Maybe b)) -> s -> IO ()-  replState f = replState $ readShow' f+  pReplState :: String -> (a -> State s (Maybe b)) -> s -> IO ()+  pReplState p = pReplState p . readShowAA  -- | return 'Left' to exit, string in 'Left' is printed instance {-# OVERLAPPING #-} (Read a, Show b) => ReplState a (State s (Either String b)) s where-  replState :: (a -> State s (Either String b)) -> s -> IO ()-  replState f = replState $ readShow' f--readShow' ::-  (Monad f, Read a, Show b) => (a -> State s (f b)) -> String -> State s (f String)-readShow' f = maybe (pure $ pure invalid) (fmap $ fmap show) . fmap f . readMaybe+  pReplState :: String -> (a -> State s (Either String b)) -> s -> IO ()+  pReplState p = pReplState p . readShowAA  -- | Same as 'replState' with @(a -> State s b)@ function but the first -- argument is the value that will cause 'replState'' to exit. replState' ::-  forall a b s. (Eq a, Read a, Show b) => a -> (a -> State s b) -> s -> IO ()-replState' stop f = replState f'-  where-    f' :: String -> State s (Maybe String)-    f' s = case readMaybe s of-      Nothing -> pure $ Just invalid-      Just x-        | x == stop -> pure Nothing-        | otherwise -> Just . show <$> f x+  (Eq a, Read a, Show b) => a -> (a -> State s b) -> s -> IO ()+replState' = pReplState' "" +-- | 'replState'' with prompt+pReplState' ::+  forall a b s.+  (Eq a, Read a, Show b) =>+  -- | prompt+  String ->+  -- | value to stop+  a ->+  -- | state function+  (a -> State s b) ->+  -- | initial state+  s ->+  IO ()+pReplState' p stop f =+  pReplState p . readShowAA $+    sequence . checkEq stop f+ -- | 'replFold' combines the entered values with the accumulated value using -- provided function and prints the resulting values. replFold ::-  forall a b. (Read a, Show b) => (b -> a -> b) -> b -> IO ()-replFold f = replState f'-  where-    f' :: String -> b -> (String, b)-    f' s y = case readMaybe s of-      Nothing -> (invalid, y)-      Just x -> let y' = f y x in (show y', y')+  (Read a, Show b) => (b -> a -> b) -> b -> IO ()+replFold = pReplFold "" +-- | 'replFold' with prompt+pReplFold :: (Read a, Show b) => String -> (b -> a -> b) -> b -> IO ()+pReplFold p = pReplState p . readShowA . foldState++foldState :: (b -> a -> b) -> a -> State b b+foldState f x = modify (`f` x) >> get+ -- | Same as 'replFold' but the first argument is the value that will cause -- 'replFold'' to exit. replFold' ::-  forall a b. (Eq a, Read a, Show b) => a -> (b -> a -> b) -> b -> IO ()-replFold' stop f = replState' stop f'-  where-    f' :: a -> State b b-    f' x = modify (`f` x) >> get+  (Eq a, Read a, Show b) => a -> (b -> a -> b) -> b -> IO ()+replFold' = pReplFold' ""++-- | 'replFold'' with prompt+pReplFold' ::+  (Eq a, Read a, Show b) =>+  -- | prompt+  String ->+  -- | value to stop+  a ->+  -- | folding function+  (b -> a -> b) ->+  -- | initial value+  b ->+  IO ()+pReplFold' p stop = pReplState' p stop . foldState
tests/Test.hs view
@@ -119,12 +119,123 @@     it "sums of squares (stopped)" $       ["2", "5", "12", "0", "11"] #-> replFold' @Int 0 (flip ((+) . (^ (2 :: Int)))) 0         -># ["4", "29", "173"]+  describe "pRepl" do+    it "[String] -> [String]" $+      ["ab", "cdf", "", "efgh"] #-> pRepl ">" (map doubleString)+        ->># [">abab", ">cdfcdf", ">", ">efghefgh"]+    it "String -> String" $+      ["ab", "cdf", "", "efgh"] #-> pRepl ">" doubleString+        ->># [">abab", ">cdfcdf", ">", ">efghefgh"]+    it "String -> Maybe String" $+      ["ab", "cdf", "", "efgh"] #-> pRepl ">" maybeDoubleString+        ->># [">abab", ">cdfcdf"]+    it "String -> Either String String" $+      ["ab", "cdf", "", "efgh"] #-> pRepl ">" eitherDoubleString+        ->># [">abab", ">cdfcdf", ">bye"]+    it "[Int] -> [Int]" $+      ["2", "17", "0", "123"] #-> pRepl ">" (map doubleInt)+        ->># [">4", ">34", ">0", ">246"]+    it "[Int] -> [Int] (invalid input)" $+      ["2", "abc", "17", "0", "123"] #-> pRepl ">" (map doubleInt)+        ->># [">4", ">34", ">0", ">246"]+    it "Int -> Int" $+      ["2", "17", "0", "123"] #-> pRepl ">" doubleInt+        ->># [">4", ">34", ">0", ">246"]+    it "Int -> Int (invalid input)" $+      ["2", "abc", "17", "0", "123"] #-> pRepl ">" doubleInt+        ->># [">4", ">Invalid input", ">34", ">0", ">246"]+    it "Int -> Maybe Int" $+      ["2", "17", "0", "123"] #-> pRepl ">" maybeDoubleInt+        ->># [">4", ">34"]+    it "Int -> Maybe Int (invalid input) " $+      ["2", "abc", "17", "0", "123"] #-> pRepl ">" maybeDoubleInt+        ->># [">4", ">Invalid input", ">34"]+    it "Int -> Either String Int" $+      ["2", "17", "0", "123"] #-> pRepl ">" eitherDoubleInt+        ->># [">4", ">34", ">bye"]+    it "Int -> Either String Int (invalid input)" $+      ["2", "abc", "17", "0", "123"] #-> pRepl ">" eitherDoubleInt+        ->># [">4", ">Invalid input", ">34", ">bye"]+  describe "pRepl'" do+    it "0 -> Int -> Int" $+      ["2", "17", "0", "123"] #-> pRepl' ">" 0 doubleInt+        ->># [">4", ">34"]+    it "0 -> Int -> Int (invalid input)" $+      ["2", "abc", "17", "0", "123"] #-> pRepl' ">" 0 doubleInt+        ->># [">4", ">Invalid input", ">34"]+  describe "pReplState" do+    it "String -> Int -> (Int, String)" $+      ["2", "5", "12", "0", "11"] #-> pReplState ">" infAdderStrFunc 0+        ->># [">2", ">7", ">19", ">19", ">30"]+    it "String -> State Int String" $+      ["2", "5", "12", "0", "11"] #-> pReplState ">" infAdderStr 0+        ->># [">2", ">7", ">19", ">19", ">30"]+    it "String -> State Int (Maybe String)" $+      ["2", "5", "12", "0", "11"] #-> pReplState ">" adderStr 0+        ->># [">2", ">7", ">19"]+    it "String -> State Int (Either String String)" $+      ["2", "5", "12", "0", "11"] #-> pReplState ">" adderStrBye 0+        ->># [">2", ">7", ">19", ">bye"]+    it "Int -> Int -> (Int, Int)" $+      ["2", "5", "12", "0", "11"] #-> pReplState ">" infAdderFunc 0+        ->># [">2", ">7", ">19", ">19", ">30"]+    it "Int -> Int -> (Int, Int) (invlid input)" $+      ["2", "abc", "5", "12", "0", "11"] #-> pReplState ">" infAdderFunc 0+        ->># [">2", ">Invalid input", ">7", ">19", ">19", ">30"]+    it "Int -> State Int Int" $+      ["2", "5", "12", "0", "11"] #-> pReplState ">" infAdder 0+        ->># [">2", ">7", ">19", ">19", ">30"]+    it "Int -> State Int Int (invalid input)" $+      ["2", "abc", "5", "12", "0", "11"] #-> pReplState ">" infAdder 0+        ->># [">2", ">Invalid input", ">7", ">19", ">19", ">30"]+    it "Int -> State Int (Maybe Int)" $+      ["2", "5", "12", "0", "11"] #-> pReplState ">" adder 0+        ->># [">2", ">7", ">19"]+    it "Int -> State Int (Maybe Int) (invalid input)" $+      ["2", "abc", "5", "12", "0", "11"] #-> pReplState ">" adder 0+        ->># [">2", ">Invalid input", ">7", ">19"]+    it "Int -> State Int (Either String Int)" $+      ["2", "5", "12", "0", "11"] #-> pReplState ">" adderBye 0+        ->># [">2", ">7", ">19", ">bye"]+    it "Int -> State Int (Either String Int) (invalid input)" $+      ["2", "abc", "5", "12", "0", "11"] #-> pReplState ">" adderBye 0+        ->># [">2", ">Invalid input", ">7", ">19", ">bye"]+  describe "pReplState'" do+    it "0 -> Int -> State Int Int" $+      ["2", "5", "12", "0", "11"] #-> pReplState' ">" 0 infAdder 0+        ->># [">2", ">7", ">19"]+    it "0 -> Int -> State Int Int (invalid input)" $+      ["2", "abc", "5", "12", "0", "11"] #-> pReplState' ">" 0 infAdder 0+        ->># [">2", ">Invalid input", ">7", ">19"]+  describe "pReplFold" do+    it "Int -> Int -> Int" $+      ["2", "5", "12", "0", "11"] #-> pReplFold @Int ">" (+) 0+        ->># [">2", ">7", ">19", ">19", ">30"]+    it "Int -> Int -> Int (invalid input)" $+      ["2", "abc", "5", "12", "0", "11"] #-> pReplFold @Int ">" (+) 0+        ->># [">2", ">Invalid input", ">7", ">19", ">19", ">30"]+    it "sums of squares" $+      ["2", "5", "12", "0", "11"] #-> pReplFold @Int ">" (flip ((+) . (^ (2 :: Int)))) 0+        ->># [">4", ">29", ">173", ">173", ">294"]+  describe "pReplFold'" do+    it "0 -> Int -> Int -> Int" $+      ["2", "5", "12", "0", "11"] #-> pReplFold' @Int ">" 0 (+) 0+        ->># [">2", ">7", ">19"]+    it "0 -> Int -> Int -> Int (invalid input)" $+      ["2", "abc", "5", "12", "0", "11"] #-> pReplFold' @Int ">" 0 (+) 0+        ->># [">2", ">Invalid input", ">7", ">19"]+    it "sums of squares (stopped)" $+      ["2", "5", "12", "0", "11"] #-> pReplFold' @Int ">" 0 (flip ((+) . (^ (2 :: Int)))) 0+        ->># [">4", ">29", ">173"]  (#->) :: [String] -> IO () -> IO () (#->) = withStdin . pack . unlines  (->#) :: IO () -> [String] -> Expectation-testedIO -># expected = capture_ testedIO `shouldReturn` unlines expected+testIO -># expected = capture_ testIO `shouldReturn` unlines expected++(->>#) :: IO () -> [String] -> Expectation+testIO ->># expected = capture_ testIO `shouldReturn` ((++ ">") . unlines) expected  doubleString :: String -> String doubleString s = s ++ s