diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,10 +1,49 @@
-# Changelog
-
-## 0.1.3.0
-
-* Added `check` function
-* Improved testing facilities
-
-## 0.1.2.0
-
+# Changelog
+
+# 0.2
+
+Highlights:
+
+* Switching from list-based parsing to `TokenStream`
+
+## 0.2.1
+
+### 0.2.1.0
+
+* Deprecated `skipN`, replaced with `sDrop` 
+
+## 0.2.0
+
+### 0.2.0.0
+
+* Added `TokenStream`, an abstraction of lists
+* Used `TokenStream` to reformulate Atomics and Transformers
+
+
+# 0.1
+
+Highlights:
+
+* Initial Version
+
+## 0.1.4
+
+### 0.1.4.0
+
+* Added `digit` and `letter` parsers
+
+## 0.1.3
+
+### 0.1.3.1
+
+* Improved testing facilities
+
+### 0.1.3.0
+
+* Added `check` function
+
+## 0.1.2
+
+### 0.1.2.0
+
 * Added `peek` function
diff --git a/LParse.cabal b/LParse.cabal
--- a/LParse.cabal
+++ b/LParse.cabal
@@ -1,5 +1,5 @@
 name:                LParse
-version:             0.1.3.1
+version:             0.2.1.0
 synopsis:            A continuation-based parser library
 description:         A parser library using continuations with a possibility for failure to build parsers in a clear and concise manner.
 homepage:            https://github.com/MarcusVoelker/LParse#readme
@@ -15,7 +15,7 @@
 
 library
   hs-source-dirs:      src
-  exposed-modules:     Control.DoubleContinuations Text.LParse.Atomics, Text.LParse.Parser, Text.LParse.Transformers
+  exposed-modules:     Control.DoubleContinuations Text.LParse.Atomics, Text.LParse.Parser, Text.LParse.Transformers, Text.LParse.TokenStream
   build-depends:       base >= 4.7 && < 5
   default-language:    Haskell2010
 
