+> type Codec
+> = forall m r . Monad m => Lens' (Producer ByteString m r) (Producer Text m (Producer ByteString m r))
+
+ and call the decoding lenses @utf8@, @utf16BE@ \"codecs\", since they can
+ re-encode what they have decoded. Thus you use any particular codec with
+ the @view@ / @(^.)@ , @zoom@ and @over@ functions from the standard lens libraries;
+ <http://hackage.haskell.org/package/lens lens>,
+ <http://hackage.haskell.org/package/lens-family lens-family>,
+ <http://hackage.haskell.org/package/lens-simple lens-simple>, or one of the
+ and <http://hackage.haskell.org/package/microlens microlens> packages will all work
+ the same, since we already have access to the types they require.
+
+ Each decoding lens looks into a byte stream that is supposed to contain text.
+ The particular lenses are named in accordance with the expected
+ encoding, 'utf8', 'utf16LE' etc. To turn a such a lens or @Codec@
+ into an ordinary function, use @view@ / @(^.)@ -- here also called 'decode':
+
+> view utf8 :: Producer ByteString m r -> Producer Text m (Producer ByteString m r)
+> decode utf8 Byte.stdin :: Producer Text IO (Producer ByteString IO r)
+> Bytes.stdin ^. utf8 :: Producer Text IO (Producer ByteString IO r)
+
+ Of course, we could always do this with the specialized decoding functions, e.g.
+
+> decodeUtf8 :: Producer ByteString m r -> Producer Text m (Producer ByteString m r)
+> decodeUtf8 Byte.stdin :: Producer Text IO (Producer ByteString IO r)
+
+ As with these functions, the stream of text that a @Codec@ \'sees\'
+ in the stream of bytes begins at its head.
+ At any point of decoding failure, the stream of text ends and reverts to (returns)
+ the original byte stream. Thus if the first bytes are already
+ un-decodable, the whole ByteString producer will be returned, i.e.
+
+> view utf8 bad_bytestream
+
+ will just come to the same as
+
+> return bad_bytestream
+
+ Where there is no decoding failure, the return value of the text stream will be
+ an empty byte stream followed by its own return value. In all cases you must
+ deal with the fact that it is a /ByteString producer/ that is returned, even if
+ it can be thrown away with @Control.Monad.void@
+
+> void (Bytes.stdin ^. utf8) :: Producer Text IO ()
+
+ The @eof@ lens permits you to pattern match: if there is a Right value,
+ it is the leftover bytestring producer, if there is a Right value, it
+ is the return value of the original bytestring producer:
+
+> Bytes.stdin ^. utf8 . eof :: Producer Text IO (Either (Producer ByteString IO IO) ())
+
+ Thus for the stream of un-decodable bytes mentioned above,
+
+> view (utf8 . eof) bad_bytestream
+
+ will be the same as
+
+> return (Left bad_bytestream)
+
+ @zoom utf8@ converts a Text parser into a ByteString parser:
+
+> zoom utf8 drawChar :: Monad m => StateT (Producer ByteString m r) m (Maybe Char)
+
+ or, using the type synonymn from @Pipes.Parse@:
+
+> zoom utf8 drawChar :: Monad m => Parser ByteString m (Maybe Char)
+
+ Thus we can define a ByteString parser (in the pipes-parse sense) like this:
+
+> charPlusByte :: Parser ByteString m (Maybe Char, Maybe Word8)))
+> charPlusByte = do char_ <- zoom utf8 Text.drawChar
+> byte_ <- Bytes.peekByte
+> return (char_, byte_)
+
+ Though @charPlusByte@ is partly defined with a Text parser 'drawChar';
+ but it is a ByteString parser; it will return the first valid utf8-encoded
+ Char in a ByteString, /whatever its byte-length/,
+ and the first byte following, if both exist. Because
+ we \'draw\' one and \'peek\' at the other, the parser as a whole only
+ advances one Char's length along the bytestring, whatever that length may be.
+ See the slightly more complex example \'decode.hs\' in the
+ <http://www.haskellforall.com/2014/02/pipes-parse-30-lens-based-parsing.html#batteries-included haskellforall blog>
+ discussion of this type of byte stream parsing.