diff --git a/benchmark/Main.hs b/benchmark/Main.hs
new file mode 100644
--- /dev/null
+++ b/benchmark/Main.hs
@@ -0,0 +1,19 @@
+module Main where
+
+import Prelude
+import Criterion.Main
+import Potoki.Core.IO
+import qualified Potoki.Core.Produce as Produce
+import qualified Potoki.Core.Consume as Consume
+import qualified Potoki.Core.Transform as Transform
+
+
+main =
+  defaultMain $
+  [
+    bench "extractLinesTransform" $ whnfIO $ produceAndConsume
+      (Produce.fileBytes "data/2.tsv")
+      (right' (Consume.transform
+        (Transform.extractLines)
+        (Consume.count)))
+  ]
diff --git a/library/Potoki/Core/Transform.hs b/library/Potoki/Core/Transform.hs
--- a/library/Potoki/Core/Transform.hs
+++ b/library/Potoki/Core/Transform.hs
@@ -14,6 +14,7 @@
   just,
   list,
   vector,
+  chunk,
   distinctBy,
   distinct,
   executeIO,
diff --git a/library/Potoki/Core/Transform/Basic.hs b/library/Potoki/Core/Transform/Basic.hs
--- a/library/Potoki/Core/Transform/Basic.hs
+++ b/library/Potoki/Core/Transform/Basic.hs
@@ -7,6 +7,8 @@
 import qualified Data.HashSet as C
 import qualified Data.Vector as P
 import qualified Acquire.Acquire as M
