diff --git a/machines-binary.cabal b/machines-binary.cabal
--- a/machines-binary.cabal
+++ b/machines-binary.cabal
@@ -1,5 +1,5 @@
 name:                machines-binary
-version:             0.3.0.3
+version:             0.4.0.2
 synopsis:            Binary utilities for the machines library
 homepage:            http://github.com/aloiscochard/machines-binary
 license:             Apache-2.0
@@ -14,10 +14,10 @@
   default-language:  Haskell2010
   hs-source-dirs:    src
   ghc-options:       -Wall
-  exposed-modules: 
+  exposed-modules:
     Data.Binary.Machine
-  build-depends:       
+  build-depends:
     base >=4.6 && < 5
     , binary              >= 0.7      && < 0.9
     , bytestring          >= 0.10     && < 0.11
-    , machines            >= 0.2      && < 0.7
+    , machines            >= 0.2      && < 0.8
diff --git a/src/Data/Binary/Machine.hs b/src/Data/Binary/Machine.hs
--- a/src/Data/Binary/Machine.hs
+++ b/src/Data/Binary/Machine.hs
@@ -1,28 +1,99 @@
-module Data.Binary.Machine where
+{-# LANGUAGE Rank2Types #-}
 
+module Data.Binary.Machine (
+  -- * Get
+    processGet
+  , processGetL
+  , streamGet
+  , streamGetL
+  -- * Put
+  , processPut
+  -- * Types
+  , DecodingError(..)
+  ) where
+
 import Data.ByteString (ByteString)
-import Data.Binary.Get (Decoder(..), Get, pushChunk, runGetIncremental)
+import Data.Binary.Get (Decoder(..), Get, ByteOffset, pushChunk, runGetIncremental)
 import Data.Binary.Put (Put, runPut)
-import Data.Machine (MachineT(..), ProcessT, Step(Await, Yield), Is(Refl), auto, stopped)
+import Data.Machine (Plan, ProcessT, Process, auto, repeatedly, yield, echo)
+import Data.Machine.Stack (Stack(..), stack, push, pop)
 
 import qualified Data.ByteString.Lazy as Lazy
 
-streamGet :: Monad m => Get a -> ProcessT m ByteString (Either String a)
-streamGet getA = processDecoder' (runGetIncremental getA) (streamGet getA)
+-- |
+-- Construct a Plan that run a 'Get' until it fails or it return a parsed result.
+-- This plan automatically manages the pushback of unused input.
+--
+-- You can use this function to construct a machine and run a 'Get' on the
+-- provided input.
+-- With 'stack' you can convert the created machine to a normal machine
+--
+-- @
+-- -- construct the machine
+-- myMachine :: 'Machine' ('Stack' ByteString) (Either DecodingError Word8)
+-- myMachine = 'construct' $ 'processGet' 'getWord8'
+--
+-- -- run the machine
+-- run $ 'stack' ('source' ["abc", "d", "efgh"]) myMachine
+-- @
+--
+-- You can combine machines created in this way with the facilities provided by
+-- the machines package.
+--
+-- @
+-- --run m2 after m1
+-- myMachine = m1 <> m2
+--   where
+--     m1 = construct $ processGet (getByteString 5)
+--     m2 = construct $ processGet (getByteString 1)
+--
+-- run $ stack (source ["abc", "d", "efgh"]) myMachine
+-- > [Right "abcde",Right "f"]
+-- @
+processGet :: Get a -> Plan (Stack ByteString) (Either DecodingError a) ()
+processGet getA = _getPlan getA >>= pure . fmap snd >>= yield
 
-processGet :: Monad m => Get a -> ProcessT m ByteString (Either String a)
-processGet getA = processDecoder (runGetIncremental getA)
+-- | Same as 'processGet' with additional information about the number
+-- of bytes consumed by the 'Get'
+processGetL :: Get a -> Plan (Stack ByteString) (Either DecodingError (ByteOffset, a)) ()
+processGetL getA = _getPlan getA >>= yield
 
-processDecoder :: Monad m => Decoder a -> ProcessT m ByteString (Either String a)
-processDecoder decA = processDecoder' decA stopped
+-- | Run a 'Get' multiple times and stream its results
+--
+-- @
+-- run $ source ["abc", "d", "efgh"] ~> streamGet (getByteString 2)
+-- > [Right "ab",Right "cd",Right "ef",Right "gh"]
+-- @
+streamGet :: Get a -> Process ByteString (Either DecodingError a)
+streamGet getA = stack echo (repeatedly $ processGet getA)
 
-processDecoder' :: Monad m => Decoder a -> ProcessT m ByteString (Either String a) -> ProcessT m ByteString (Either String a)
-processDecoder' decA r = MachineT . return $ Await f Refl stopped where
-  f xs = case pushChunk decA xs of
-    Fail _ _ e    -> yield' $ Left e
-    Done _ _ a    -> yield' $ Right a
-    decA'         -> processDecoder' decA' r
-  yield' ea = MachineT . return $ Yield ea r
+-- | Same as 'streamGet' with additional information about the number
+-- of bytes consumed by the 'Get'
+streamGetL :: Get a -> Process ByteString (Either DecodingError (ByteOffset, a))
+streamGetL getA = stack echo (repeatedly $ processGetL getA)
 
+-- | Encode evrery input object with a 'Put'
 processPut :: Monad m => (a -> Put) -> ProcessT m a ByteString
 processPut f = auto $ Lazy.toStrict . runPut . f
+
+-- | A 'Get' decoding error.
+data DecodingError = DecodingError
+  { deConsumed :: {-# UNPACK #-} !ByteOffset
+    -- ^ Number of bytes consumed before the error
+  , deMessage  :: !String
+    -- ^ Error message
+  } deriving (Show, Read, Eq)
+
+--------------------------------------------------------------------------
+-- Internals
+_decoderPlan :: Decoder a -> Plan (Stack ByteString) o (Either DecodingError (ByteOffset, a))
+_decoderPlan decA = do
+  xs <- pop
+  case pushChunk decA xs of
+    Fail leftovers consumed e -> push leftovers >> pure (Left (DecodingError consumed e))
+    Done leftovers consumed a -> push leftovers >> pure (Right (consumed, a))
+    decA'                     -> _decoderPlan decA'
+
+_getPlan :: Get a -> Plan (Stack ByteString) o (Either DecodingError (ByteOffset, a))
+_getPlan getA = _decoderPlan $ runGetIncremental getA
+--------------------------------------------------------------------------