diff --git a/src/Text/LParse/Atomics.hs b/src/Text/LParse/Atomics.hs
--- a/src/Text/LParse/Atomics.hs
+++ b/src/Text/LParse/Atomics.hs
@@ -1,63 +1,71 @@
-{-|
-Module      : Text.LParse.Atomics
-Description : Atomic parsers for use with LParse
-Copyright   : (c) Marcus Völker, 2017
-License     : MIT
-Maintainer  : marcus.voelker@rwth-aachen.de
-
-This module implements LParse's atomic parsers, i.e., parsers that are not built up from other parsers.
--}
-module Text.LParse.Atomics where 
-
-import Control.DoubleContinuations
-
-import Control.Applicative
-import Control.Monad
-import Data.Char
-import Text.LParse.Parser
-import Text.LParse.Transformers
-
--- | A parser that always succeeds, parses nothing and returns unit
-noop :: Parser r t ()
-noop = return ()
-
--- | A parser that consumes the whole input and returns it unchanged
-full :: Parser r [t] [t]
-full = many tokenReturn
-
--- | A parser that consumes the whole input and discards it, successfully
-discard :: Parser r [t] ()
-discard = void full
-
--- | A parser that parses nothing, but only succeeds if the input is empty
-eoi :: Parser r [t] ()
-eoi = cParse null noop ("Input not fully consumed")
-
--- | Extracts the first token from the input and applies the given function to it
-tokenParse :: (t -> a) -> Parser r [t] a
-tokenParse f = Parser (\s -> DCont (\btr etr -> if null s then etr "Unexpected EOI" else btr (f $ head s,tail s)))
-
--- | Consumes and returns the first token of the input
-tokenReturn :: Parser r [a] a
-tokenReturn = tokenParse id
-
--- | Succeeds exactly if the input begins with the given sequence. On success, consumes that sequence
-consume :: (Eq t, Show t) => [t] -> Parser r [t] ()
-consume pre = cParse ((&&) <$> (all id . zipWith (==) pre) <*> ((>= length pre) . length)) (pParse (drop (length pre)) noop) ("Expected " ++ show pre)
-
--- | Succeeds exactly if the input begins with the given token. On success, consumes that token
-consumeSingle :: (Eq t, Show t) => t -> Parser r [t] ()
-consumeSingle t = cParse (\s -> not (null s) && head s == t) (pParse tail noop) ("Expected " ++ show t)
-
--- | Extracts the first word (i.e. contiguous string of letters) from the input and returns it
-word :: Parser r String String
-word = some (cParse (\s -> not (null s) && isLetter (head s)) tokenReturn "Expected letter")
-
--- | Extracts the first integer (i.e. contiguous string of digits) from the input and returns it
-integer :: Parser r String Integer
-integer = read <$> some (cParse (\s -> not (null s) && isDigit (head s)) tokenReturn "Expected digit")
-
-
--- | Succeeds if the first token matches the given function, without consuming it
-peek :: (t -> Bool) -> String -> Parser r [t] ()
-peek c = cParse (c . head) noop
+{-|
+Module      : Text.LParse.Atomics
+Description : Atomic parsers for use with LParse
+Copyright   : (c) Marcus Völker, 2017
+License     : MIT
+Maintainer  : marcus.voelker@rwth-aachen.de
+
+This module implements LParse's atomic parsers, i.e., parsers that are not built up from other parsers.
+-}
+module Text.LParse.Atomics where 
+
+import Control.DoubleContinuations
+
+import Control.Applicative
+import Control.Monad
+import Data.Char
+import Text.LParse.Parser
+import Text.LParse.Transformers
+import Text.LParse.TokenStream
+
+-- | A parser that always succeeds, parses nothing and returns unit
+noop :: Parser r t ()
+noop = return ()
+
+-- | A parser that consumes the whole input and returns it unchanged
+full :: Parser r [t] [t]
+full = many tokenReturn
+
+-- | A parser that consumes the whole input and discards it, successfully
+discard :: Parser r [t] ()
+discard = void full
+
+-- | A parser that parses nothing, but only succeeds if the input is empty
+eoi :: Parser r [t] ()
+eoi = cParse null noop ("Input not fully consumed")
+
+-- | Extracts the first token from the input and applies the given function to it
+tokenParse :: (TokenStream s) => (t -> a) -> Parser r (s t) a
+tokenParse f = Parser (\s -> DCont (\btr etr -> if null s then etr "Unexpected EOI" else btr (f $ top s,rest s)))
+
+-- | Consumes and returns the first token of the input
+tokenReturn :: (TokenStream s) => Parser r (s a) a
+tokenReturn = tokenParse id
+
+-- | Succeeds exactly if the input begins with the given sequence. On success, consumes that sequence
+consume :: (Eq t, Show (s t), TokenStream s) => (s t) -> Parser r (s t) ()
+consume pre = cParse ((&&) <$> (all id . sZipWith (==) pre) <*> ((>= length pre) . length)) (pParse (sDrop (length pre)) noop) ("Expected " ++ show pre)
+
+-- | Succeeds exactly if the input begins with the given token. On success, consumes that token
+consumeSingle :: (Eq t, Show t, TokenStream s) => t -> Parser r (s t) ()
+consumeSingle t = cParse (\s -> not (null s) && top s == t) (pParse rest noop) ("Expected " ++ show t)
+
+-- | Extracts the first digit and returns it
+digit :: Parser r String Integer
+digit = read . return <$> cParse (\s -> not (null s) && isDigit (head s)) tokenReturn "Expected digit"
+
+-- | Extracts the first digit and returns it
+letter :: Parser r String Char
+letter = cParse (\s -> not (null s) && isLetter (head s)) tokenReturn "Expected letter"
+
+-- | Extracts the first word (i.e. contiguous string of letters) from the input and returns it
+word :: Parser r String String
+word = some letter 
+
+-- | Extracts the first integer (i.e. contiguous string of digits) from the input and returns it
+integer :: Parser r String Integer
+integer = foldl (\x y -> x*10+y) 0 <$> some digit
+
+-- | Succeeds if the first token matches the given function, without consuming it
+peek :: (TokenStream s) => (t -> Bool) -> String -> Parser r (s t) ()
+peek c = cParse (c . top) noop
diff --git a/src/Text/LParse/TokenStream.hs b/src/Text/LParse/TokenStream.hs
new file mode 100644
--- /dev/null
+++ b/src/Text/LParse/TokenStream.hs
@@ -0,0 +1,62 @@
+{-|
+Module      : Text.LParse.TokenStream
+Description : Underlying data structure for sequential parsing
+Copyright   : (c) Marcus Völker, 2017
+License     : MIT
+Maintainer  : marcus.voelker@rwth-aachen.de
+
+This module contains the `TokenStream` class, an abstraction of lists, similar to `Traversable`, but geared for use with LParse
+-}
+module Text.LParse.TokenStream where 
+
+import Data.Maybe
+import Data.Traversable
+
+{-# DEPRECATED skipN "Use sDrop in place of skipN" #-}
+
+-- | `TokenStream` abstracts a list, i.e., something that has a next element to process and a rest afterwards
+class (Functor t, Foldable t) => TokenStream t where
+    -- | `top` gives the next element to process. Similar to `head`
+    top :: t a -> a
+    -- | `rest` gives what is left after processing `top`. Similar to `tail`
+    rest :: t a -> t a
+    -- | `nil` gives the empty `TokenStream`. Similar to `[]`
+    nil :: t a
+    -- | `cons` prepends an element to the `TokenStream`. Similar to `(:)`
+    cons :: a -> t a -> t a
+
+instance TokenStream [] where
+    top = head
+    rest = tail
+    nil = []
+    cons = (:)
+
+instance TokenStream Maybe where
+    top = fromJust
+    rest = const Nothing
+    nil = Nothing
+    cons a _ = Just a
+
+-- | Deprecated version of `sDrop`
+skipN :: (TokenStream s) => Int -> s a -> s a
+skipN = sDrop 
+
+-- | `TokenStream` version of `drop` 
+sDrop ::  (TokenStream s) => Int -> s a -> s a
+sDrop 0 x = x
+sDrop n x = rest $ sDrop (n-1) x
+
+-- | `TokenStream` version of `zip`
+sZip :: (TokenStream s) => s a -> s b -> s (a,b)
+sZip = sZipWith (,)
+
+-- | `TokenStream` version of `zipWith`
+sZipWith :: (TokenStream s) => (a -> b -> c) -> s a -> s b -> s c
+sZipWith f l r | null l || null r = nil
+               | otherwise      = f (top l) (top r) `cons` (sZipWith f (rest l) (rest r))
+
+-- | `TokenStream` version of `filter`
+sFilter :: (TokenStream s) => (a -> Bool) -> s a -> s a
+sFilter c x | null x = nil
+            | c (top x) = (top x) `cons` sFilter c (rest x)
+            | otherwise = sFilter c (rest x)
diff --git a/src/Text/LParse/Transformers.hs b/src/Text/LParse/Transformers.hs
--- a/src/Text/LParse/Transformers.hs
+++ b/src/Text/LParse/Transformers.hs
@@ -1,53 +1,54 @@
-{-|
-Module      : Text.LParse.Transformers
-Description : Parser transformers for use with LParse
-Copyright   : (c) Marcus Völker, 2017
-License     : MIT
-Maintainer  : marcus.voelker@rwth-aachen.de
-
-This module implements LParse's transformers, i.e. functions that build new parsers from other parsers
--}
-module Text.LParse.Transformers where 
-
-import Control.DoubleContinuations
-import Text.LParse.Parser
-
-import Control.Applicative
-import Data.Char
-
--- | Executes components in the same order as @(>>)@, but returning the first rather than the second monad. Note that @a >> b /= b << a@
-(<<) :: (Monad m) => m a -> m b -> m a
-a << b = a >>= ((b >>) . return)
-
--- | Takes a condition the parser's input has to fulfil in order for the parser to succeed
-cParse :: (t -> Bool) -> Parser r t a -> String -> Parser r t a
-cParse c p err = Parser (\s -> if c s then pFunc p s else throw err)
-
--- | Transforms the input before applying the parser
-pParse :: (t -> t) -> Parser r t a -> Parser r t a
-pParse f p = Parser (pFunc p . f)
-
--- | Takes a parser that consumes separators and a parser that consumes the desired data and returns a non-empty list of desired data (separated by the separator in source)
--- For example: @sepSome (consume " ") word@ applied to @"a banana is tasty"@ returns @["a","banana","is","tasty"]@
-sepSome :: Parser r t () -> Parser r t a -> Parser r t [a]
-sepSome sep p = ((:) <$> p <*> many (sep >> p)) <|> fmap return p
-
--- | Same as @sepSome@, but allows empty lists
-sepMany :: Parser r t () -> Parser r t a -> Parser r t [a]
-sepMany sep p = sepSome sep p <|> return []
-
--- | Removes all tokens from the given list from the input
-skip :: (Eq t) => [t] -> Parser r [t] a -> Parser r [t] a
-skip s = skipBy (not . (`elem` s))
-
--- | Same as skip, but with a custom comparator
-skipBy :: (t -> Bool) -> Parser r [t] a -> Parser r [t] a
-skipBy f = pParse (filter f)
-
--- | Skips standard whitespace characters from a String input
-skipWhitespace :: Parser r String a -> Parser r String a
-skipWhitespace = skipBy (not . isSpace)
-
--- | Replaces the first token by applying the given function
-replace :: (t -> t) -> Parser r [t] a -> Parser r [t] a
-replace f p = Parser (pFunc p . (\(x:xs) -> f x:xs))
+{-|
+Module      : Text.LParse.Transformers
+Description : Parser transformers for use with LParse
+Copyright   : (c) Marcus Völker, 2017
+License     : MIT
+Maintainer  : marcus.voelker@rwth-aachen.de
+
+This module implements LParse's transformers, i.e. functions that build new parsers from other parsers
+-}
+module Text.LParse.Transformers where 
+
+import Control.DoubleContinuations
+import Text.LParse.Parser
+import Text.LParse.TokenStream
+
+import Control.Applicative
+import Data.Char
+
+-- | Executes components in the same order as @(>>)@, but returning the first rather than the second monad. Note that @a >> b /= b << a@
+(<<) :: (Monad m) => m a -> m b -> m a
+a << b = a >>= ((b >>) . return)
+
+-- | Takes a condition the parser's input has to fulfil in order for the parser to succeed
+cParse :: (t -> Bool) -> Parser r t a -> String -> Parser r t a
+cParse c p err = Parser (\s -> if c s then pFunc p s else throw err)
+
+-- | Transforms the input before applying the parser
+pParse :: (t -> t) -> Parser r t a -> Parser r t a
+pParse f p = Parser (pFunc p . f)
+
+-- | Takes a parser that consumes separators and a parser that consumes the desired data and returns a non-empty list of desired data (separated by the separator in source)
+-- For example: @sepSome (consume " ") word@ applied to @"a banana is tasty"@ returns @["a","banana","is","tasty"]@
+sepSome :: Parser r t () -> Parser r t a -> Parser r t [a]
+sepSome sep p = ((:) <$> p <*> many (sep >> p)) <|> fmap return p
+
+-- | Same as @sepSome@, but allows empty lists
+sepMany :: Parser r t () -> Parser r t a -> Parser r t [a]
+sepMany sep p = sepSome sep p <|> return []
+
+-- | Removes all tokens from the given list from the input
+skip :: (Eq t, TokenStream s) => [t] -> Parser r (s t) a -> Parser r (s t) a
+skip s = skipBy (not . (`elem` s))
+
+-- | Same as skip, but with a custom comparator
+skipBy :: (TokenStream s) => (t -> Bool) -> Parser r (s t) a -> Parser r (s t) a
+skipBy f = pParse (sFilter f)
+
+-- | Skips standard whitespace characters from a String input
+skipWhitespace :: Parser r String a -> Parser r String a
+skipWhitespace = skipBy (not . isSpace)
+
+-- | Replaces the first token by applying the given function
+replace :: (TokenStream s) => (t -> t) -> Parser r (s t) a -> Parser r (s t) a
+replace f p = Parser (pFunc p . (\x -> f (top x) `cons` rest x))
diff --git a/test/Test.hs b/test/Test.hs
--- a/test/Test.hs
+++ b/test/Test.hs
@@ -20,6 +20,8 @@
     (consume "prefix","prefixed"),
     (consume "", "foo"),
     (consume "", ""),
+    (letter >> eoi, "b"),
+    (digit >> eoi, "4"),
     (word >> eoi, "banana")
     ]
 
@@ -28,6 +30,8 @@
     (eoi,"foo"),
     (consume "prefix", "freepix"),
     (consume "prefix", ""),
+    (letter >> eoi, "banana"),
+    (digit >> eoi, "42"),
     (word >> eoi, "banana bread")
     ]
 
@@ -39,6 +43,7 @@
 intCases :: [(Parser r String Integer, String, Integer)]
 intCases = [
     (integer,"123 is a nice number",123),
+    (digit,"123 is a nice number",1),
     (sum <$> sepMany (consume " ") integer,"1 4 12 61 192",1+4+12+61+192)
     ]
 