+import qualified Data.Vector.Generic.Mutable as MutableGenericVector
+import qualified Data.Vector.Generic as GenericVector
 
 
 {-# INLINE mapFilter #-}
@@ -88,6 +90,48 @@
               writeIORef indexRef 0
               loop
             Nothing -> return Nothing
+      in loop
+
+{-|
+Chunk to vectors of the given size.
+Useful as a precursor of 'concurrently' in cases where the lifted transform's iteration is too light.
+-}
+{-# INLINABLE chunk #-}
+chunk :: Int -> Transform a (Vector a)
+chunk size = if size < 1
+  then Transform $ const $ liftIO $ return $ empty
+  else Transform $ \ (Fetch fetch) -> liftIO $ do
+    mvec <- MutableGenericVector.new size
+    cursor <- newIORef 0
+    activeVar <- newIORef True
+    return $ Fetch $ let
+      loop = do
+        active <- readIORef activeVar
+        if active
+          then do
+            fetchingResult <- fetch
+            case fetchingResult of
+              Just !a -> do
+                index <- readIORef cursor
+                MutableGenericVector.unsafeWrite mvec index a
+                let !nextIndex = succ index
+                if nextIndex == size
+                  then do
+                    writeIORef cursor 0
+                    !vec <- GenericVector.freeze mvec
+                    return (Just vec)
+                  else do
+                    writeIORef cursor nextIndex
+                    loop
+              Nothing -> do
+                writeIORef activeVar False
+                index <- readIORef cursor
+                if index > 0
+                  then do
+                    !vec <- GenericVector.freeze (MutableGenericVector.unsafeSlice 0 index mvec)
+                    return (Just vec)
+                  else return Nothing
+          else return Nothing
       in loop
 
 {-# INLINE distinctBy #-}
diff --git a/library/Potoki/Core/Transform/Concurrency.hs b/library/Potoki/Core/Transform/Concurrency.hs
--- a/library/Potoki/Core/Transform/Concurrency.hs
+++ b/library/Potoki/Core/Transform/Concurrency.hs
@@ -103,7 +103,6 @@
   Transform $ \ fetchIO -> liftIO $ do
     chan <- newTBQueueIO (workersAmount * 2)
     workersCounter <- newTVarIO workersAmount
-    fetchingAvailableVar <- newTVarIO True
 
     replicateM_ workersAmount $ forkIO $ do
       (A.Fetch fetchIO, finalize) <- case syncTransformIO fetchIO of M.Acquire io -> io
diff --git a/potoki-core.cabal b/potoki-core.cabal
--- a/potoki-core.cabal
+++ b/potoki-core.cabal
@@ -1,5 +1,5 @@
 name: potoki-core
-version: 2.2.10
+version: 2.2.11
 synopsis: Low-level components of "potoki"
 description:
   Provides everything required for building custom instances of
@@ -63,9 +63,9 @@
     unordered-containers >=0.2 && <0.3,
     vector >=0.12 && <0.13
 
-test-suite tests
+test-suite test
   type: exitcode-stdio-1.0
-  hs-source-dirs: tests
+  hs-source-dirs: test
   main-is: Main.hs
   default-extensions: Arrows, BangPatterns, ConstraintKinds, DataKinds, DefaultSignatures, DeriveDataTypeable, DeriveFoldable, DeriveFunctor, DeriveGeneric, DeriveTraversable, EmptyDataDecls, FlexibleContexts, FlexibleInstances, FunctionalDependencies, GADTs, GeneralizedNewtypeDeriving, InstanceSigs, LambdaCase, LiberalTypeSynonyms, MagicHash, MultiParamTypeClasses, MultiWayIf, NoImplicitPrelude, NoMonomorphismRestriction, OverloadedStrings, PatternGuards, ParallelListComp, QuasiQuotes, RankNTypes, RecordWildCards, ScopedTypeVariables, StandaloneDeriving, TemplateHaskell, TupleSections, TypeFamilies, TypeOperators, UnboxedTuples
   default-language: Haskell2010
@@ -77,6 +77,7 @@
     attoparsec,
     foldl >=1.3.7 && <1.4,
     ilist >=0.3.1.0 && <0.4,
+    split >=0.2.3.3 && <0.3,
     potoki-core,
     QuickCheck >=2.8.1 && <3,
     quickcheck-instances >=0.3.11 && <0.4,
@@ -85,3 +86,15 @@
     tasty >=1.0.1 && <1.2,
     tasty-hunit >=0.10 && <0.11,
     tasty-quickcheck >=0.10 && <0.11
+
+benchmark benchmark
+  type: exitcode-stdio-1.0
+  hs-source-dirs: benchmark
+  default-extensions: Arrows, BangPatterns, ConstraintKinds, DataKinds, DefaultSignatures, DeriveDataTypeable, DeriveFoldable, DeriveFunctor, DeriveGeneric, DeriveTraversable, EmptyDataDecls, FlexibleContexts, FlexibleInstances, FunctionalDependencies, GADTs, GeneralizedNewtypeDeriving, InstanceSigs, LambdaCase, LiberalTypeSynonyms, MagicHash, MultiParamTypeClasses, MultiWayIf, NoImplicitPrelude, NoMonomorphismRestriction, OverloadedStrings, PatternGuards, ParallelListComp, QuasiQuotes, RankNTypes, RecordWildCards, ScopedTypeVariables, StandaloneDeriving, TemplateHaskell, TupleSections, TypeFamilies, TypeOperators, UnboxedTuples
+  default-language: Haskell2010
+  ghc-options: -O2 -threaded "-with-rtsopts=-N -A64M"
+  main-is: Main.hs
+  build-depends:
+    criterion >=1.5.1 && <2,
+    potoki-core,
+    rerebase >=1 && <2
diff --git a/test/Main.hs b/test/Main.hs
new file mode 100644
--- /dev/null
+++ b/test/Main.hs
@@ -0,0 +1,229 @@
+module Main where
+
+import Prelude hiding (first, second)
+import Control.Arrow
+import Test.QuickCheck.Instances
+import Test.QuickCheck.Monadic as M
+import Test.Tasty
+import Test.Tasty.Runners
+import Test.Tasty.HUnit
+import Test.Tasty.QuickCheck
+import qualified Potoki.Core.IO as C
+import qualified Potoki.Core.Consume as D
+import qualified Potoki.Core.Transform as A
+import qualified Potoki.Core.Produce as E
+import qualified Potoki.Core.Fetch as Fe
+import qualified Data.Attoparsec.ByteString.Char8 as B
+import qualified Data.ByteString as F
+import qualified Data.Vector as G
+import qualified System.Random as H
+import qualified Acquire.Acquire as Ac
+import Potoki
+import Transform
+
+main =
+  defaultMain $
+  testGroup "All tests" $
+  [
+    testCase "extractLinesWithoutTrail" $ do
+      assertEqual "" ["ab", "", "cd"] =<<
+        C.produceAndConsume (E.transform A.extractLinesWithoutTrail (E.list ["a", "b\n", "\nc", "d\n"])) D.list
+    ,
+    testCase "extractLines" $ do
+      assertEqual "" ["ab", "", "cd", ""] =<<
+        C.produceAndConsume (E.transform A.extractLines (E.list ["a", "b\n", "\nc", "d\n"])) D.list
+    ,
+    testProperty "list to list" $ \ (list :: [Int]) ->
+    list === unsafePerformIO (C.produceAndConsume (E.list list) D.list)
+    ,
+    testProperty "consecutive consumers" $ \ (list :: [Int], amount) ->
+    list === unsafePerformIO (C.produceAndConsume (E.list list) ((++) <$> D.transform (A.take amount) D.list <*> D.list))
+    ,
+    potoki
+    ,
+    transform
+    ,
+    resourceChecker
+    ,
+    testCase "sync resource checker" $ do
+      resourceVar <- newIORef Initial
+      let produce = syncCheckResource resourceVar
+      C.produceAndConsume produce D.sum
+      fin <- readIORef resourceVar
+      assertEqual "" Released fin
+    ,
+    testCase "async resource checker" $ do
+      resourceVar <- newTVarIO Initial
+      let produce = asyncCheckResource resourceVar
+      potokiThreadId <- forkIO $ do
+          C.produceAndConsume produce D.sum
+          return ()
+      atomically $ do
+        resource <- readTVar resourceVar
+        guard $ resource == Acquired
+      killThread potokiThreadId
+      atomically $ do
+        resource <- readTVar resourceVar
+        guard $ resource == Released
+    ,
+    testProperty "Produce.transform resource checker" $ \ (list :: [Int]) ->
+    let prod = E.list list
+    in monadicIO $ do
+      check <- run $ do
+        resourceVar1 <-  newIORef Initial
+        res <- C.produceAndConsume (E.transform (checkTransform resourceVar1) prod) D.sum
+        readIORef resourceVar1
+      M.assert $ check == Released
+    ,
+    testProperty "Consume.transform resource checker" $ \ (list :: [Int]) ->
+    let prod = E.list list
+    in monadicIO $ do
+      check <- run $ do
+        resourceVar1 <-  newIORef Initial
+        res <- C.produceAndConsume prod (D.transform (checkTransform resourceVar1) D.sum)
+        readIORef resourceVar1
+      M.assert $ check == Released
+    ,
+    testCase "Transform.produce resource checker #1" $ do
+      resourceVar <- newIORef Initial
+      let prod = checkProduce resourceVar (/= Released) 100
+      res1 <- C.produceAndConsume (E.transform (A.produce intToProduce) prod) D.sum
+      res2 <- C.produceAndConsume prod (D.transform (A.produce intToProduce) D.sum)
+      fin <- readIORef resourceVar
+      assertEqual "" Released fin
+      assertEqual "" res1 res2
+    ,
+    testCase "Transform.produce resource checker #2" $ do
+      resourceVar <- newIORef Initial
+      let prod = checkProduce resourceVar (/= Released) 100
+      res1 <- C.produceAndConsume (E.transform (A.take 5 >>> A.produce intToProduce) prod) D.sum
+      res2 <- C.produceAndConsume prod (D.transform (A.take 5 >>> A.produce intToProduce) D.sum)
+      fin <- readIORef resourceVar
+      assertEqual "" Released fin
+      assertEqual "" res1 res2
+  ]
+
+resourceChecker :: TestTree
+resourceChecker =
+  testGroup "produce1 >>= produce2" $
+  [
+    testCase "Check produce binding" $ do
+      resourceVar1 <- newIORef Initial
+      resourceVar2 <- newIORef Initial
+      let prod1 = checkProduce resourceVar1 (const True) 100
+          prod2 = \x -> checkProduce resourceVar2 (/= Released) x
+      res <- C.produceAndConsume (prod1 >>= prod2) D.sum
+      fin <- readIORef resourceVar1
+      assertEqual "" Released fin
+    ,
+    testProperty "Bind for produce" $ \ (list :: [Int]) ->
+    let check = list >>= (enumFromTo 0)
+        prod1 = E.list list
+        prod2 = \x -> E.list $ enumFromTo 0 x
+    in monadicIO $ do
+      res <- run $ C.produceAndConsume (prod1 >>= prod2) D.list
+      M.assert (check == res)
+    ,
+    testProperty "liftIO for Produce. Consume0" $ \ (_ :: Int) ->
+    monadicIO $ do
+      check <- run $ do
+        checkVar <- newIORef False
+        let prod = liftIO $ writeIORef checkVar True
+        C.produceAndConsume prod someThing
+        readIORef checkVar
+      M.assert $ check == False
+    ,
+    testProperty "liftIO for Produce. ConsumeN" $ \ (n :: Int) ->
+    monadicIO $ do
+      let prod = liftIO (return n)
+      len <- run (C.produceAndConsume prod D.count)
+      M.assert (len == 1)
+    ]
+
+intToProduce :: Int -> E.Produce Int
+intToProduce a = E.Produce . Ac.Acquire $ do
+  stVar <- newIORef 0
+  return $ flip (,) (return ()) $ Fe.Fetch $ do
+      n <- readIORef stVar
+      if n >= a + 1 then (return Nothing)
+      else do
+        writeIORef stVar $! n + 1
+        return (Just n)
+
+someThing :: D.Consume input Int
+someThing = D.Consume $ \ (Fe.Fetch _) -> return 0
+
+single :: Foldable f => f a -> Maybe a
+single = join . (foldr' f Nothing)
+  where
+    f x Nothing = Just $ Just x
+    f _ _       = Just Nothing
+
+data Resource
+  = Initial
+  | Acquired
+  | Released
+  | AcquiredImproperly
+  | ReleasedImproperly
+  deriving (Show, Eq)
+
+checkTransform :: IORef Resource -> A.Transform Int Int
+checkTransform resourceVar = A.Transform $ \ fetchIO -> Ac.Acquire $ do
+  writeIORef resourceVar Acquired
+  return $ (,) (plusFetch fetchIO) $ do
+      res <- readIORef resourceVar
+      case res of
+        Acquired -> writeIORef resourceVar Released
+        _ -> writeIORef resourceVar ReleasedImproperly
+
+plusFetch :: Fe.Fetch Int -> Fe.Fetch Int
+plusFetch (Fe.Fetch fetchIO) = Fe.Fetch $ do
+  fetch <- fetchIO
+  return $ fmap succ fetch
+
+checkProduce :: IORef Resource -> (Resource -> Bool) -> Int -> E.Produce Int
+checkProduce resourceVar f k = E.Produce . Ac.Acquire $ do
+  res <- readIORef resourceVar
+  if f res && res /= Initial
+    then do
+      return $ (,) (Fe.Fetch $ return Nothing) $ writeIORef resourceVar AcquiredImproperly
+    else do
+      writeIORef resourceVar Acquired
+      stVar <- newIORef 0
+      let fetch = do
+            n <- readIORef stVar
+            if n >= k then return (Nothing)
+            else do
+              writeIORef stVar $! n + 1
+              return (Just n)
+      return $ (,) (Fe.Fetch fetch) $ do
+          res <- readIORef resourceVar
+          case res of
+            Acquired -> writeIORef resourceVar Released
+            _ -> writeIORef resourceVar ReleasedImproperly
+
+asyncCheckResource :: TVar Resource -> E.Produce Int
+asyncCheckResource resourceVar = E.Produce . Ac.Acquire $ do
+  atomically $ writeTVar resourceVar Acquired
+  stVar <- newIORef 0
+  let fetch = do
+        n <- readIORef stVar
+        writeIORef stVar $! n + 1
+        return (Just n)
+  return (Fe.Fetch fetch, atomically $ writeTVar resourceVar Released)
+
+syncCheckResource :: IORef Resource -> E.Produce Int
+syncCheckResource resourceVar = E.Produce . Ac.Acquire $ do
+  writeIORef resourceVar Acquired
+  stVar <- newIORef 0
+  let fetch = do
+        n <- readIORef stVar
+        if n >= 1000 then return (Nothing)
+        else do
+          writeIORef stVar $! n + 1
+          return (Just n)
+  return $ (,) (Fe.Fetch fetch) $ do
+      res <- readIORef resourceVar
+      case res of
+        Acquired -> writeIORef resourceVar Released
+        _ -> writeIORef resourceVar ReleasedImproperly
diff --git a/test/Potoki.hs b/test/Potoki.hs
new file mode 100644
--- /dev/null
+++ b/test/Potoki.hs
@@ -0,0 +1,279 @@
+module Potoki where
+
+import Prelude hiding (first, second)
+import Control.Arrow
+import Test.QuickCheck.Instances
+import Test.QuickCheck.Monadic as M
+import Test.Tasty
+import Test.Tasty.Runners
+import Test.Tasty.HUnit
+import Test.Tasty.QuickCheck
+import qualified Control.Foldl as Fl
+import qualified Potoki.Core.IO as C
+import qualified Potoki.Core.Consume as D
+import qualified Potoki.Core.Transform as A
+import qualified Potoki.Core.Produce as E
+import qualified Data.Attoparsec.ByteString.Char8 as B
+import qualified Data.ByteString as F
+import qualified Data.Vector as G
+import qualified System.Random as H
+import Data.List.Index (indexed)
+
+potoki :: TestTree
+potoki =
+  testGroup "All tests for potoki's end-users functions" $
+  [
+    testCase "vector to list" $ do
+      result <- C.produceAndConsume (E.vector (G.fromList [1,2,3])) (D.list)
+      assertEqual "" [1,2,3] result
+    ,
+    testCase "just" $ do
+      result <- C.produceAndConsume (E.list [Just 1, Nothing, Just 2]) (D.transform A.just D.list)
+      assertEqual "" [1,2] result
+    ,
+    testCase "transform,consume,take" $ do
+      let
+        transform = A.consume (D.transform (A.take 3) D.list)
+        consume = D.transform transform D.list
+        produceAndConsume list = C.produceAndConsume (E.list list) (consume)
+      assertEqual "" [[1,2,3], [4,5,6], [7,8]] =<< produceAndConsume [1,2,3,4,5,6,7,8]
+      assertEqual "" [[1,2,3], [4,5,6], [7,8,9]] =<< produceAndConsume [1,2,3,4,5,6,7,8,9]
+      assertEqual "" [] =<< produceAndConsume ([] :: [Int])
+    ,
+    testCase "File reading" $ do
+      let produce =
+            E.transform (arr (either (const Nothing) Just) >>> A.just) $
+            E.fileBytes "samples/1"
+      result <- C.produceAndConsume produce (fmap F.length D.concat)
+      assertEqual "" 17400 result
+    ,
+    transformPotoki
+    ,
+    parsingPotoki
+    ,
+    consumePotoki
+  ]
+
+
+transformPotoki :: TestTree
+transformPotoki =
+  testGroup "Transform" $
+  [
+    testCase "Order" $ do
+      let
+        list = [Left 1, Left 2, Right 'z', Left 2, Right 'a', Left 1, Right 'b', Left 0, Right 'x', Left 4, Left 3]
+        transform = left (A.consume (D.transform (A.take 2) D.sum))
+      result <- C.produceAndConsume (E.list list) (D.transform transform D.list)
+      assertEqual "" [Left 3, Right 'z', Left 2, Right 'a', Left 1, Right 'b', Left 0, Right 'x', Left 7] result
+    ,
+    testCase "Interrupted order" $ do
+      let
+        list = [Left 1, Left 2, Right 'a']
+        transform = left (A.consume (D.transform (A.take 3) D.sum))
+      result <- C.produceAndConsume (E.list list) (D.transform transform D.list)
+      assertEqual "" [Left 3, Right 'a'] result
+    ,
+    testCase "Distinct" $ do
+      let
+        list = [1,2,3,2,3,2,1,4,1] :: [Int]
+      result <- C.produceAndConsume (E.list list) (D.transform A.distinct D.list)
+      assertEqual "" [1,2,3,4] result
+    ,
+    testCase "Distinct By" $ do
+      let
+        list = [(1, ""),(2, ""),(3, ""),(2, ""),(3, ""),(2, ""),(1, ""),(4, ""),(1, "")] :: [(Int, String)]
+      result <- C.produceAndConsume (E.list list) (D.transform (A.distinctBy fst) D.list)
+      assertEqual "" [(1, ""),(2, ""),(3, ""),(4, "")] result
+    ,
+    testCase "Concurrently" $ do
+      let
+        list = [1..20000]
+        produce = E.list list
+        transform =
+          A.concurrently 12 $
+          arr (\ x -> H.randomRIO (0, 100) >>= threadDelay >> return x) >>>
+          A.executeIO
+        consume = D.transform transform D.list
+      result <- C.produceAndConsume produce consume
+      assertBool "Is dispersed" (list /= result)
+      assertEqual "Contains no duplicates" 0 (length result - length (nub result))
+      assertEqual "Equals the original once sorted" list (sort result)
+    ,
+    testProperty "Line" $ \ chunks ->
+    let
+      expected =
+        mconcat chunks
+      actual =
+        unsafePerformIO (C.produceAndConsume produce consume)
+        where
+          produce =
+            E.list chunks
+          consume =
+            rmap (mconcat . intersperse "\n") $
+            D.transform A.extractLines D.list
+      in expected === actual
+    ,
+    testProperty "takeWhile" $ \ (list :: [Int]) ->
+    let listPart = takeWhile odd list
+    in monadicIO $ do
+      let prod = E.list list
+      res <- run (C.produceAndTransformAndConsume prod (A.takeWhile odd) D.list)
+      M.assert (res == listPart)
+    ,
+    testProperty "mapFilter" $ \ (list :: [Int]) ->
+    let in2MaybeOut input =
+          if input `mod` 4 == 0
+            then Just $ input `mod` 4
+            else Nothing
+        filteredList = map fromJust . filter (/= Nothing) . map in2MaybeOut $ list
+    in monadicIO $ do
+      let prod = E.list list
+      res <- run (C.produceAndTransformAndConsume prod (A.mapFilter in2MaybeOut) D.list)
+      M.assert (res == filteredList)
+    ,
+    testProperty "filter" $ \ (list :: [Int]) ->
+    let filteredList = filter even list
+    in monadicIO $ do
+      let prod = E.list list
+      res <- run (C.produceAndTransformAndConsume prod (A.filter even) D.list)
+      M.assert (res == filteredList)
+  ]
+
+parsingPotoki :: TestTree
+parsingPotoki =
+  testGroup "Parsing" $
+  [
+    testCase "Sample 1" $ do
+      let parser = B.double <* B.char ','
+          transform = arr (either (const Nothing) Just) >>> A.just >>> A.parseBytes parser
+          produce = E.transform transform (E.fileBytes "samples/1")
+      result <- C.produceAndConsume produce D.count
+      assertEqual "" 4350 result
+    ,
+    testCase "Sample 1 greedy" $ do
+      let parser = B.sepBy B.double (B.char ',')
+          transform = arr (either (const Nothing) Just) >>> A.just >>> A.parseBytes parser
+          produce = E.transform transform (E.fileBytes "samples/1")
+      result <- C.produceAndConsume produce D.list
+      assertEqual "" [Right 4350] (fmap (fmap length) result)
+    ,
+    testCase "Split chunk" $
+    let
+      produce = E.list ["1", "2", "3"]
+      parser = B.anyChar
+      transform = A.parseBytes parser >>> arr (either (const Nothing) Just) >>> A.just
+      consume = D.transform transform D.count
+      in do
+        assertEqual "" 3 =<< C.produceAndConsume produce consume
+  ]
+
+consumePotoki =
+  testGroup "Consume" $
+  [
+    testProperty "count" $ \ (list :: [Int]) ->
+    let n = length list
+    in monadicIO $ do
+      let prod = E.list list
+      len <- run (C.produceAndConsume prod D.count)
+      M.assert (len == n)
+    ,
+    testProperty "sum" $ \ (list :: [Int]) ->
+    let n = sum list
+    in monadicIO $ do
+      let prod = E.list list
+      len <- run (C.produceAndConsume prod D.sum)
+      M.assert (len == n)
+    ,
+    testProperty "head" $ \ (list :: [Int]) ->
+    let el = if null list then Nothing else (Just (head list))
+    in monadicIO $ do
+      let prod = E.list list
+      he <- run (C.produceAndConsume prod D.head)
+      M.assert (he == el)
+    ,
+    testProperty "last" $ \ (list :: [Int]) ->
+    let el = if null list then Nothing else (Just (last list))
+    in monadicIO $ do
+      let prod = E.list list
+      he <- run (C.produceAndConsume prod D.last)
+      M.assert (he == el)
+    ,
+    testProperty "list" $ \ (list :: [Int]) ->
+    monadicIO $ do
+      let prod = E.list list
+      res <- run (C.produceAndConsume prod D.list)
+      M.assert (res == list)
+    ,
+    testProperty "reverseList" $ \ (list :: [Int]) ->
+    let revList = reverse list
+    in monadicIO $ do
+      let prod = E.list list
+      res <- run (C.produceAndConsume prod D.reverseList)
+      M.assert (res == revList)
+    ,
+    testProperty "vector" $ \ (list :: [Int]) ->
+    let vec = G.fromList list
+    in monadicIO $ do
+      let prod = E.list list
+      res <- run (C.produceAndConsume prod D.vector)
+      M.assert (res == vec)
+    ,
+    testProperty "concat" $ \ (list :: [[Int]]) ->
+    let con = concat list
+    in monadicIO $ do
+      let prod = E.list list
+      res <- run (C.produceAndConsume prod D.concat)
+      M.assert (res == con)
+    ,
+    testProperty "fold" $ \ (list :: [Int], fun :: (Fun (Int, Int) Int), first :: Int) ->
+    let f = applyFun2 fun
+        fol = foldl' f first list
+    in monadicIO $ do
+      let prod = E.list list
+      res <- run (C.produceAndConsume prod (D.fold (Fl.Fold f first id)))
+      M.assert (res == fol)
+    ,
+    testProperty "foldInIO" $ \ (list :: [Int]) ->
+    let fin = sum list
+    in monadicIO $ do
+      let prod = E.list list
+      res <- run $ do
+        sumVar <- newIORef 0
+        (C.produceAndConsume prod (D.foldInIO (Fl.FoldM (\_ a -> modifyIORef' sumVar (a+)) (pure ()) (\_ -> readIORef sumVar)) ) )
+      M.assert (res == fin)
+    ,
+    testProperty "folding" $ \ (list :: [Int], fun :: (Fun (Int, Int) Int), first :: Int) ->
+    let f = applyFun2 fun
+        fol = foldl' f first list
+    in monadicIO $ do
+      let prod = E.list list
+      res <- run (C.produceAndConsume prod (D.folding (Fl.Fold f first id) (D.sum) ))
+      a <- run (C.produceAndConsume prod D.sum)
+      M.assert (res == (fol, a))
+    ,
+    testProperty "foldingInIO" $ \ (list :: [Int]) ->
+    let fol = sum list
+    in monadicIO $ do
+      let prod = E.list list
+      res <- run $ do
+        sumVar <- newIORef 0
+        (C.produceAndConsume prod (D.foldingInIO (Fl.FoldM (\_ a -> modifyIORef' sumVar (a+)) (pure ()) (\_ -> readIORef sumVar)) (D.sum) ))
+      a <- run (C.produceAndConsume prod D.sum)
+      M.assert (res == (fol, a))
+    ,
+    testProperty "execState" $ \ (list :: [Int]) ->
+    let f = modify' . (+)
+        resL = sum list
+    in monadicIO $ do
+      let prod = E.list list
+      res <- run (C.produceAndConsume prod (D.execState f 0))
+      M.assert (res == resL)
+    ,
+    testProperty "Choice consume right'" $ \ (list :: [Either Bool Int]) ->
+    let n = sum <$> sequence list
+    in monadicIO $ do
+      let prod = E.list list
+      len <- run (C.produceAndConsume prod $ right' D.sum)
+      M.assert (len == n)
+  ]
+
diff --git a/test/Transform.hs b/test/Transform.hs
new file mode 100644
--- /dev/null
+++ b/test/Transform.hs
@@ -0,0 +1,190 @@
+module Transform where
+
+import Prelude hiding (first, second, choose)
+import Control.Arrow
+import Test.QuickCheck.Instances
+import Test.Tasty
+import Test.Tasty.Runners
+import Test.Tasty.HUnit
+import Test.Tasty.QuickCheck
+import qualified Potoki.Core.IO as C
+import qualified Potoki.Core.Consume as D
+import qualified Potoki.Core.Transform as A
+import qualified Potoki.Core.Produce as E
+import qualified Data.Attoparsec.ByteString.Char8 as B
+import qualified Data.ByteString as F
+import qualified Data.Vector as G
+import qualified Data.List.Split as SplitList
+import qualified System.Random as H
+
+transform :: TestTree
+transform =
+  testGroup "Transform" $
+  [
+    testProperty "Applying chunksOf to list has the same effect as the \"chunk\" transform" $ let
+      gen = do
+        list <- listOf (choose (0, 1000 :: Int))
+        chunkSize <- frequency [(1000, choose (1, 3)), (100, choose (4, 100)), (1, pure 0)]
+        traceShowM (list, chunkSize)
+        return (list, chunkSize)
+      in forAll gen $ \ (list, chunkSize) -> let
+        listChunks = if chunkSize < 1 then [] else SplitList.chunksOf chunkSize list
+        potokiChunks = unsafePerformIO $ C.produceAndTransformAndConsume (E.list list) (rmap toList (A.chunk chunkSize)) D.list
+        in listChunks === potokiChunks
+    ,
+    transformProduce
+    ,
+    transformChoice
+    ,
+    transformArrowLaws
+  ]
+
+transformProduce =
+  testGroup "Produce" $
+  [
+    testCase "1" $ do
+      let
+        list = [1, 2, 3] :: [Int]
+      result <- C.produceAndTransformAndConsume
+        (E.list list)
+        (A.produce (E.list . \ n -> flip replicate n n))
+        (D.list)
+      assertEqual "" [1, 2, 2, 3, 3, 3] result
+  ,
+    testCase "2" $ do
+      let
+        list = [1, 2, 3] :: [Int]
+      result <- C.produceAndTransformAndConsume
+        (E.list list)
+        (A.produce (E.list . \ n -> [(n, n)]))
+        (D.list)
+      assertEqual "" [(1, 1), (2, 2), (3, 3)] result
+  ,
+    testCase "3" $ do
+      let
+        list = [1, 2, 3] :: [Int]
+      result <- C.produceAndTransformAndConsume
+        (E.list list)
+        (A.produce (E.list . \ n -> [n, n]))
+        (D.list)
+      assertEqual "" [1, 1, 2, 2, 3, 3] result
+  ]
+
+transformChoice =
+  testGroup "Choice" $
+  [
+    testCase "1" $ do
+      let
+        list = [Left 1, Left 2, Right 'z', Left 2, Right 'a', Left 1, Right 'b', Left 0, Right 'x', Left 4, Left 3]
+        transform = left' id
+      result <- C.produceAndTransformAndConsume (E.list list) transform D.list
+      assertEqual "" [Left 1, Left 2, Right 'z', Left 2, Right 'a', Left 1, Right 'b', Left 0, Right 'x', Left 4, Left 3] result
+    ,
+    testCase "2" $ do
+      let
+        list = [Left 1, Left 2, Right 'z', Right 'a', Right 'b', Left 0, Right 'x', Left 4, Left 3]
+        transform = right' (A.consume D.list)
+      result <- C.produceAndTransformAndConsume (E.list list) transform D.list
+      assertEqual "" [Left 1, Left 2, Right "zab", Left 0, Right "x", Left 4, Left 3] result
+    ,
+    testCase "3" $ do
+      let
+        list = [Left 4, Right 'z', Right 'a', Left 3, Right 'b', Left 0, Left 1, Right 'x', Left 4, Left 3]
+        transform = left' (A.consume D.list)
+      result <- C.produceAndTransformAndConsume (E.list list) transform D.list
+      assertEqual "" [Left [4], Right 'z', Right 'a', Left [3], Right 'b', Left [0, 1], Right 'x', Left [4, 3]] result
+  ]
+
+transformArrowLaws =
+  testGroup "Arrow laws"
+  [
+    testGroup "Strong"
+    [
+      testCase "1" $ do
+        let
+          input = [(1,'a'),(2,'b'),(3,'c'),(4,'d')]
+          transform = first transform1
+        result <- C.produceAndTransformAndConsume (E.list input) transform D.list
+        assertEqual "" [(6,'c'),(4,'d')] result
+      ,
+      testCase "Lack of elements" $ do
+        let
+          input = [(1,'a'),(2,'b')]
+          transform = first transform1
+        result <- C.produceAndTransformAndConsume (E.list input) transform D.list
+        assertEqual "" [(3,'b')] result
+    ]
+    ,
+    transformProperty "arr id = id"
+      (arr id :: A.Transform Int Int)
+      id
+    ,
+    transformProperty "arr (f >>> g) = arr f >>> arr g"
+      (arr (f >>> g))
+      (arr f >>> arr g)
+    ,
+    transformProperty "first (arr f) = arr (first f)"
+      (first (arr f) :: A.Transform (Int, Char) (Int, Char))
+      (arr (first f))
+    ,
+    transformProperty "first (f >>> g) = first f >>> first g"
+      (first (transform1 >>> transform2) :: A.Transform (Int, Char) (Int, Char))
+      (first (transform1) >>> first (transform2))
+    ,
+    transformProperty "first f >>> arr fst = arr fst >>> f"
+      (first transform1 >>> arr fst :: A.Transform (Int, Char) Int)
+      (arr fst >>> transform1)
+    ,
+    transformProperty "first f >>> arr (id *** g) = arr (id *** g) >>> first f"
+      (first transform1 >>> arr (id *** g))
+      (arr (id *** g) >>> first transform1)
+    ,
+    transformProperty "first (first f) >>> arr assoc = arr assoc >>> first f"
+      (first (first transform1) >>> arr assoc :: A.Transform ((Int, Char), Double) (Int, (Char, Double)))
+      (arr assoc >>> first transform1)
+    ,
+    transformProperty "left (arr f) = arr (left f)"
+      (left (arr f) :: A.Transform (Either Int Char) (Either Int Char))
+      (arr (left f))
+    ,
+    transformProperty "left (f >>> g) = left f >>> left g"
+      (left (transform1 >>> transform2) :: A.Transform (Either Int Char) (Either Int Char))
+      (left (transform1) >>> left (transform2))
+    ,
+    transformProperty "f >>> arr Left = arr Left >>> left f"
+      (transform1 >>> arr Left :: A.Transform Int (Either Int Char))
+      (arr Left >>> left transform1)
+    ,
+    transformProperty "left f >>> arr (id +++ g) = arr (id +++ g) >>> left f"
+      (left transform1 >>> arr (id +++ g))
+      (arr (id +++ g) >>> left transform1)
+    ,
+    transformProperty "left (left f) >>> arr assocsum = arr assocsum >>> left f"
+      (left (left transform1) >>> arr assocsum :: A.Transform (Either (Either Int Char) Double) (Either Int (Either Char Double)))
+      (arr assocsum >>> left transform1)
+    ,
+    transformProperty "left (left (arr f)) >>> arr assocsum = arr assocsum >>> left (arr f)"
+      (left (left (arr f)) >>> arr assocsum :: A.Transform (Either (Either Int Char) Double) (Either Int (Either Char Double)))
+      (arr assocsum >>> left (arr f))
+  ]
+  where
+    f = (+24) :: Int -> Int
+    g = (*3) :: Int -> Int
+    transform1 = A.consume (D.transform (A.take 3) D.sum) :: A.Transform Int Int
+    transform2 = A.consume (D.transform (A.take 4) D.sum) :: A.Transform Int Int
+    assoc ((a,b),c) = (a,(b,c))
+    assocsum (Left (Left x)) = Left x
+    assocsum (Left (Right y)) = Right (Left y)
+    assocsum (Right z) = Right (Right z)
+
+transformProperty ::
+  (Arbitrary input, Show input, Eq output, Show output) =>
+  String -> A.Transform input output -> A.Transform input output -> TestTree
+transformProperty name leftTransform rightTransform =
+  testProperty name property
+  where
+    property list =
+      transform leftTransform === transform rightTransform
+      where
+        transform transform =
+          unsafePerformIO (C.produceAndTransformAndConsume (E.list list) transform D.list)
diff --git a/tests/Main.hs b/tests/Main.hs
deleted file mode 100644
--- a/tests/Main.hs
+++ /dev/null
@@ -1,229 +0,0 @@
-module Main where
-
-import Prelude hiding (first, second)
-import Control.Arrow
-import Test.QuickCheck.Instances
-import Test.QuickCheck.Monadic as M
-import Test.Tasty
-import Test.Tasty.Runners
-import Test.Tasty.HUnit
-import Test.Tasty.QuickCheck
-import qualified Potoki.Core.IO as C
-import qualified Potoki.Core.Consume as D
-import qualified Potoki.Core.Transform as A
-import qualified Potoki.Core.Produce as E
-import qualified Potoki.Core.Fetch as Fe
-import qualified Data.Attoparsec.ByteString.Char8 as B
-import qualified Data.ByteString as F
-import qualified Data.Vector as G
-import qualified System.Random as H
-import qualified Acquire.Acquire as Ac
-import Potoki
-import Transform
-
-main =
-  defaultMain $
-  testGroup "All tests" $
-  [
-    testCase "extractLinesWithoutTrail" $ do
-      assertEqual "" ["ab", "", "cd"] =<<
-        C.produceAndConsume (E.transform A.extractLinesWithoutTrail (E.list ["a", "b\n", "\nc", "d\n"])) D.list
-    ,
-    testCase "extractLines" $ do
-      assertEqual "" ["ab", "", "cd", ""] =<<
-        C.produceAndConsume (E.transform A.extractLines (E.list ["a", "b\n", "\nc", "d\n"])) D.list
-    ,
-    testProperty "list to list" $ \ (list :: [Int]) ->
-    list === unsafePerformIO (C.produceAndConsume (E.list list) D.list)
-    ,
-    testProperty "consecutive consumers" $ \ (list :: [Int], amount) ->
-    list === unsafePerformIO (C.produceAndConsume (E.list list) ((++) <$> D.transform (A.take amount) D.list <*> D.list))
-    ,
-    potoki
-    ,
-    transform
-    ,
-    resourceChecker
-    ,
-    testCase "sync resource checker" $ do
-      resourceVar <- newIORef Initial
-      let produce = syncCheckResource resourceVar
-      C.produceAndConsume produce D.sum
-      fin <- readIORef resourceVar
-      assertEqual "" Released fin
-    ,
-    testCase "async resource checker" $ do
-      resourceVar <- newTVarIO Initial
-      let produce = asyncCheckResource resourceVar
-      potokiThreadId <- forkIO $ do
-          C.produceAndConsume produce D.sum
-          return ()
-      atomically $ do
-        resource <- readTVar resourceVar
-        guard $ resource == Acquired
-      killThread potokiThreadId
-      atomically $ do
-        resource <- readTVar resourceVar
-        guard $ resource == Released
-    ,
-    testProperty "Produce.transform resource checker" $ \ (list :: [Int]) ->
-    let prod = E.list list
-    in monadicIO $ do
-      check <- run $ do
-        resourceVar1 <-  newIORef Initial
-        res <- C.produceAndConsume (E.transform (checkTransform resourceVar1) prod) D.sum
-        readIORef resourceVar1
-      M.assert $ check == Released
-    ,
-    testProperty "Consume.transform resource checker" $ \ (list :: [Int]) ->
-    let prod = E.list list
-    in monadicIO $ do
-      check <- run $ do
-        resourceVar1 <-  newIORef Initial
-        res <- C.produceAndConsume prod (D.transform (checkTransform resourceVar1) D.sum)
-        readIORef resourceVar1
-      M.assert $ check == Released
-    ,
-    testCase "Transform.produce resource checker #1" $ do
-      resourceVar <- newIORef Initial
-      let prod = checkProduce resourceVar (/= Released) 100
-      res1 <- C.produceAndConsume (E.transform (A.produce intToProduce) prod) D.sum
-      res2 <- C.produceAndConsume prod (D.transform (A.produce intToProduce) D.sum)
-      fin <- readIORef resourceVar
-      assertEqual "" Released fin
-      assertEqual "" res1 res2
-    ,
-    testCase "Transform.produce resource checker #2" $ do
-      resourceVar <- newIORef Initial
-      let prod = checkProduce resourceVar (/= Released) 100
-      res1 <- C.produceAndConsume (E.transform (A.take 5 >>> A.produce intToProduce) prod) D.sum
-      res2 <- C.produceAndConsume prod (D.transform (A.take 5 >>> A.produce intToProduce) D.sum)
-      fin <- readIORef resourceVar
-      assertEqual "" Released fin
-      assertEqual "" res1 res2
-  ]
-
-resourceChecker :: TestTree
-resourceChecker =
-  testGroup "produce1 >>= produce2" $
-  [
-    testCase "Check produce binding" $ do
-      resourceVar1 <- newIORef Initial
-      resourceVar2 <- newIORef Initial
-      let prod1 = checkProduce resourceVar1 (const True) 100
-          prod2 = \x -> checkProduce resourceVar2 (/= Released) x
-      res <- C.produceAndConsume (prod1 >>= prod2) D.sum
-      fin <- readIORef resourceVar1
-      assertEqual "" Released fin
-    ,
-    testProperty "Bind for produce" $ \ (list :: [Int]) ->
-    let check = list >>= (enumFromTo 0)
-        prod1 = E.list list
-        prod2 = \x -> E.list $ enumFromTo 0 x
-    in monadicIO $ do
-      res <- run $ C.produceAndConsume (prod1 >>= prod2) D.list
-      M.assert (check == res)
-    ,
-    testProperty "liftIO for Produce. Consume0" $ \ (_ :: Int) ->
-    monadicIO $ do
-      check <- run $ do
-        checkVar <- newIORef False
-        let prod = liftIO $ writeIORef checkVar True
-        C.produceAndConsume prod someThing
-        readIORef checkVar
-      M.assert $ check == False
-    ,
-    testProperty "liftIO for Produce. ConsumeN" $ \ (n :: Int) ->
-    monadicIO $ do
-      let prod = liftIO (return n)
-      len <- run (C.produceAndConsume prod D.count)
-      M.assert (len == 1)
-    ]
-
-intToProduce :: Int -> E.Produce Int
-intToProduce a = E.Produce . Ac.Acquire $ do
-  stVar <- newIORef 0
-  return $ flip (,) (return ()) $ Fe.Fetch $ do
-      n <- readIORef stVar
-      if n >= a + 1 then (return Nothing)
-      else do
-        writeIORef stVar $! n + 1
-        return (Just n)
-
-someThing :: D.Consume input Int
-someThing = D.Consume $ \ (Fe.Fetch _) -> return 0
-
-single :: Foldable f => f a -> Maybe a
-single = join . (foldr' f Nothing)
-  where
-    f x Nothing = Just $ Just x
-    f _ _       = Just Nothing
-
-data Resource
-  = Initial
-  | Acquired
-  | Released
-  | AcquiredImproperly
-  | ReleasedImproperly
-  deriving (Show, Eq)
-
-checkTransform :: IORef Resource -> A.Transform Int Int
-checkTransform resourceVar = A.Transform $ \ fetchIO -> Ac.Acquire $ do
-  writeIORef resourceVar Acquired
-  return $ (,) (plusFetch fetchIO) $ do
-      res <- readIORef resourceVar
-      case res of
-        Acquired -> writeIORef resourceVar Released
-        _ -> writeIORef resourceVar ReleasedImproperly
-
-plusFetch :: Fe.Fetch Int -> Fe.Fetch Int
-plusFetch (Fe.Fetch fetchIO) = Fe.Fetch $ do
-  fetch <- fetchIO
-  return $ fmap succ fetch
-
-checkProduce :: IORef Resource -> (Resource -> Bool) -> Int -> E.Produce Int
-checkProduce resourceVar f k = E.Produce . Ac.Acquire $ do
-  res <- readIORef resourceVar
-  if f res && res /= Initial
-    then do
-      return $ (,) (Fe.Fetch $ return Nothing) $ writeIORef resourceVar AcquiredImproperly
-    else do
-      writeIORef resourceVar Acquired
-      stVar <- newIORef 0
-      let fetch = do
-            n <- readIORef stVar
-            if n >= k then return (Nothing)
-            else do
-              writeIORef stVar $! n + 1
-              return (Just n)
-      return $ (,) (Fe.Fetch fetch) $ do
-          res <- readIORef resourceVar
-          case res of
-            Acquired -> writeIORef resourceVar Released
-            _ -> writeIORef resourceVar ReleasedImproperly
-
-asyncCheckResource :: TVar Resource -> E.Produce Int
-asyncCheckResource resourceVar = E.Produce . Ac.Acquire $ do
-  atomically $ writeTVar resourceVar Acquired
-  stVar <- newIORef 0
-  let fetch = do
-        n <- readIORef stVar
-        writeIORef stVar $! n + 1
-        return (Just n)
-  return (Fe.Fetch fetch, atomically $ writeTVar resourceVar Released)
-
-syncCheckResource :: IORef Resource -> E.Produce Int
-syncCheckResource resourceVar = E.Produce . Ac.Acquire $ do
-  writeIORef resourceVar Acquired
-  stVar <- newIORef 0
-  let fetch = do
-        n <- readIORef stVar
-        if n >= 1000 then return (Nothing)
-        else do
-          writeIORef stVar $! n + 1
-          return (Just n)
-  return $ (,) (Fe.Fetch fetch) $ do
-      res <- readIORef resourceVar
-      case res of
-        Acquired -> writeIORef resourceVar Released
-        _ -> writeIORef resourceVar ReleasedImproperly
diff --git a/tests/Potoki.hs b/tests/Potoki.hs
deleted file mode 100644
--- a/tests/Potoki.hs
+++ /dev/null
@@ -1,279 +0,0 @@
-module Potoki where
-
-import Prelude hiding (first, second)
-import Control.Arrow
-import Test.QuickCheck.Instances
-import Test.QuickCheck.Monadic as M
-import Test.Tasty
-import Test.Tasty.Runners
-import Test.Tasty.HUnit
-import Test.Tasty.QuickCheck
-import qualified Control.Foldl as Fl
-import qualified Potoki.Core.IO as C
-import qualified Potoki.Core.Consume as D
-import qualified Potoki.Core.Transform as A
-import qualified Potoki.Core.Produce as E
-import qualified Data.Attoparsec.ByteString.Char8 as B
-import qualified Data.ByteString as F
-import qualified Data.Vector as G
-import qualified System.Random as H
-import Data.List.Index (indexed)
-
-potoki :: TestTree
-potoki =
-  testGroup "All tests for potoki's end-users functions" $
-  [
-    testCase "vector to list" $ do
-      result <- C.produceAndConsume (E.vector (G.fromList [1,2,3])) (D.list)
-      assertEqual "" [1,2,3] result
-    ,
-    testCase "just" $ do
-      result <- C.produceAndConsume (E.list [Just 1, Nothing, Just 2]) (D.transform A.just D.list)
-      assertEqual "" [1,2] result
-    ,
-    testCase "transform,consume,take" $ do
-      let
-        transform = A.consume (D.transform (A.take 3) D.list)
-        consume = D.transform transform D.list
-        produceAndConsume list = C.produceAndConsume (E.list list) (consume)
-      assertEqual "" [[1,2,3], [4,5,6], [7,8]] =<< produceAndConsume [1,2,3,4,5,6,7,8]
-      assertEqual "" [[1,2,3], [4,5,6], [7,8,9]] =<< produceAndConsume [1,2,3,4,5,6,7,8,9]
-      assertEqual "" [] =<< produceAndConsume ([] :: [Int])
-    ,
-    testCase "File reading" $ do
-      let produce =
-            E.transform (arr (either (const Nothing) Just) >>> A.just) $
-            E.fileBytes "samples/1"
-      result <- C.produceAndConsume produce (fmap F.length D.concat)
-      assertEqual "" 17400 result
-    ,
-    transformPotoki
-    ,
-    parsingPotoki
-    ,
-    consumePotoki
-  ]
-
-
-transformPotoki :: TestTree
-transformPotoki =
-  testGroup "Transform" $
-  [
-    testCase "Order" $ do
-      let
-        list = [Left 1, Left 2, Right 'z', Left 2, Right 'a', Left 1, Right 'b', Left 0, Right 'x', Left 4, Left 3]
-        transform = left (A.consume (D.transform (A.take 2) D.sum))
-      result <- C.produceAndConsume (E.list list) (D.transform transform D.list)
-      assertEqual "" [Left 3, Right 'z', Left 2, Right 'a', Left 1, Right 'b', Left 0, Right 'x', Left 7] result
-    ,
-    testCase "Interrupted order" $ do
-      let
-        list = [Left 1, Left 2, Right 'a']
-        transform = left (A.consume (D.transform (A.take 3) D.sum))
-      result <- C.produceAndConsume (E.list list) (D.transform transform D.list)
-      assertEqual "" [Left 3, Right 'a'] result
-    ,
-    testCase "Distinct" $ do
-      let
-        list = [1,2,3,2,3,2,1,4,1] :: [Int]
-      result <- C.produceAndConsume (E.list list) (D.transform A.distinct D.list)
-      assertEqual "" [1,2,3,4] result
-    ,
-    testCase "Distinct By" $ do
-      let
-        list = [(1, ""),(2, ""),(3, ""),(2, ""),(3, ""),(2, ""),(1, ""),(4, ""),(1, "")] :: [(Int, String)]
-      result <- C.produceAndConsume (E.list list) (D.transform (A.distinctBy fst) D.list)
-      assertEqual "" [(1, ""),(2, ""),(3, ""),(4, "")] result
-    ,
-    testCase "Concurrently" $ do
-      let
-        list = [1..20000]
-        produce = E.list list
-        transform =
-          A.concurrently 12 $
-          arr (\ x -> H.randomRIO (0, 100) >>= threadDelay >> return x) >>>
-          A.executeIO
-        consume = D.transform transform D.list
-      result <- C.produceAndConsume produce consume
-      assertBool "Is dispersed" (list /= result)
-      assertEqual "Contains no duplicates" 0 (length result - length (nub result))
-      assertEqual "Equals the original once sorted" list (sort result)
-    ,
-    testProperty "Line" $ \ chunks ->
-    let
-      expected =
-        mconcat chunks
-      actual =
-        unsafePerformIO (C.produceAndConsume produce consume)
-        where
-          produce =
-            E.list chunks
-          consume =
-            rmap (mconcat . intersperse "\n") $
-            D.transform A.extractLines D.list
-      in expected === actual
-    ,
-    testProperty "takeWhile" $ \ (list :: [Int]) ->
-    let listPart = takeWhile odd list
-    in monadicIO $ do
-      let prod = E.list list
-      res <- run (C.produceAndTransformAndConsume prod (A.takeWhile odd) D.list)
-      M.assert (res == listPart)
-    ,
-    testProperty "mapFilter" $ \ (list :: [Int]) ->
-    let in2MaybeOut input =
-          if input `mod` 4 == 0
-            then Just $ input `mod` 4
-            else Nothing
-        filteredList = map fromJust . filter (/= Nothing) . map in2MaybeOut $ list
-    in monadicIO $ do
-      let prod = E.list list
-      res <- run (C.produceAndTransformAndConsume prod (A.mapFilter in2MaybeOut) D.list)
-      M.assert (res == filteredList)
-    ,
-    testProperty "filter" $ \ (list :: [Int]) ->
-    let filteredList = filter even list
-    in monadicIO $ do
-      let prod = E.list list
-      res <- run (C.produceAndTransformAndConsume prod (A.filter even) D.list)
-      M.assert (res == filteredList)
-  ]
-
-parsingPotoki :: TestTree
-parsingPotoki =
-  testGroup "Parsing" $
-  [
-    testCase "Sample 1" $ do
-      let parser = B.double <* B.char ','
-          transform = arr (either (const Nothing) Just) >>> A.just >>> A.parseBytes parser
-          produce = E.transform transform (E.fileBytes "samples/1")
-      result <- C.produceAndConsume produce D.count
-      assertEqual "" 4350 result
-    ,
-    testCase "Sample 1 greedy" $ do
-      let parser = B.sepBy B.double (B.char ',')
-          transform = arr (either (const Nothing) Just) >>> A.just >>> A.parseBytes parser
-          produce = E.transform transform (E.fileBytes "samples/1")
-      result <- C.produceAndConsume produce D.list
-      assertEqual "" [Right 4350] (fmap (fmap length) result)
-    ,
-    testCase "Split chunk" $
-    let
-      produce = E.list ["1", "2", "3"]
-      parser = B.anyChar
-      transform = A.parseBytes parser >>> arr (either (const Nothing) Just) >>> A.just
-      consume = D.transform transform D.count
-      in do
-        assertEqual "" 3 =<< C.produceAndConsume produce consume
-  ]
-
-consumePotoki =
-  testGroup "Consume" $
-  [
-    testProperty "count" $ \ (list :: [Int]) ->
-    let n = length list
-    in monadicIO $ do
-      let prod = E.list list
-      len <- run (C.produceAndConsume prod D.count)
-      M.assert (len == n)
-    ,
-    testProperty "sum" $ \ (list :: [Int]) ->
-    let n = sum list
-    in monadicIO $ do
-      let prod = E.list list
-      len <- run (C.produceAndConsume prod D.sum)
-      M.assert (len == n)
-    ,
-    testProperty "head" $ \ (list :: [Int]) ->
-    let el = if null list then Nothing else (Just (head list))
-    in monadicIO $ do
-      let prod = E.list list
-      he <- run (C.produceAndConsume prod D.head)
-      M.assert (he == el)
-    ,
-    testProperty "last" $ \ (list :: [Int]) ->
-    let el = if null list then Nothing else (Just (last list))
-    in monadicIO $ do
-      let prod = E.list list
-      he <- run (C.produceAndConsume prod D.last)
-      M.assert (he == el)
-    ,
-    testProperty "list" $ \ (list :: [Int]) ->
-    monadicIO $ do
-      let prod = E.list list
-      res <- run (C.produceAndConsume prod D.list)
-      M.assert (res == list)
-    ,
-    testProperty "reverseList" $ \ (list :: [Int]) ->
-    let revList = reverse list
-    in monadicIO $ do
-      let prod = E.list list
-      res <- run (C.produceAndConsume prod D.reverseList)
-      M.assert (res == revList)
-    ,
-    testProperty "vector" $ \ (list :: [Int]) ->
-    let vec = G.fromList list
-    in monadicIO $ do
-      let prod = E.list list
-      res <- run (C.produceAndConsume prod D.vector)
-      M.assert (res == vec)
-    ,
-    testProperty "concat" $ \ (list :: [[Int]]) ->
-    let con = concat list
-    in monadicIO $ do
-      let prod = E.list list
-      res <- run (C.produceAndConsume prod D.concat)
-      M.assert (res == con)
-    ,
-    testProperty "fold" $ \ (list :: [Int], fun :: (Fun (Int, Int) Int), first :: Int) ->
-    let f = applyFun2 fun
-        fol = foldl' f first list
-    in monadicIO $ do
-      let prod = E.list list
-      res <- run (C.produceAndConsume prod (D.fold (Fl.Fold f first id)))
-      M.assert (res == fol)
-    ,
-    testProperty "foldInIO" $ \ (list :: [Int]) ->
-    let fin = sum list
-    in monadicIO $ do
-      let prod = E.list list
-      res <- run $ do
-        sumVar <- newIORef 0
-        (C.produceAndConsume prod (D.foldInIO (Fl.FoldM (\_ a -> modifyIORef' sumVar (a+)) (pure ()) (\_ -> readIORef sumVar)) ) )
-      M.assert (res == fin)
-    ,
-    testProperty "folding" $ \ (list :: [Int], fun :: (Fun (Int, Int) Int), first :: Int) ->
-    let f = applyFun2 fun
-        fol = foldl' f first list
-    in monadicIO $ do
-      let prod = E.list list
-      res <- run (C.produceAndConsume prod (D.folding (Fl.Fold f first id) (D.sum) ))
-      a <- run (C.produceAndConsume prod D.sum)
-      M.assert (res == (fol, a))
-    ,
-    testProperty "foldingInIO" $ \ (list :: [Int]) ->
-    let fol = sum list
-    in monadicIO $ do
-      let prod = E.list list
-      res <- run $ do
-        sumVar <- newIORef 0
-        (C.produceAndConsume prod (D.foldingInIO (Fl.FoldM (\_ a -> modifyIORef' sumVar (a+)) (pure ()) (\_ -> readIORef sumVar)) (D.sum) ))
-      a <- run (C.produceAndConsume prod D.sum)
-      M.assert (res == (fol, a))
-    ,
-    testProperty "execState" $ \ (list :: [Int]) ->
-    let f = modify' . (+)
-        resL = sum list
-    in monadicIO $ do
-      let prod = E.list list
-      res <- run (C.produceAndConsume prod (D.execState f 0))
-      M.assert (res == resL)
-    ,
-    testProperty "Choice consume right'" $ \ (list :: [Either Bool Int]) ->
-    let n = sum <$> sequence list
-    in monadicIO $ do
-      let prod = E.list list
-      len <- run (C.produceAndConsume prod $ right' D.sum)
-      M.assert (len == n)
-  ]
-
diff --git a/tests/Transform.hs b/tests/Transform.hs
deleted file mode 100644
--- a/tests/Transform.hs
+++ /dev/null
@@ -1,178 +0,0 @@
-module Transform where
-
-import Prelude hiding (first, second)
-import Control.Arrow
-import Test.QuickCheck.Instances
-import Test.Tasty
-import Test.Tasty.Runners
-import Test.Tasty.HUnit
-import Test.Tasty.QuickCheck
-import qualified Potoki.Core.IO as C
-import qualified Potoki.Core.Consume as D
-import qualified Potoki.Core.Transform as A
-import qualified Potoki.Core.Produce as E
-import qualified Data.Attoparsec.ByteString.Char8 as B
-import qualified Data.ByteString as F
-import qualified Data.Vector as G
-import qualified System.Random as H
-
-transform :: TestTree
-transform =
-  testGroup "Transform" $
-  [
-    transformProduce
-    ,
-    transformChoice
-    ,
-    transformArrowLaws
-  ]
-
-transformProduce =
-  testGroup "Produce" $
-  [
-    testCase "1" $ do
-      let
-        list = [1, 2, 3] :: [Int]
-      result <- C.produceAndTransformAndConsume
-        (E.list list)
-        (A.produce (E.list . \ n -> flip replicate n n))
-        (D.list)
-      assertEqual "" [1, 2, 2, 3, 3, 3] result
-  ,
-    testCase "2" $ do
-      let
-        list = [1, 2, 3] :: [Int]
-      result <- C.produceAndTransformAndConsume
-        (E.list list)
-        (A.produce (E.list . \ n -> [(n, n)]))
-        (D.list)
-      assertEqual "" [(1, 1), (2, 2), (3, 3)] result
-  ,
-    testCase "3" $ do
-      let
-        list = [1, 2, 3] :: [Int]
-      result <- C.produceAndTransformAndConsume
-        (E.list list)
-        (A.produce (E.list . \ n -> [n, n]))
-        (D.list)
-      assertEqual "" [1, 1, 2, 2, 3, 3] result
-  ]
-
-transformChoice =
-  testGroup "Choice" $
-  [
-    testCase "1" $ do
-      let
-        list = [Left 1, Left 2, Right 'z', Left 2, Right 'a', Left 1, Right 'b', Left 0, Right 'x', Left 4, Left 3]
-        transform = left' id
-      result <- C.produceAndTransformAndConsume (E.list list) transform D.list
-      assertEqual "" [Left 1, Left 2, Right 'z', Left 2, Right 'a', Left 1, Right 'b', Left 0, Right 'x', Left 4, Left 3] result
-    ,
-    testCase "2" $ do
-      let
-        list = [Left 1, Left 2, Right 'z', Right 'a', Right 'b', Left 0, Right 'x', Left 4, Left 3]
-        transform = right' (A.consume D.list)
-      result <- C.produceAndTransformAndConsume (E.list list) transform D.list
-      assertEqual "" [Left 1, Left 2, Right "zab", Left 0, Right "x", Left 4, Left 3] result
-    ,
-    testCase "3" $ do
-      let
-        list = [Left 4, Right 'z', Right 'a', Left 3, Right 'b', Left 0, Left 1, Right 'x', Left 4, Left 3]
-        transform = left' (A.consume D.list)
-      result <- C.produceAndTransformAndConsume (E.list list) transform D.list
-      assertEqual "" [Left [4], Right 'z', Right 'a', Left [3], Right 'b', Left [0, 1], Right 'x', Left [4, 3]] result
-  ]
-
-transformArrowLaws =
-  testGroup "Arrow laws"
-  [
-    testGroup "Strong"
-    [
-      testCase "1" $ do
-        let
-          input = [(1,'a'),(2,'b'),(3,'c'),(4,'d')]
-          transform = first transform1
-        result <- C.produceAndTransformAndConsume (E.list input) transform D.list
-        assertEqual "" [(6,'c'),(4,'d')] result
-      ,
-      testCase "Lack of elements" $ do
-        let
-          input = [(1,'a'),(2,'b')]
-          transform = first transform1
-        result <- C.produceAndTransformAndConsume (E.list input) transform D.list
-        assertEqual "" [(3,'b')] result
-    ]
-    ,
-    transformProperty "arr id = id"
-      (arr id :: A.Transform Int Int)
-      id
-    ,
-    transformProperty "arr (f >>> g) = arr f >>> arr g"
-      (arr (f >>> g))
-      (arr f >>> arr g)
-    ,
-    transformProperty "first (arr f) = arr (first f)"
-      (first (arr f) :: A.Transform (Int, Char) (Int, Char))
-      (arr (first f))
-    ,
-    transformProperty "first (f >>> g) = first f >>> first g"
-      (first (transform1 >>> transform2) :: A.Transform (Int, Char) (Int, Char))
-      (first (transform1) >>> first (transform2))
-    ,
-    transformProperty "first f >>> arr fst = arr fst >>> f"
-      (first transform1 >>> arr fst :: A.Transform (Int, Char) Int)
-      (arr fst >>> transform1)
-    ,
-    transformProperty "first f >>> arr (id *** g) = arr (id *** g) >>> first f"
-      (first transform1 >>> arr (id *** g))
-      (arr (id *** g) >>> first transform1)
-    ,
-    transformProperty "first (first f) >>> arr assoc = arr assoc >>> first f"
-      (first (first transform1) >>> arr assoc :: A.Transform ((Int, Char), Double) (Int, (Char, Double)))
-      (arr assoc >>> first transform1)
-    ,
-    transformProperty "left (arr f) = arr (left f)"
-      (left (arr f) :: A.Transform (Either Int Char) (Either Int Char))
-      (arr (left f))
-    ,
-    transformProperty "left (f >>> g) = left f >>> left g"
-      (left (transform1 >>> transform2) :: A.Transform (Either Int Char) (Either Int Char))
-      (left (transform1) >>> left (transform2))
-    ,
-    transformProperty "f >>> arr Left = arr Left >>> left f"
-      (transform1 >>> arr Left :: A.Transform Int (Either Int Char))
-      (arr Left >>> left transform1)
-    ,
-    transformProperty "left f >>> arr (id +++ g) = arr (id +++ g) >>> left f"
-      (left transform1 >>> arr (id +++ g))
-      (arr (id +++ g) >>> left transform1)
-    ,
-    transformProperty "left (left f) >>> arr assocsum = arr assocsum >>> left f"
-      (left (left transform1) >>> arr assocsum :: A.Transform (Either (Either Int Char) Double) (Either Int (Either Char Double)))
-      (arr assocsum >>> left transform1)
-    ,
-    transformProperty "left (left (arr f)) >>> arr assocsum = arr assocsum >>> left (arr f)"
-      (left (left (arr f)) >>> arr assocsum :: A.Transform (Either (Either Int Char) Double) (Either Int (Either Char Double)))
-      (arr assocsum >>> left (arr f))
-  ]
-  where
-    f = (+24) :: Int -> Int
-    g = (*3) :: Int -> Int
-    transform1 = A.consume (D.transform (A.take 3) D.sum) :: A.Transform Int Int
-    transform2 = A.consume (D.transform (A.take 4) D.sum) :: A.Transform Int Int
-    assoc ((a,b),c) = (a,(b,c))
-    assocsum (Left (Left x)) = Left x
-    assocsum (Left (Right y)) = Right (Left y)
-    assocsum (Right z) = Right (Right z)
-
-transformProperty ::
-  (Arbitrary input, Show input, Eq output, Show output) =>
-  String -> A.Transform input output -> A.Transform input output -> TestTree
-transformProperty name leftTransform rightTransform =
-  testProperty name property
-  where
-    property list =
-      transform leftTransform === transform rightTransform
-      where
-        transform transform =
-          unsafePerformIO (C.produceAndTransformAndConsume (E.list list) transform D.list)
