306 lines
9.7 KiB
Haskell
306 lines
9.7 KiB
Haskell
|
{-# LANGUAGE DeriveFunctor #-}
|
||
|
{-# LANGUAGE GADTs #-}
|
||
|
{-# LANGUAGE RankNTypes #-}
|
||
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||
|
module Servant.Types.SourceT where
|
||
|
|
||
|
import Control.Monad.Except
|
||
|
(ExceptT (..), runExceptT, throwError)
|
||
|
import Control.Monad.Morph
|
||
|
(MFunctor (..))
|
||
|
import Control.Monad.Trans.Class
|
||
|
(MonadTrans (..))
|
||
|
import qualified Data.Attoparsec.ByteString as A
|
||
|
import qualified Data.ByteString as BS
|
||
|
import Data.Functor.Classes
|
||
|
(Show1 (..), showsBinaryWith, showsPrec1, showsUnaryWith)
|
||
|
import Data.Functor.Identity
|
||
|
(Identity (..))
|
||
|
import Prelude hiding
|
||
|
(readFile)
|
||
|
import System.IO
|
||
|
(Handle, IOMode (..), withFile)
|
||
|
|
||
|
-- $setup
|
||
|
-- >>> :set -XOverloadedStrings
|
||
|
-- >>> import Control.Monad.Except (runExcept)
|
||
|
-- >>> import Data.Foldable (toList)
|
||
|
-- >>> import qualified Data.Attoparsec.ByteString.Char8 as A8
|
||
|
|
||
|
-- | This is CPSised ListT.
|
||
|
newtype SourceT m a = SourceT
|
||
|
{ unSourceT :: forall b. (StepT m a -> m b) -> m b
|
||
|
}
|
||
|
|
||
|
mapStepT :: (StepT m a -> StepT m b) -> SourceT m a -> SourceT m b
|
||
|
mapStepT f (SourceT m) = SourceT $ \k -> m (k . f)
|
||
|
{-# INLINE mapStepT #-}
|
||
|
|
||
|
-- | @ListT@ with additional constructors.
|
||
|
data StepT m a
|
||
|
= Stop
|
||
|
| Error String -- we can this argument configurable.
|
||
|
| Skip (StepT m a) -- Note: not sure about this constructor
|
||
|
| Yield a (StepT m a)
|
||
|
| Effect (m (StepT m a))
|
||
|
deriving Functor
|
||
|
|
||
|
-- | Create 'SourceT' from 'Step'.
|
||
|
--
|
||
|
-- /Note:/ often enough you want to use 'SourceT' directly.
|
||
|
fromStepT :: StepT m a -> SourceT m a
|
||
|
fromStepT s = SourceT ($ s)
|
||
|
|
||
|
-------------------------------------------------------------------------------
|
||
|
-- SourceT instances
|
||
|
-------------------------------------------------------------------------------
|
||
|
|
||
|
instance Functor m => Functor (SourceT m) where
|
||
|
fmap f = mapStepT (fmap f)
|
||
|
|
||
|
-- | >>> toList (source [1..10])
|
||
|
-- [1,2,3,4,5,6,7,8,9,10]
|
||
|
--
|
||
|
instance Identity ~ m => Foldable (SourceT m) where
|
||
|
foldr f z (SourceT m) = foldr f z (runIdentity (m Identity))
|
||
|
|
||
|
instance (Applicative m, Show1 m) => Show1 (SourceT m) where
|
||
|
liftShowsPrec sp sl d (SourceT m) = showsUnaryWith
|
||
|
(liftShowsPrec sp sl)
|
||
|
"fromStepT" d (Effect (m pure'))
|
||
|
where
|
||
|
pure' (Effect s) = s
|
||
|
pure' s = pure s
|
||
|
|
||
|
instance (Applicative m, Show1 m, Show a) => Show (SourceT m a) where
|
||
|
showsPrec = showsPrec1
|
||
|
|
||
|
-- | >>> hoist (Just . runIdentity) (source [1..3]) :: SourceT Maybe Int
|
||
|
-- fromStepT (Effect (Just (Yield 1 (Yield 2 (Yield 3 Stop)))))
|
||
|
instance MFunctor SourceT where
|
||
|
hoist f (SourceT m) = SourceT $ \k -> k $
|
||
|
Effect $ f $ fmap (hoist f) $ m return
|
||
|
|
||
|
-------------------------------------------------------------------------------
|
||
|
-- StepT instances
|
||
|
-------------------------------------------------------------------------------
|
||
|
|
||
|
instance Identity ~ m => Foldable (StepT m) where
|
||
|
foldr f z = go where
|
||
|
go Stop = z
|
||
|
go (Error _) = z
|
||
|
go (Skip s) = go s
|
||
|
go (Yield a s) = f a (go s)
|
||
|
go (Effect (Identity s)) = go s
|
||
|
|
||
|
instance (Applicative m, Show1 m) => Show1 (StepT m) where
|
||
|
liftShowsPrec sp sl = go where
|
||
|
go _ Stop = showString "Stop"
|
||
|
go d (Skip s) = showsUnaryWith
|
||
|
go
|
||
|
"Skip" d s
|
||
|
go d (Error err) = showsUnaryWith
|
||
|
showsPrec
|
||
|
"Error" d err
|
||
|
go d (Effect ms) = showsUnaryWith
|
||
|
(liftShowsPrec go goList)
|
||
|
"Effect" d ms
|
||
|
go d (Yield x s) = showsBinaryWith
|
||
|
sp go
|
||
|
"Yield" d x s
|
||
|
|
||
|
goList = liftShowList sp sl
|
||
|
|
||
|
instance (Applicative m, Show1 m, Show a) => Show (StepT m a) where
|
||
|
showsPrec = showsPrec1
|
||
|
|
||
|
-- | >>> lift [1,2,3] :: StepT [] Int
|
||
|
-- Effect [Yield 1 Stop,Yield 2 Stop,Yield 3 Stop]
|
||
|
--
|
||
|
instance MonadTrans StepT where
|
||
|
lift = Effect . fmap (`Yield` Stop)
|
||
|
|
||
|
instance MFunctor StepT where
|
||
|
hoist f = go where
|
||
|
go Stop = Stop
|
||
|
go (Error err) = Error err
|
||
|
go (Skip s) = Skip (go s)
|
||
|
go (Yield x s) = Yield x (go s)
|
||
|
go (Effect ms) = Effect (f (fmap go ms))
|
||
|
|
||
|
-------------------------------------------------------------------------------
|
||
|
-- Operations
|
||
|
-------------------------------------------------------------------------------
|
||
|
|
||
|
-- | Create pure 'SourceT'.
|
||
|
--
|
||
|
-- >>> source "foo" :: SourceT Identity Char
|
||
|
-- fromStepT (Effect (Identity (Yield 'f' (Yield 'o' (Yield 'o' Stop)))))
|
||
|
--
|
||
|
source :: [a] -> SourceT m a
|
||
|
source = fromStepT . foldr Yield Stop
|
||
|
|
||
|
-- | Get the answers.
|
||
|
--
|
||
|
-- >>> runSourceT (source "foo" :: SourceT Identity Char)
|
||
|
-- ExceptT (Identity (Right "foo"))
|
||
|
--
|
||
|
-- >>> runSourceT (source "foo" :: SourceT [] Char)
|
||
|
-- ExceptT [Right "foo"]
|
||
|
--
|
||
|
runSourceT :: Monad m => SourceT m a -> ExceptT String m [a]
|
||
|
runSourceT (SourceT m) = ExceptT (m (runExceptT . runStepT))
|
||
|
|
||
|
runStepT :: Monad m => StepT m a -> ExceptT String m [a]
|
||
|
runStepT Stop = return []
|
||
|
runStepT (Error err) = throwError err
|
||
|
runStepT (Skip s) = runStepT s
|
||
|
runStepT (Yield x s) = fmap (x :) (runStepT s)
|
||
|
runStepT (Effect ms) = lift ms >>= runStepT
|
||
|
|
||
|
{-
|
||
|
-- | >>> uncons (foldr Yield Stop "foo" :: StepT Identity Char)
|
||
|
-- Identity (Just ('f',Yield 'o' (Yield 'o' Stop)))
|
||
|
--
|
||
|
uncons :: Monad m => StepT m a -> m (Maybe (a, StepT m a))
|
||
|
uncons Stop = return Nothing
|
||
|
uncons (Skip s) = uncons s
|
||
|
uncons (Yield x s) = return (Just (x, s))
|
||
|
uncons (Effect ms) = ms >>= uncons
|
||
|
uncons (Error _) =
|
||
|
-}
|
||
|
|
||
|
-- | Filter values.
|
||
|
--
|
||
|
-- >>> toList $ mapMaybe (\x -> if odd x then Just x else Nothing) (source [0..10]) :: [Int]
|
||
|
-- [1,3,5,7,9]
|
||
|
--
|
||
|
-- >>> mapMaybe (\x -> if odd x then Just x else Nothing) (source [0..2]) :: SourceT Identity Int
|
||
|
-- fromStepT (Effect (Identity (Skip (Yield 1 (Skip Stop)))))
|
||
|
--
|
||
|
-- Illustrates why we need 'Skip'.
|
||
|
mapMaybe :: Functor m => (a -> Maybe b) -> SourceT m a -> SourceT m b
|
||
|
mapMaybe p (SourceT m) = SourceT $ \k -> m (k . mapMaybeStep p)
|
||
|
|
||
|
mapMaybeStep :: Functor m => (a -> Maybe b) -> StepT m a -> StepT m b
|
||
|
mapMaybeStep p = go where
|
||
|
go Stop = Stop
|
||
|
go (Error err) = Error err
|
||
|
go (Skip s) = Skip (go s)
|
||
|
go (Effect ms) = Effect (fmap go ms)
|
||
|
go (Yield x s) = case p x of
|
||
|
Nothing -> Skip (go s)
|
||
|
Just y -> Yield y (go s)
|
||
|
|
||
|
-- | Run action for each value in the 'SourceT'.
|
||
|
--
|
||
|
-- >>> foreach fail print (source "abc")
|
||
|
-- 'a'
|
||
|
-- 'b'
|
||
|
-- 'c'
|
||
|
--
|
||
|
foreach
|
||
|
:: Monad m
|
||
|
=> (String -> m ()) -- ^ error handler
|
||
|
-> (a -> m ())
|
||
|
-> SourceT m a
|
||
|
-> m ()
|
||
|
foreach f g src = unSourceT src (foreachStep f g)
|
||
|
|
||
|
-- | See 'foreach'.
|
||
|
foreachStep
|
||
|
:: Monad m
|
||
|
=> (String -> m ()) -- ^ error handler
|
||
|
-> (a -> m ())
|
||
|
-> StepT m a
|
||
|
-> m ()
|
||
|
foreachStep f g = go where
|
||
|
go Stop = return ()
|
||
|
go (Skip s) = go s
|
||
|
go (Yield x s) = g x >> go s
|
||
|
go (Error err) = f err
|
||
|
go (Effect ms) = ms >>= go
|
||
|
|
||
|
-------------------------------------------------------------------------------
|
||
|
-- Monadic
|
||
|
-------------------------------------------------------------------------------
|
||
|
|
||
|
fromAction :: Functor m => (a -> Bool) -> m a -> SourceT m a
|
||
|
fromAction stop action = SourceT ($ fromActionStep stop action)
|
||
|
{-# INLINE fromAction #-}
|
||
|
|
||
|
fromActionStep :: Functor m => (a -> Bool) -> m a -> StepT m a
|
||
|
fromActionStep stop action = loop where
|
||
|
loop = Effect $ fmap step action
|
||
|
step x
|
||
|
| stop x = Stop
|
||
|
| otherwise = Yield x loop
|
||
|
{-# INLINE fromActionStep #-}
|
||
|
|
||
|
-------------------------------------------------------------------------------
|
||
|
-- File
|
||
|
-------------------------------------------------------------------------------
|
||
|
|
||
|
-- | Read file.
|
||
|
--
|
||
|
-- >>> foreach fail BS.putStr (readFile "servant.cabal")
|
||
|
-- name: servant
|
||
|
-- ...
|
||
|
--
|
||
|
readFile :: FilePath -> SourceT IO BS.ByteString
|
||
|
readFile fp =
|
||
|
SourceT $ \k ->
|
||
|
withFile fp ReadMode $ \hdl ->
|
||
|
k (readHandle hdl)
|
||
|
where
|
||
|
readHandle :: Handle -> StepT IO BS.ByteString
|
||
|
readHandle hdl = fromActionStep BS.null (BS.hGet hdl 4096)
|
||
|
|
||
|
-------------------------------------------------------------------------------
|
||
|
-- Attoparsec
|
||
|
-------------------------------------------------------------------------------
|
||
|
|
||
|
-- | Transform using @attoparsec@ parser.
|
||
|
--
|
||
|
-- Note: @parser@ should not accept empty input!
|
||
|
--
|
||
|
-- >>> let parser = A.skipWhile A8.isSpace_w8 >> A.takeWhile1 A8.isDigit_w8
|
||
|
--
|
||
|
-- >>> runExcept $ runSourceT $ transformWithAtto parser (source ["1 2 3"])
|
||
|
-- Right ["1","2","3"]
|
||
|
--
|
||
|
-- >>> runExcept $ runSourceT $ transformWithAtto parser (source ["1", "2", "3"])
|
||
|
-- Right ["123"]
|
||
|
--
|
||
|
-- >>> runExcept $ runSourceT $ transformWithAtto parser (source ["1", "2 3", "4"])
|
||
|
-- Right ["12","34"]
|
||
|
--
|
||
|
-- >>> runExcept $ runSourceT $ transformWithAtto parser (source ["foobar"])
|
||
|
-- Left "Failed reading: takeWhile1"
|
||
|
--
|
||
|
transformWithAtto :: Monad m => A.Parser a -> SourceT m BS.ByteString -> SourceT m a
|
||
|
transformWithAtto parser = mapStepT (transformStepWithAtto parser)
|
||
|
|
||
|
transformStepWithAtto
|
||
|
:: forall a m. Monad m
|
||
|
=> A.Parser a -> StepT m BS.ByteString -> StepT m a
|
||
|
transformStepWithAtto parser = go (A.parse parser) where
|
||
|
p0 = A.parse parser
|
||
|
|
||
|
go :: (BS.ByteString -> A.Result a)
|
||
|
-> StepT m BS.ByteString -> StepT m a
|
||
|
go _ (Error err) = Error err
|
||
|
go p (Skip s) = Skip (go p s)
|
||
|
go p (Effect ms) = Effect (fmap (go p) ms)
|
||
|
go p Stop = case p mempty of
|
||
|
A.Fail _ _ err -> Error err
|
||
|
A.Done _ a -> Yield a Stop
|
||
|
A.Partial _ -> Stop
|
||
|
go p (Yield bs0 s) = loop p bs0 where
|
||
|
loop p' bs
|
||
|
| BS.null bs = Skip (go p' s)
|
||
|
| otherwise = case p' bs of
|
||
|
A.Fail _ _ err -> Error err
|
||
|
A.Done bs' a -> Yield a (loop p0 bs')
|
||
|
A.Partial p'' -> Skip (go p'' s)
|