packages feed

leveldb-haskell 0.3.1 → 0.4.0

raw patch · 12 files changed

+873/−124 lines, 12 filesdep +QuickCheckdep +directorydep +exceptionsdep ~basePVP ok

version bump matches the API change (PVP)

Dependencies added: QuickCheck, directory, exceptions, mtl, tasty, tasty-quickcheck, temporary

Dependency ranges changed: base

API changes (from Hackage documentation)

- Database.LevelDB.Iterator: iterItems :: (Functor m, MonadIO m) => Iterator -> m [(ByteString, ByteString)]
- Database.LevelDB.Iterator: iterKeys :: (Functor m, MonadIO m) => Iterator -> m [ByteString]
- Database.LevelDB.Iterator: iterValues :: (Functor m, MonadIO m) => Iterator -> m [ByteString]
- Database.LevelDB.Iterator: mapIter :: MonadIO m => (Iterator -> m a) -> Iterator -> m [a]
- Database.LevelDB.MonadResource: iterItems :: MonadResource m => Iterator -> m [(ByteString, ByteString)]
- Database.LevelDB.MonadResource: iterKeys :: MonadResource m => Iterator -> m [ByteString]
- Database.LevelDB.MonadResource: iterValues :: MonadResource m => Iterator -> m [ByteString]
- Database.LevelDB.MonadResource: mapIter :: MonadResource m => (Iterator -> m a) -> Iterator -> m [a]
+ Data.Stream.Monadic: Done :: Step a s
+ Data.Stream.Monadic: Skip :: !s -> Step a s
+ Data.Stream.Monadic: Stream :: (s -> m (Step a s)) -> (m s) -> Stream m a
+ Data.Stream.Monadic: Yield :: a -> !s -> Step a s
+ Data.Stream.Monadic: append :: (Functor m, Monad m) => Stream m a -> Stream m a -> Stream m a
+ Data.Stream.Monadic: concatMap :: (Functor m, Monad m) => (a -> Stream m b) -> Stream m a -> Stream m b
+ Data.Stream.Monadic: cons :: (Functor m, Monad m) => a -> Stream m a -> Stream m a
+ Data.Stream.Monadic: cycle :: (Functor m, Monad m) => Stream m a -> Stream m a
+ Data.Stream.Monadic: data Step a s
+ Data.Stream.Monadic: data Stream m a
+ Data.Stream.Monadic: drop :: (Functor m, Monad m) => Int -> Stream m a -> Stream m a
+ Data.Stream.Monadic: dropWhile :: (Functor m, Monad m) => (a -> Bool) -> Stream m a -> Stream m a
+ Data.Stream.Monadic: filter :: Monad m => (a -> Bool) -> Stream m a -> Stream m a
+ Data.Stream.Monadic: foldM :: Monad m => (b -> a -> m b) -> b -> Stream m a -> m b
+ Data.Stream.Monadic: foldM_ :: Monad m => (b -> a -> m b) -> b -> Stream m a -> m ()
+ Data.Stream.Monadic: foldMap :: (Monoid m, Functor n, Monad n) => (a -> m) -> Stream n a -> n m
+ Data.Stream.Monadic: foldl :: Monad m => (b -> a -> b) -> b -> Stream m a -> m b
+ Data.Stream.Monadic: foldl' :: Monad m => (b -> a -> b) -> b -> Stream m a -> m b
+ Data.Stream.Monadic: foldr :: (Functor m, Monad m) => (a -> b -> b) -> b -> Stream m a -> m b
+ Data.Stream.Monadic: fromList :: Monad m => [a] -> Stream m a
+ Data.Stream.Monadic: head :: Monad m => Stream m a -> m (Maybe a)
+ Data.Stream.Monadic: init :: (Functor m, Monad m) => Stream m a -> Stream m a
+ Data.Stream.Monadic: instance Monad m => Functor (Stream m)
+ Data.Stream.Monadic: intersperse :: (Functor m, Monad m) => a -> Stream m a -> Stream m a
+ Data.Stream.Monadic: iterate :: Monad m => (a -> a) -> a -> Stream m a
+ Data.Stream.Monadic: last :: Monad m => Stream m a -> m (Maybe a)
+ Data.Stream.Monadic: length :: Monad m => Stream m a -> m Int
+ Data.Stream.Monadic: map :: Monad m => (a -> b) -> Stream m a -> Stream m b
+ Data.Stream.Monadic: null :: Monad m => Stream m a -> m Bool
+ Data.Stream.Monadic: repeat :: Monad m => a -> Stream m a
+ Data.Stream.Monadic: replicate :: Monad m => Int -> a -> Stream m a
+ Data.Stream.Monadic: snoc :: (Functor m, Monad m) => Stream m a -> a -> Stream m a
+ Data.Stream.Monadic: tail :: (Functor m, Monad m) => Stream m a -> Stream m a
+ Data.Stream.Monadic: take :: (Functor m, Monad m) => Int -> Stream m a -> Stream m a
+ Data.Stream.Monadic: takeWhile :: Monad m => (a -> Bool) -> Stream m a -> Stream m a
+ Data.Stream.Monadic: toList :: (Functor m, Monad m) => Stream m a -> m [a]
+ Data.Stream.Monadic: unfoldr :: Monad m => (b -> Maybe (a, b)) -> b -> Stream m a
+ Data.Stream.Monadic: unfoldrM :: (Functor m, Monad m) => (b -> Maybe (a, m b)) -> m b -> Stream m a
+ Data.Stream.Monadic: unzip :: (Functor m, Monad m) => Stream m (a, b) -> m ([a], [b])
+ Data.Stream.Monadic: zip :: (Functor m, Applicative m, Monad m) => Stream m a -> Stream m b -> Stream m (a, b)
+ Data.Stream.Monadic: zipWith :: (Functor m, Applicative m, Monad m) => (a -> b -> c) -> Stream m a -> Stream m b -> Stream m c
+ Database.LevelDB.Streaming: AllKeys :: KeyRange
+ Database.LevelDB.Streaming: Asc :: Direction
+ Database.LevelDB.Streaming: Desc :: Direction
+ Database.LevelDB.Streaming: KeyRange :: !ByteString -> (ByteString -> Ordering) -> KeyRange
+ Database.LevelDB.Streaming: Slice :: !Iterator -> (Maybe KeyRange) -> !Direction -> Slice
+ Database.LevelDB.Streaming: data Direction
+ Database.LevelDB.Streaming: data KeyRange
+ Database.LevelDB.Streaming: data Slice
+ Database.LevelDB.Streaming: end :: KeyRange -> ByteString -> Ordering
+ Database.LevelDB.Streaming: entrySlice :: (Applicative m, MonadIO m) => Iterator -> KeyRange -> Direction -> Stream m Entry
+ Database.LevelDB.Streaming: instance Show Direction
+ Database.LevelDB.Streaming: keySlice :: (Applicative m, MonadIO m) => Iterator -> KeyRange -> Direction -> Stream m Key
+ Database.LevelDB.Streaming: start :: KeyRange -> !ByteString
+ Database.LevelDB.Streaming: type Entry = (Key, Value)
+ Database.LevelDB.Streaming: type Key = ByteString
+ Database.LevelDB.Streaming: type Value = ByteString
- Database.LevelDB.Base: withSnapshot :: MonadIO m => DB -> (Snapshot -> IO a) -> m a
+ Database.LevelDB.Base: withSnapshot :: (MonadMask m, MonadIO m) => DB -> (Snapshot -> m a) -> m a
- Database.LevelDB.Iterator: withIter :: MonadIO m => DB -> ReadOptions -> (Iterator -> IO a) -> m a
+ Database.LevelDB.Iterator: withIter :: (MonadMask m, MonadIO m) => DB -> ReadOptions -> (Iterator -> m a) -> m a

