diff --git a/sgd.cabal b/sgd.cabal
--- a/sgd.cabal
+++ b/sgd.cabal
@@ -4,12 +4,12 @@
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: c76dc64057b0ad62fdd40b4496b01d9bdbf827e48db5996639347df6c4209224
+-- hash: 2f29d911949ea62e385d514bbbce417c880285e7f8cd576492e8444fb45ae1c8
 
 name:           sgd
-version:        0.8.0.0
+version:        0.8.0.1
 synopsis:       Stochastic gradient descent library
-description:    Please see the README on GitHub at <https://github.com/kawu/sgd#readme>
+description:    Stochastic gradient descent library. . Import "Numeric.SGD" to use the library.
 category:       Math
 homepage:       https://github.com/kawu/sgd#readme
 bug-reports:    https://github.com/kawu/sgd/issues
@@ -35,9 +35,7 @@
       Numeric.SGD.DataSet
       Numeric.SGD.Momentum
       Numeric.SGD.ParamSet
-      Numeric.SGD.Pipe
       Numeric.SGD.Sparse
-      Numeric.SGD.Sparse.Dataset
       Numeric.SGD.Sparse.Grad
       Numeric.SGD.Sparse.LogSigned
       Numeric.SGD.Sparse.Momentum
diff --git a/src/Numeric/SGD/Pipe.hs b/src/Numeric/SGD/Pipe.hs
deleted file mode 100644
--- a/src/Numeric/SGD/Pipe.hs
+++ /dev/null
@@ -1,169 +0,0 @@
-{-# LANGUAGE RecordWildCards #-}
-{-# LANGUAGE DeriveGeneric #-}
-
-
--- | Pipe-based interface
-
-
-module Numeric.SGD.Pipe where
---   ( Config(..)
---   , Method(..)
---   , sgd
---   , result
---   , every
---   , pipeSeq
---   , pipeRan
---   ) where
-
-
--- import           GHC.Generics (Generic)
--- import           Numeric.Natural (Natural)
--- 
--- import           Control.Monad (when, forM_)
--- 
--- import qualified System.Random as R
--- 
--- import qualified Data.IORef as IO
--- 
--- import qualified Pipes as P
--- import qualified Pipes.Prelude as P
--- import           Pipes ((>->))
--- 
--- import           Numeric.SGD.DataSet
--- import           Numeric.SGD.ParamSet
--- import qualified Numeric.SGD.AdaDelta as Ada
--- import qualified Numeric.SGD.Momentum as Mom
--- 
--- 
--- ------------------------------- 
--- -- Data
--- -------------------------------
--- 
--- 
--- -- | Top-level SGD configuration
--- data Config = Config
---   { iterNum :: Natural
---     -- ^ Number of iteration over the entire training dataset
---   , batchRandom :: Bool
---     -- ^ Should the mini-batch be selected at random?  If not, the subsequent
---     -- training elements will be picked sequentially.  Random selection gives
---     -- no guarantee of seeing each training sample in every epoch.  Use `False`
---     -- if unsure.
---   , method :: Method
---     -- ^ Selected SGD method
---   , reportPeriod :: Double
---     -- ^ How often the quality should be reported (with `1` meaning once per
---     -- pass over the training data)
---   } deriving (Show, Eq, Ord, Generic)
--- 
--- 
--- -- | Different SGD methods, together with the corresponding configurations
--- data Method
---   = AdaDelta {adaDeltaCfg :: Ada.Config}
---   | Momentum {momentumCfg :: Mom.Config}
---   deriving (Show, Eq, Ord, Generic)
--- 
--- 
--- ------------------------------- 
--- -- SGD
--- -------------------------------
--- 
--- 
--- -- | Pipe-based SGD.
--- sgd
---   :: (ParamSet p)
---   => Config
---   -> DataSet e
---   -> (e -> p -> p)
---     -- ^ Network gradient on a sample element
---   -> (e -> p -> Double)
---     -- ^ Value of the objective function on a sample element
---   -> p
---     -- ^ Initial parameter values
---   -> IO p
--- sgd Config{..} dataSet grad0 quality0 net0 = do
---   let sgdPipe =
---         case method of
---           Momentum cfg -> Mom.momentum cfg
---             -- (cfg {Mom.tau = iterScale (Mom.tau cfg)})
---             grad0 net0
---           AdaDelta cfg -> Ada.adaDelta cfg grad0 net0
---   report net0
---   result net0 $ pipeSeq dataSet
---     >-> sgdPipe
---     >-> P.take realIterNum
---     >-> every realReportPeriod report
---   where
---     -- Iteration scaling
---     iterScale x = fromIntegral (size dataSet) * x
---     -- Number of iterations and reporting period
---     realIterNum = ceiling $ iterScale (fromIntegral iterNum :: Double)
---     realReportPeriod = ceiling $ iterScale reportPeriod
---     -- Network quality over the entire training dataset
---     report net = do
---       putStr . show =<< quality net
---       putStrLn $ " (norm_2 = " ++ show (norm_2 net) ++ ")"
---     quality net = do
---       res <- IO.newIORef 0.0
---       forM_ [0 .. size dataSet - 1] $ \ix -> do
---         x <- elemAt dataSet ix
---         IO.modifyIORef' res (+ quality0 x net)
---       IO.readIORef res
--- 
--- 
--- ------------------------------- 
--- -- Dataset producers
--- -------------------------------
--- 
--- 
--- -- | Pipe the dataset sequentially in a loop.
--- pipeSeq :: DataSet e -> P.Producer e IO ()
--- pipeSeq dataSet = do
---   go (0 :: Int)
---   where
---     go k
---       | k >= size dataSet = go 0
---       | otherwise = do
---           x <- P.lift $ elemAt dataSet k
---           P.yield x
---           go (k+1)
--- 
--- 
--- -- | Pipe the dataset randomly in a loop.
--- pipeRan :: DataSet e -> P.Producer e IO ()
--- pipeRan dataSet = do
---   x <- P.lift $ do
---     ix <- R.randomRIO (0, size dataSet - 1)
---     elemAt dataSet ix
---   P.yield x
---   pipeRan dataSet
--- 
--- 
--- ------------------------------- 
--- -- Utils
--- -------------------------------
--- 
--- 
--- -- | Extract the result of the SGD calculation (the last parameter
--- -- set flowing downstream).
--- result
---   :: (Monad m)
---   => p     
---     -- ^ Default value (in case the stream is empty)
---   -> P.Producer p m ()
---     -- ^ Stream of parameter sets
---   -> m p
--- result pDef = fmap (maybe pDef id) . P.last
--- 
--- 
--- -- | Report every `k`-th parameter set flowing downstream.
--- every :: (Monad m) => Int -> (p -> m ()) -> P.Pipe p p m x
--- every k report = do
---   go (1 `mod` k)
---   where
---     go i = do
---       paramSet <- P.await
---       when (i == 0) $ do
---         P.lift $ report paramSet
---       P.yield paramSet
---       go $ (i+1) `mod` k
diff --git a/src/Numeric/SGD/Sparse/Dataset.hs b/src/Numeric/SGD/Sparse/Dataset.hs
deleted file mode 100644
--- a/src/Numeric/SGD/Sparse/Dataset.hs
+++ /dev/null
@@ -1,124 +0,0 @@
-{-# LANGUAGE RecordWildCards #-}
-
-
--- | Dataset abstraction.
-
-
-module Numeric.SGD.Sparse.Dataset
-( 
--- * Dataset
-  Dataset (..)
--- * Reading
-, loadData
-, sample
--- * Construction
-, withVect
-, withDisk
-, withData
-) where
-
-
-import           Control.Monad (forM_)
-import           Data.Binary (Binary, encodeFile, decode)
-import qualified Data.ByteString as B
-import qualified Data.ByteString.Lazy as BL
-import           System.IO.Temp (withTempDirectory)
-import           System.IO.Unsafe (unsafeInterleaveIO)
-import           System.FilePath ((</>))
-import qualified System.Random as R
-import qualified Data.Vector as V
-import qualified Control.Monad.State.Strict as S
-
-
--- | A dataset with elements of type @a@.
-data Dataset a = Dataset {
-    -- | A size of the dataset.
-      size      :: Int
-    -- | Get dataset element with a given index.  The set of indices
-    -- is of a {0, 1, .., size - 1} form.
-    , elemAt    :: Int -> IO a 
-    }
-
-
--------------------------------------------
--- Reading
--------------------------------------------
-
-
--- | Lazily load dataset from a disk.
-loadData :: Dataset a -> IO [a]
-loadData Dataset{..} = lazyMapM elemAt [0 .. size - 1]
-
-
--- | A dataset sample of the given size.
-sample :: R.RandomGen g => g -> Int -> Dataset a -> IO ([a], g)
-sample g 0 _       = return ([], g)
-sample g n dataset = do
-    (xs, g') <- sample g (n-1) dataset
-    let (i, g'') = R.next g'
-    x <- dataset `elemAt` (i `mod` size dataset)
-    return (x:xs, g'')
-
-
--------------------------------------------
--- Construction
--------------------------------------------
-
-
--- | Construct dataset from a vector of elements and run the
--- given handler.
-withVect :: [a] -> (Dataset a -> IO b) -> IO b
-withVect xs handler =
-    handler dataset
-  where
-    v = V.fromList xs
-    dataset = Dataset
-        { size      = V.length v
-        , elemAt    = \k -> return (v V.! k) }
-
-
--- | Construct dataset from a list of elements, store it on a disk
--- and run the given handler.
-withDisk :: Binary a => [a] -> (Dataset a -> IO b) -> IO b
-withDisk xs handler = withTempDirectory "." ".sgd" $ \tmpDir -> do
-    -- We use state monad to compute the number of dataset elements. 
-    n <- flip S.execStateT 0 $ forM_ (zip xs [0 :: Int ..]) $ \(x, ix) -> do
-        S.lift $ encodeFile (tmpDir </> show ix) x
-        S.modify (+1)
-
-    -- We need to avoid decodeFile laziness when using some older
-    -- versions of the binary library.
-    let at ix = do
-          cs <- B.readFile (tmpDir </> show ix)
-          return . decode $ BL.fromChunks [cs]
-
-    handler $ Dataset {size = n, elemAt = at}
-
-
--- | Use disk or vector dataset representation depending on
--- the first argument: when `True`, use `withDisk`, otherwise
--- use `withVect`.
-withData :: Binary a => Bool -> [a] -> (Dataset a -> IO b) -> IO b
-withData x = case x of
-    True    -> withDisk
-    False   -> withVect
-
-
--------------------------------------------
--- Lazy IO Utils
--------------------------------------------
-
-
--- | Lazily evaluate each action in the sequence from left to right,
--- and collect the results.
-lazySequence :: [IO a] -> IO [a]
-lazySequence (mx:mxs) = do
-    x   <- mx
-    xs  <- unsafeInterleaveIO (lazySequence mxs)
-    return (x : xs)
-lazySequence [] = return []
-
-
--- | `lazyMapM` f is equivalent to `lazySequence` . `map` f.
-lazyMapM :: (a -> IO b) -> [a] -> IO [b]
-lazyMapM f = lazySequence . map f