Files

CHANGELOG view
@@ -1,3 +1,11 @@+[0.4.0]:++* New streaming interface (experimental)++[0.3.1]:++* Deprecate `mapIter`, `iterItems`, `iterKeys`, `iterValues`+ [0.3.0]:  * ResourceT is no longer compulsory
Readme.md view
@@ -5,6 +5,14 @@  ## History +Version 0.4.0:++* New streaming interface (experimental)++Version 0.3.1:++* Deprecate `mapIter`, `iterItems`, `iterKeys`, `iterValues`+ Version 0.3.0:  * ResourceT is no longer compulsory
examples/comparator.hs view
@@ -4,10 +4,10 @@  module Main where -import Control.Monad.IO.Class (liftIO)-import Data.Default--import Database.LevelDB+import           Control.Monad.IO.Class     (liftIO)+import           Data.Default+import           Database.LevelDB+import qualified Database.LevelDB.Streaming as S   customComparator :: Comparator@@ -24,8 +24,8 @@     put db def "yyy" ""     put db def "xxx" "" -    withIterator db def $ \iter -> do-        iterFirst iter-        iterItems iter >>= liftIO . print+    withIterator db def $ \iter -> liftIO $+            S.toList (S.entrySlice iter S.AllKeys S.Asc)+        >>= print      return ()
examples/features.hs view
@@ -4,14 +4,14 @@ -- module Main where -import Control.Monad-import Control.Monad.IO.Class       (liftIO)-import Control.Monad.Trans.Resource (release)-import Data.ByteString.Char8 hiding (take)-import Data.Default-import Prelude               hiding (putStrLn)--import Database.LevelDB+import           Control.Monad+import           Control.Monad.IO.Class       (liftIO)+import           Control.Monad.Trans.Resource (release)+import           Data.ByteString.Char8        hiding (take)+import           Data.Default+import           Database.LevelDB+import qualified Database.LevelDB.Streaming   as S+import           Prelude                      hiding (putStrLn)   main :: IO ()@@ -59,10 +59,9 @@     return ()    where--    dumpEntries iter = do-        iterFirst iter-        iterItems iter >>= liftIO . print+    dumpEntries iter = liftIO $+            S.toList (S.entrySlice iter S.AllKeys S.Asc)+        >>= print      printProperty l p = liftIO $ do         putStrLn l
leveldb-haskell.cabal view
@@ -1,5 +1,5 @@ name:                leveldb-haskell-version:             0.3.1+version:             0.4.0 synopsis:            Haskell bindings to LevelDB homepage:            http://github.com/kim/leveldb-haskell bug-reports:         http://github.com/kim/leveldb-haskell/issues@@ -45,13 +45,16 @@                   , Database.LevelDB.Internal                   , Database.LevelDB.Iterator                   , Database.LevelDB.MonadResource+                  , Database.LevelDB.Streaming                   , Database.LevelDB.Types+                  , Data.Stream.Monadic    default-language: Haskell2010    build-depends:    base >= 3 && < 5                   , bytestring                   , data-default+                  , exceptions == 0.6.*                   , filepath                   , resourcet > 0.3.2                   , transformers@@ -73,7 +76,7 @@                   , data-default                   , leveldb-haskell -  ghc-options:      -Wall -Werror -O -rtsopts+  ghc-options:      -Wall -O -rtsopts   ghc-prof-options: -prof -auto-all    hs-source-dirs:   examples@@ -115,7 +118,7 @@                   , data-default                   , leveldb-haskell -  ghc-options:      -Wall -Werror -O -rtsopts+  ghc-options:      -Wall -O -rtsopts   ghc-prof-options: -prof -auto-all    hs-source-dirs:   examples@@ -136,7 +139,7 @@                   , data-default                   , leveldb-haskell -  ghc-options:      -Wall -Werror -O -rtsopts+  ghc-options:      -Wall -O -rtsopts   ghc-prof-options: -prof -auto-all    hs-source-dirs:   examples@@ -145,3 +148,25 @@     buildable:      True   else     buildable:      False++test-suite leveldb-properties+  type:             exitcode-stdio-1.0+  main-is:          Main.hs+  hs-source-dirs:   test++  default-language: Haskell2010++  build-depends:    base+                  , bytestring+                  , data-default+                  , directory+                  , exceptions       == 0.6.*+                  , mtl+                  , leveldb-haskell+                  , QuickCheck       == 2.7.*+                  , tasty            == 0.10.*+                  , tasty-quickcheck == 0.8.*+                  , temporary+                  , transformers++  ghc-options:      -Wall -O -rtsopts -threaded -with-rtsopts=-N
+ src/Data/Stream/Monadic.hs view
@@ -0,0 +1,661 @@+{-# LANGUAGE BangPatterns              #-}+{-# LANGUAGE ExistentialQuantification #-}++-- |+-- Module      : Data.Stream.Monadic+-- Copyright   : (c) 2014 Kim Altintop+-- License     : BSD3+-- Maintainer  : kim.altintop@gmail.com+-- Stability   : experimental+-- Portability : non-portable+--+-- (Mostly mechanical) adaptation of the+-- <http://hackage.haskell.org/package/stream-fusion/docs/Data-Stream.html Data.Stream>+-- module from the+-- <http://hackage.haskell.org/package/stream-fusion stream-fusion> package to a+-- monadic 'Stream' datatype similar to the one+-- <https://www.fpcomplete.com/blog/2014/08/conduit-stream-fusion proposed> by+-- Michael Snoyman for the <http://hackage.haskell.org/package/conduit conduit>+-- package.+--+-- The intention here is to provide a high-level, "Data.List"-like interface to+-- "Database.LevelDB.Iterator"s with predictable space and time complexity (see+-- "Database.LevelDB.Streaming"), and without introducing a dependency eg. on+-- one of the streaming libraries (all relevant datatypes are fully exported,+-- though, so it should be straightforward to write wrappers for your favourite+-- streaming library).+--+-- Fusion and inlining rules and strictness annotations have been put in place+-- faithfully, and may need further profiling. Also, some functions (from+-- "Data.List") have been omitted as either no obvious solution exists (notably+-- @mapM@), they didn't seem too useful in the given context (eg. @lookup@), or+-- I was just too lazy. Missing functions may be added upon+-- <https://github.com/kim/leveldb-haskell/pulls request>.++module Data.Stream.Monadic+    ( Step   (..)+    , Stream (..)++    -- * Conversion with lists+    , toList+    , fromList++    -- * Basic functions+    , append+    , cons+    , snoc+    , head+    , last+    , tail+    , init+    , null+    , length++    -- * Transformations+    , map+    -- , mapM+    , intersperse++    -- * Folds+    , foldl+    , foldl'+    -- , foldl1+    -- , foldl1'+    , foldr+    -- , foldr1+    , foldMap+    , foldM+    , foldM_++    -- * Special folds+    -- , concat+    , concatMap+    -- , and+    -- , or+    -- , any+    -- , all+    -- , sum+    -- , product+    -- , maximum+    -- , minimum++    -- , scanl+    -- , scanl1++    -- * Infinite streams+    , iterate+    , repeat+    , replicate+    , cycle++    -- * Unfolding+    , unfoldr+    , unfoldrM++    -- , isPrefixOf++    -- * Searching streams+    -- , elem+    -- , lookup++    -- , find+    , filter++    -- , index+    -- , findIndex+    -- , elemIndex+    -- , elemIndices+    -- , findIndices++    -- * Substreams+    , take+    , drop+    -- , splitAt+    , takeWhile+    , dropWhile++    -- * Zipping and unzipping+    , zip+    -- , zip3+    -- , zip4+    , zipWith+    -- , zipWith3+    -- , zipWith4+    , unzip++    -- , insertBy+    -- , maximumBy+    -- , minimumBy++    -- , genericLength+    -- , genericTake+    -- , genericDrop+    -- , genericIndex+    -- , genericSplitAt++    -- , enumFromToInt+    -- , enumFromToChar+    -- , enumDeltaInteger+    )+where++import Control.Applicative+import Data.Monoid++import Prelude (Bool (..), Either (..), Eq (..), Functor (..), Int, Maybe (..),+                Monad (..), Num (..), Ord (..), error, otherwise, ($), (&&),+                (.), (=<<))+++data Step   a  s+   = Yield  a !s+   | Skip  !s+   | Done++data Stream m a = forall s. Stream (s -> m (Step a s)) (m s)++instance Monad m => Functor (Stream m) where+    fmap = map+++toList :: (Functor m, Monad m) => Stream m a -> m [a]+toList (Stream next s0) = unfold =<< s0+  where+    unfold !s = do+        step <- next s+        case step of+            Done       -> return []+            Skip    s' -> unfold s'+            Yield x s' -> (x :) <$> unfold s'++fromList :: Monad m => [a] -> Stream m a+fromList xs = Stream next (return xs)+  where+    {-# INLINE next #-}+    next []      = return Done+    next (x:xs') = return $ Yield x xs'++{-# RULES+    "Stream fromList/toList fusion" forall s.+        fmap fromList (toList s) = return s+  #-}++append :: (Functor m, Monad m) => Stream m a -> Stream m a -> Stream m a+append (Stream next0 s0) (Stream next1 s1) = Stream next (Left <$> s0)+  where+    {-# INLINE next #-}+    next (Left s) = do+        step <- next0 s+        case step of+            Done       -> Skip . Right <$> s1+            Skip    s' -> return $ Skip    (Left s')+            Yield x s' -> return $ Yield x (Left s')++    next (Right s) = do+        step <- next1 s+        return $ case step of+            Done       -> Done+            Skip    s' -> Skip    (Right s')+            Yield x s' -> Yield x (Right s')+{-# INLINE [0] append #-}++cons :: (Functor m, Monad m) => a -> Stream m a -> Stream m a+cons w (Stream next0 s0) = Stream next ((,) S2 <$> s0)+  where+    {-# INLINE next #-}+    next (S2, s) = return $ Yield w (S1, s)+    next (S1, s) = do+        step <- next0 s+        return $ case step of+            Done       -> Done+            Skip    s' -> Skip    (S1, s')+            Yield x s' -> Yield x (S1, s')+{-# INLINE [0] cons #-}++snoc :: (Functor m, Monad m) => Stream m a -> a -> Stream m a+snoc (Stream next0 s0) y = Stream next (Just <$> s0)+  where+    {-# INLINE next #-}+    next Nothing  = return Done+    next (Just s) = do+        step <- next0 s+        return $ case step of+            Done       -> Yield y Nothing+            Skip    s' -> Skip    (Just s')+            Yield x s' -> Yield x (Just s')+{-# INLINE [0] snoc #-}++-- | Unlike 'Data.List.head', this function does not diverge if the 'Stream is+-- empty. Instead, 'Nothing' is returned.+head :: Monad m => Stream m a -> m (Maybe a)+head (Stream next s0) = loop =<< s0+  where+    loop !s = do+        step <- next s+        case step of+            Yield x _  -> return $ Just x+            Skip    s' -> loop s'+            Done       -> return Nothing+{-# INLINE [0] head #-}++-- | Unlike 'Data.List.last', this function does not diverge if the 'Stream' is+-- empty. Instead, 'Nothing' is returned.+last :: Monad m => Stream m a -> m (Maybe a)+last (Stream next s0) = loop =<< s0+  where+    loop !s = do+        step <- next s+        case step of+            Done       -> return Nothing+            Skip    s' -> loop s'+            Yield x s' -> loop' x s'+    loop' x !s = do+        step <- next s+        case step of+            Done        -> return $ Just x+            Skip     s' -> loop' x s'+            Yield x' s' -> loop' x' s'+{-# INLINE [0] last #-}++data Switch = S1 | S2++-- | Unlike 'Data.List.tail', this function does not diverge if the 'Stream' is+-- empty. Instead, it is the identity in this case.+tail :: (Functor m, Monad m) => Stream m a -> Stream m a+tail (Stream next0 s0) = Stream next ((,) S1 <$> s0)+  where+    {-# INLINE next #-}+    next (S1, s) = do+        step <- next0 s+        return $ case step of+            Done       -> Done+            Skip    s' -> Skip (S1, s')+            Yield _ s' -> Skip (S2, s')++    next (S2, s) = do+        step <- next0 s+        return $ case step of+            Done       -> Done+            Skip    s' -> Skip (S2, s')+            Yield x s' -> Yield x (S2, s')+{-# INLINE [0] tail #-}++-- | Unlike 'Data.List.init', this function does not diverge if the 'Stream' is+-- empty. Instead, it is the identity in this case.+init :: (Functor m, Monad m) => Stream m a -> Stream m a+init (Stream next0 s0) = Stream next ((,) Nothing <$> s0)+  where+    {-# INLINE next #-}+    next (Nothing, s) = do+        step <- next0 s+        return $ case step of+            Done       -> Done+            Skip    s' -> Skip (Nothing, s')+            Yield x s' -> Skip (Just x , s')++    next (Just x, s) = do+        step <- next0 s+        return $ case step of+            Done        -> Done+            Skip     s' -> Skip    (Just x , s')+            Yield x' s' -> Yield x (Just x', s')+{-# INLINE [0] init #-}++null :: Monad m => Stream m a -> m Bool+null (Stream next s0) = loop =<< s0+  where+    loop !s = do+        step <- next s+        case step of+            Done       -> return True+            Yield _ _  -> return False+            Skip    s' -> loop s'+{-# INLINE [0] null #-}++length :: Monad m => Stream m a -> m Int+length (Stream next s0) = loop 0 =<< s0+  where+    loop !z !s = do+        step <- next s+        case step of+            Done       -> return z+            Skip    s' -> loop   z    s'+            Yield _ s' -> loop  (z+1) s'+{-# INLINE [0] length #-}++filter :: Monad m => (a -> Bool) -> Stream m a -> Stream m a+filter p (Stream next0 s0) = Stream next s0+  where+    {-# INLINE next #-}+    next !s = do+        step <- next0 s+        return $ case step of+            Done                   -> Done+            Skip    s'             -> Skip    s'+            Yield x s' | p x       -> Yield x s'+                       | otherwise -> Skip    s'+{-# INLINE [0] filter #-}+{-# RULES+    "Stream filter/filter fusion" forall p q s.+        filter p (filter q s) = filter (\x -> q x && p x) s+  #-}++map :: Monad m => (a -> b) -> Stream m a -> Stream m b+map f (Stream next0 s0) = Stream next s0+  where+    {-# INLINE next #-}+    next !s = do+        step <- next0 s+        return $ case step of+            Done       -> Done+            Skip    s' -> Skip        s'+            Yield x s' -> Yield (f x) s'+{-# INLINE [0] map #-}+{-# RULES+    "Stream map/map fusion" forall f g s.+        map f (map g s) = map (\x -> f (g x)) s+  #-}++-- 'mapM' is tricky:+--+-- > mapM :: (Monad m, Monad n) => (a -> n b) -> Stream m a -> n (Stream n b)+--+-- we would need a constraint which specifies how to lift any monad /m/ into+-- some monad /n/ (or specialise /m/ to 'IO').+--+-- alternatively, we may define:+--+-- > mapM :: Monad m => (a -> m b) -> Stream m a -> m (Stream m b)+--+-- or rather:+--+-- > mapM :: Monad m => (a -> m b) -> Stream m a -> Stream m b+--+-- not sure how useful this would be.+++intersperse :: (Functor m, Monad m) => a -> Stream m a -> Stream m a+intersperse sep (Stream next0 s0) = Stream next ((,,) Nothing S1 <$> s0)+  where+    {-# INLINE next #-}+    next (Nothing, S1, s) = do+        step <- next0 s+        return $ case step of+            Done       -> Done+            Skip    s' -> Skip (Nothing, S1, s')+            Yield x s' -> Skip (Just x , S1, s')++    next (Just x, S1, s)  = return $ Yield x (Nothing, S2, s)++    next (Nothing, S2, s) = do+        step <- next0 s+        return $ case step of+            Done       -> Done+            Skip    s' -> Skip      (Nothing, S2, s')+            Yield x s' -> Yield sep (Just x , S1, s')++    next (Just _, S2, _)  = error "Data.Stream.Monadic.intersperse: impossible"+{-# INLINE [0] intersperse #-}++foldMap :: (Monoid m, Functor n, Monad n) => (a -> m) -> Stream n a -> n m+foldMap f (Stream next s0) = loop mempty =<< s0+  where+    loop z !s = do+        step <- next s+        case step of+            Done       -> return z+            Skip    s' -> loop z s'+            Yield x s' -> loop (z <> f x) s'+{-# INLINE [0] foldMap #-}++-- | Left-associative fold.+--+-- Note that the /direction/ of the traversal is not defined here.+foldl :: Monad m => (b -> a -> b) -> b -> Stream m a -> m b+foldl f z0 (Stream next s0) = loop z0 =<< s0+  where+    loop z !s = do+        step <- next s+        case step of+            Done       -> return z+            Skip    s' -> loop z s'+            Yield x s' -> loop (f z x) s'+{-# INLINE [0] foldl #-}++-- | Left-associative fold with strict accumulator.+--+-- Note that the /direction/ of the traversal is not defined here.+foldl' :: Monad m => (b -> a -> b) -> b -> Stream m a -> m b+foldl' f z0 (Stream next s0) = loop z0 =<< s0+  where+    loop !z !s = do+        step <- next s+        case step of+            Done       -> return z+            Skip    s' -> loop z s'+            Yield x s' -> loop (f z x) s'+{-# INLINE [0] foldl' #-}++-- | Right-associative fold.+--+-- Note that the /direction/ of the traversal is not defined here.+foldr :: (Functor m, Monad m) => (a -> b -> b) -> b -> Stream m a -> m b+foldr f z (Stream next s0) = loop =<< s0+  where+    loop !s = do+        step <- next s+        case step of+            Done       -> return z+            Skip    s' -> loop s'+            Yield x s' -> f x <$> loop s'+{-# INLINE [0] foldr #-}++foldM :: Monad m => (b -> a -> m b) -> b -> Stream m a -> m b+foldM f z0 (Stream next s0) = loop z0 =<< s0+  where+    loop z !s = do+        step <- next s+        case step of+            Done       -> return z+            Skip    s' -> loop z s'+            Yield x s' -> f z x >>= (`loop` s')+{-# INLINE [0] foldM #-}++foldM_ :: Monad m => (b -> a -> m b) -> b -> Stream m a -> m ()+foldM_ f z0 (Stream next s0) = loop z0 =<< s0+  where+    loop z !s = do+        step <- next s+        case step of+            Done       -> return ()+            Skip    s' -> loop z s'+            Yield x s' -> f z x >>= (`loop` s')+{-# INLINE [0] foldM_ #-}++concatMap :: (Functor m, Monad m) => (a -> Stream m b) -> Stream m a -> Stream m b+concatMap f (Stream next0 s0) = Stream next ((,) Nothing <$> s0)+  where+    {-# INLINE next #-}+    next (Nothing, s) = do+        step <- next0 s+        return $ case step of+            Done       -> Done+            Skip    s' -> Skip (Nothing   , s')+            Yield x s' -> Skip (Just (f x), s')++    next (Just (Stream g t), s) = do+        step <- g =<< t+        return $ case step of+            Done       -> Skip    (Nothing                    , s)+            Skip    t' -> Skip    (Just (Stream g (return t')), s)+            Yield x t' -> Yield x (Just (Stream g (return t')), s)+{-# INLINE [0] concatMap #-}++iterate :: Monad m => (a -> a) -> a -> Stream m a+iterate f x0 = Stream next (return x0)+  where+    {-# INLINE next #-}+    next x = return $ Yield x (f x)+{-# INLINE [0] iterate #-}++repeat :: Monad m => a -> Stream m a+repeat x = Stream next (return ())+  where+    {-# INLINE next #-}+    next _ = return $ Yield x ()+{-# INLINE [0] repeat #-}+{-# RULES+    "map/repeat" forall f x. map f (repeat x) = repeat (f x)+  #-}++replicate :: Monad m => Int -> a -> Stream m a+replicate n x = Stream next (return n)+  where+    {-# INLINE next #-}+    next !i | i <= 0    = return Done+            | otherwise = return $ Yield x (i-1)+{-# INLINE [0] replicate #-}+{-# RULES+    "map/replicate" forall f n x. map f (replicate n x) = replicate n (f x)+  #-}++-- | Unlike 'Data.List.cycle', this function does not diverge if the 'Stream' is+-- empty. Instead, it is the identity in this case.+cycle :: (Functor m, Monad m) => Stream m a -> Stream m a+cycle (Stream next0 s0) = Stream next ((,) S1 <$> s0)+  where+    {-# INLINE next #-}+    next (S1, s) = do+        step <- next0 s+        return $ case step of+            Done       -> Done -- error?+            Skip    s' -> Skip    (S1, s')+            Yield x s' -> Yield x (S2, s')++    next (S2, s) = do+        step <- next0 s+        case step of+            Done       -> Skip . ((,) S2) <$> s0+            Skip    s' -> return $ Skip    (S2, s')+            Yield x s' -> return $ Yield x (S2, s')+{-# INLINE [0] cycle #-}++unfoldr :: Monad m => (b -> Maybe (a, b)) -> b -> Stream m a+unfoldr f s0 = Stream next (return s0)+  where+    {-# INLINE next #-}+    next s = return $ case f s of+        Nothing      -> Done+        Just (w, s') -> Yield w s'+{-# INLINE [0] unfoldr #-}++-- | Build a stream from a monadic seed (or state function).+unfoldrM :: (Functor m, Monad m) => (b -> Maybe (a, m b)) -> m b -> Stream m a+unfoldrM f s0 = Stream next s0+  where+    {-# INLINE next #-}+    next s = case f s of+        Nothing      -> return Done+        Just (w, s') -> Yield w <$> s'+{-# INLINE [0] unfoldrM #-}++take :: (Functor m, Monad m) => Int -> Stream m a -> Stream m a+take n0 (Stream next0 s0) = Stream next ((,) n0 <$> s0)+  where+    {-# INLINE next #-}+    next (!n, s)+      | n <= 0    = return Done+      | otherwise = do+          step <- next0 s+          return $ case step of+              Done       -> Done+              Skip    s' -> Skip    (n  , s')+              Yield x s' -> Yield x (n-1, s')+{-# INLINE [0] take #-}++drop :: (Functor m, Monad m) => Int -> Stream m a -> Stream m a+drop n0 (Stream next0 s0) = Stream next ((,) (Just (max 0 n0)) <$> s0)+  where+    {-# INLINE next #-}+    next (Just !n, s)+      | n == 0    = return $ Skip (Nothing, s)+      | otherwise = do+          step <- next0 s+          return $ case step of+              Done       -> Done+              Skip    s' -> Skip (Just  n   , s')+              Yield _ s' -> Skip (Just (n-1), s')+    next (Nothing, s) = do+        step <- next0 s+        return $ case step of+            Done       -> Done+            Skip    s' -> Skip    (Nothing, s')+            Yield x s' -> Yield x (Nothing, s')+{-# INLINE [0] drop #-}++takeWhile :: Monad m => (a -> Bool) -> Stream m a -> Stream m a+takeWhile p (Stream next0 s0) = Stream next s0+  where+    {-# INLINE next #-}+    next !s = do+        step <- next0 s+        return $ case step of+            Done                   -> Done+            Skip    s'             -> Skip    s'+            Yield x s' | p x       -> Yield x s'+                       | otherwise -> Done+{-# INLINE [0] takeWhile #-}++dropWhile :: (Functor m, Monad m) => (a -> Bool) -> Stream m a -> Stream m a+dropWhile p (Stream next0 s0) = Stream next ((,) S1 <$> s0)+  where+    {-# INLINE next #-}+    next (S1, s) = do+        step <- next0 s+        return $ case step of+            Done                   -> Done+            Skip    s'             -> Skip    (S1, s')+            Yield x s' | p x       -> Skip    (S1, s')+                       | otherwise -> Yield x (S2, s')+    next (S2, s) = do+        step <- next0 s+        return $ case step of+            Done       -> Done+            Skip    s' -> Skip    (S2, s')+            Yield x s' -> Yield x (S2, s')+{-# INLINE [0] dropWhile #-}++zip :: (Functor m, Applicative m, Monad m)+    => Stream m a+    -> Stream m b+    -> Stream m (a, b)+zip = zipWith (,)+{-# INLINE zip #-}++zipWith :: (Functor m, Applicative m, Monad m)+        => (a -> b -> c)+        -> Stream m a+        -> Stream m b+        -> Stream m c+zipWith f (Stream nexta sa0) (Stream nextb sb0) =+    Stream next ((,,) Nothing <$> sa0 <*> sb0)+  where+    {-# INLINE next #-}+    next (Nothing, sa, sb) = do+        step <- nexta sa+        return $ case step of+            Done        -> Done+            Skip    sa' -> Skip (Nothing, sa', sb)+            Yield a sa' -> Skip (Just a , sa', sb)++    next (Just a, sa', sb) = do+        step <- nextb sb+        return $ case step of+            Done        -> Done+            Skip    sb' -> Skip          (Just a, sa', sb')+            Yield b sb' -> Yield (f a b) (Nothing, sa', sb')+{-# INLINE [0] zipWith #-}++unzip :: (Functor m, Monad m) => Stream m (a, b) -> m ([a], [b])+unzip = foldr (\(a,b) ~(as, bs) -> (a:as, b:bs)) ([], [])+{-# INLINE unzip #-}
src/Database/LevelDB.hs view
@@ -11,6 +11,6 @@ -- The API closely follows the C-API of LevelDB. -- For more information, see: <http://leveldb.googlecode.com> -module Database.LevelDB (module R) where+module Database.LevelDB (module Database.LevelDB.MonadResource) where -import Database.LevelDB.MonadResource as R+import Database.LevelDB.MonadResource
src/Database/LevelDB/Base.hs view
@@ -59,8 +59,8 @@ where  import           Control.Applicative      ((<$>))-import           Control.Exception        (bracket, bracketOnError, finally) import           Control.Monad            (liftM)+import           Control.Monad.Catch import           Control.Monad.IO.Class   (MonadIO (liftIO)) import           Data.ByteString          (ByteString) import           Data.ByteString.Internal (ByteString (..))@@ -98,9 +98,8 @@   -- | Run an action with a 'Snapshot' of the database.-withSnapshot :: MonadIO m => DB -> (Snapshot -> IO a) -> m a-withSnapshot db act = liftIO $-    bracket (createSnapshot db) (releaseSnapshot db) act+withSnapshot :: (MonadMask m, MonadIO m) => DB -> (Snapshot -> m a) -> m a+withSnapshot db = bracket (createSnapshot db) (releaseSnapshot db)  -- | Create a snapshot of the database. --
src/Database/LevelDB/Iterator.hs view
@@ -15,28 +15,23 @@     , iterEntry     , iterFirst     , iterGetError-    , iterItems     , iterKey-    , iterKeys     , iterLast     , iterNext     , iterPrev     , iterSeek     , iterValid     , iterValue-    , iterValues-    , mapIter     , releaseIter     , withIter     ) where  import           Control.Applicative       ((<$>), (<*>))-import           Control.Exception         (bracket, finally, onException) import           Control.Monad             (when)+import           Control.Monad.Catch import           Control.Monad.IO.Class    (MonadIO (liftIO)) import           Data.ByteString           (ByteString)-import           Data.Maybe                (catMaybes) import           Foreign import           Foreign.C.Error           (throwErrnoIfNull) import           Foreign.C.String          (CString, peekCString)@@ -82,8 +77,8 @@     c_leveldb_iter_destroy iter_ptr `finally` freeCReadOpts opts  -- | Run an action with an 'Iterator'-withIter :: MonadIO m => DB -> ReadOptions -> (Iterator -> IO a) -> m a-withIter db opts = liftIO . bracket (createIter db opts) releaseIter+withIter :: (MonadMask m, MonadIO m) => DB -> ReadOptions -> (Iterator -> m a) -> m a+withIter db opts = bracket (createIter db opts) releaseIter  -- | An iterator is either positioned at a key/value pair, or not valid. This -- function returns /true/ iff the iterator is valid.@@ -164,52 +159,6 @@             else do                 err <- peekCString erra                 return . Just . BC.pack $ err---- | Map a function over an iterator, advancing the iterator forward and--- returning the value. The iterator should be put in the right position prior--- to calling the function.------ Note that this function accumulates the result strictly, ie. it reads all--- values into memory until the iterator is exhausted. This is most likely not--- what you want for large ranges. You may consider using conduits instead, for--- an example see: <https://gist.github.com/adc8ec348f03483446a5>-mapIter :: MonadIO m => (Iterator -> m a) -> Iterator -> m [a]-mapIter f iter@(Iterator iter_ptr _) = go []-  where-    go acc = do-        valid <- liftIO $ c_leveldb_iter_valid iter_ptr-        if valid == 0-            then return acc-            else do-                val <- f iter-                ()  <- liftIO $ c_leveldb_iter_next iter_ptr-                go (val : acc)-{-# DEPRECATED mapIter "will be removed in the next release" #-}---- | Return a list of key and value tuples from an iterator. The iterator--- should be put in the right position prior to calling this with the iterator.------ See strictness remarks on 'mapIter'.-iterItems :: (Functor m, MonadIO m) => Iterator -> m [(ByteString, ByteString)]-iterItems iter = catMaybes <$> mapIter iterEntry iter-{-# DEPRECATED iterItems "will be removed in the next release" #-}---- | Return a list of key from an iterator. The iterator should be put--- in the right position prior to calling this with the iterator.------ See strictness remarks on 'mapIter'-iterKeys :: (Functor m, MonadIO m) => Iterator -> m [ByteString]-iterKeys iter = catMaybes <$> mapIter iterKey iter-{-# DEPRECATED iterKeys "will be removed in the next release" #-}---- | Return a list of values from an iterator. The iterator should be put--- in the right position prior to calling this with the iterator.------ See strictness remarks on 'mapIter'-iterValues :: (Functor m, MonadIO m) => Iterator -> m [ByteString]-iterValues iter = catMaybes <$> mapIter iterValue iter-{-# DEPRECATED iterValues "will be removed in the next release" #-}-  -- -- Internal
src/Database/LevelDB/MonadResource.hs view
@@ -60,10 +60,6 @@     , iterKey     , iterValue     , iterGetError-    , mapIter-    , iterItems-    , iterKeys-    , iterValues      -- * Re-exports     , MonadResource (..)@@ -244,43 +240,6 @@ -- Note that this captures somewhat severe errors such as a corrupted database. iterGetError :: MonadResource m => Iterator -> m (Maybe ByteString) iterGetError = Base.iterGetError----- | Map a function over an iterator, advancing the iterator forward and--- returning the value. The iterator should be put in the right position prior--- to calling the function.------ Note that this function accumulates the result strictly, ie. it reads all--- values into memory until the iterator is exhausted. This is most likely not--- what you want for large ranges. You may consider using conduits instead, for--- an example see: <https://gist.github.com/adc8ec348f03483446a5>-mapIter :: MonadResource m => (Iterator -> m a) -> Iterator -> m [a]-mapIter = Base.mapIter-{-# DEPRECATED mapIter "will be removed in the next release" #-}---- | Return a list of key and value tuples from an iterator. The iterator--- should be put in the right position prior to calling this with the iterator.------ See strictness remarks on 'mapIter'.-iterItems :: MonadResource m => Iterator -> m [(ByteString, ByteString)]-iterItems = Base.iterItems-{-# DEPRECATED iterItems "will be removed in the next release" #-}---- | Return a list of key from an iterator. The iterator should be put--- in the right position prior to calling this with the iterator.------ See strictness remarks on 'mapIter'-iterKeys :: MonadResource m => Iterator -> m [ByteString]-iterKeys = Base.iterKeys-{-# DEPRECATED iterKeys "will be removed in the next release" #-}---- | Return a list of values from an iterator. The iterator should be put--- in the right position prior to calling this with the iterator.------ See strictness remarks on 'mapIter'-iterValues :: MonadResource m => Iterator -> m [ByteString]-iterValues = Base.iterValues-{-# DEPRECATED iterValues "will be removed in the next release" #-}   -- | Return the runtime version of the underlying LevelDB library as a (major,
+ src/Database/LevelDB/Streaming.hs view
@@ -0,0 +1,133 @@+{-# LANGUAGE BangPatterns #-}++-- |+-- Module      : Database.LevelDB.Streaming+-- Copyright   : (c) 2014 Kim Altintop+-- License     : BSD3+-- Maintainer  : kim.altintop@gmail.com+-- Stability   : experimental+-- Portability : non-portable+--+-- High-level, "Data.List"-like, streaming interface to+-- "Database.LevelDB.Iterator".+--+-- This module contains types and functions to construct 'Stream's from+-- 'Database.LevelDB.Iterator.Iterator's, and re-exports the functions operating+-- on 'Stream's from "Data.Stream.Monadic".+--+-- __Note__ that most of the functions from the latter module are+-- (intentionally) conflicting with the "Prelude", it is thus recommended to+-- import this module qualified:+--+-- > import Database.LevelDB -- or Database.LevelDB.Base+-- > import qualified Database.LevelDB.Streaming as S++module Database.LevelDB.Streaming+    ( Slice     (..)+    , KeyRange  (..)+    , Direction (..)+    , Key+    , Value+    , Entry++    -- * Constructing streams+    , keySlice+    , entrySlice++    -- * Re-exports+    , module Data.Stream.Monadic+    )+where++import Control.Applicative+import Control.Monad.IO.Class+import Data.ByteString        (ByteString)+import Data.Stream.Monadic+import Database.LevelDB.Base++import Prelude hiding (drop, filter, foldl, map, mapM, mapM_, take)+++data Slice = Slice !Iterator (Maybe KeyRange) !Direction++data KeyRange+    = KeyRange { start :: !ByteString+               , end   :: ByteString -> Ordering+               }+    | AllKeys++data Direction = Asc | Desc+    deriving Show++type Key   = ByteString+type Value = ByteString+type Entry = (Key, Value)+++-- | Create a 'Stream' which yields only the keys of the given 'KeyRange' (in+-- the given 'Direction').+--+-- Since traversing the 'Stream' mutates the state of the underlying 'Iterator',+-- it is obviously __unsafe__ to share the latter (between threads, or when+-- 'Data.Stream.Monadic.zip'ping). Hence, it is __highly__ recommended to create+-- a new 'Iterator' for each 'Stream'.+keySlice :: (Applicative m, MonadIO m)+         => Iterator+         -> KeyRange+         -> Direction+         -> Stream m Key+keySlice i (KeyRange s e) d = Stream next (iterSeek i s >> pure i)+  where+    next it = do+        key <- iterKey it+        case key of+            Nothing -> pure Done+            Just k  -> case d of+                Asc  | e k < GT  -> Yield k <$> (iterNext it >> pure it)+                     | otherwise -> pure Done+                Desc | e k > EQ  -> Yield k <$> (iterPrev it >> pure it)+                     | otherwise -> pure Done++keySlice i AllKeys Asc = Stream next (iterFirst i >> pure i)+  where+    next it = iterKey it+          >>= maybe (pure Done) (\ k -> Yield k <$> (iterNext it >> pure it))++keySlice i AllKeys Desc = Stream next (iterLast i >> pure i)+  where+    next it = iterKey it+          >>= maybe (pure Done) (\ k -> Yield k <$> (iterPrev it >> pure it))++-- | Create a 'Stream' which yields key/value pairs of the given 'KeyRange' (in+-- the given 'Direction').+--+-- Since traversing the 'Stream' mutates the state of the underlying 'Iterator',+-- it is obviously __unsafe__ to share the latter (between threads, or when+-- 'Data.Stream.Monadic.zip'ping). Hence, it is __highly__ recommended to create+-- a new 'Iterator' for each 'Stream'.+entrySlice :: (Applicative m, MonadIO m)+           => Iterator+           -> KeyRange+           -> Direction+           -> Stream m Entry+entrySlice i (KeyRange s e) d = Stream next (iterSeek i s >> pure i)+  where+    next it = do+        entry <- iterEntry it+        case entry of+            Nothing       -> pure Done+            Just x@(!k,_) -> case d of+                Asc  | e k < GT  -> Yield x <$> (iterNext it >> pure it)+                     | otherwise -> pure Done+                Desc | e k > EQ  -> Yield x <$> (iterPrev it >> pure it)+                     | otherwise -> pure Done++entrySlice i AllKeys Asc = Stream next (iterFirst i >> pure i)+  where+    next it = iterEntry it+          >>= maybe (pure Done) (\ x -> Yield x <$> (iterNext it >> pure it))++entrySlice i AllKeys Desc = Stream next (iterLast i >> pure i)+  where+    next it = iterEntry it+          >>= maybe (pure Done) (\ x -> Yield x <$> (iterPrev it >> pure it))
+ test/Main.hs view
@@ -0,0 +1,8 @@+module Main (main) where++import qualified Test.Streaming as Streaming+import           Test.Tasty+++main :: IO ()+main = defaultMain $ testGroup "Tests" [ Streaming.tests ]