diff --git a/hakyll.cabal b/hakyll.cabal
--- a/hakyll.cabal
+++ b/hakyll.cabal
@@ -1,5 +1,5 @@
 Name:               hakyll
-Version:            3.0.2.2
+Version:            3.1.0.0
 
 Synopsis:           A simple static site generator library.
 Description:        A simple static site generator library, mainly aimed at
@@ -81,14 +81,15 @@
                       Hakyll.Web.RelativizeUrls
                       Hakyll.Web.Page.Read
                       Hakyll.Web.Page.Metadata
-                      Hakyll.Core.ResourceProvider.FileResourceProvider
                       Hakyll.Core.Configuration
                       Hakyll.Core.Identifier.Pattern
                       Hakyll.Core.UnixFilter
                       Hakyll.Core.Util.Arrow
                       Hakyll.Core.Util.File
                       Hakyll.Core.Util.String
-                      Hakyll.Core.ResourceProvider
+                      Hakyll.Core.Resource
+                      Hakyll.Core.Resource.Provider
+                      Hakyll.Core.Resource.Provider.File
                       Hakyll.Core.CompiledItem
                       Hakyll.Core.Compiler
                       Hakyll.Core.Run
diff --git a/src-inotify/Hakyll/Web/Preview/Poll.hs b/src-inotify/Hakyll/Web/Preview/Poll.hs
--- a/src-inotify/Hakyll/Web/Preview/Poll.hs
+++ b/src-inotify/Hakyll/Web/Preview/Poll.hs
@@ -13,7 +13,7 @@
 import System.INotify
 
 import Hakyll.Core.Configuration
-import Hakyll.Core.ResourceProvider
+import Hakyll.Core.Resource
 import Hakyll.Core.Identifier
 
 -- | Calls the given callback when the directory tree changes
diff --git a/src-interval/Hakyll/Web/Preview/Poll.hs b/src-interval/Hakyll/Web/Preview/Poll.hs
--- a/src-interval/Hakyll/Web/Preview/Poll.hs
+++ b/src-interval/Hakyll/Web/Preview/Poll.hs
@@ -15,7 +15,7 @@
 
 import Hakyll.Core.Configuration
 import Hakyll.Core.Identifier
-import Hakyll.Core.ResourceProvider
+import Hakyll.Core.Resource
 
 -- | A preview thread that periodically recompiles the site.
 --
diff --git a/src/Hakyll.hs b/src/Hakyll.hs
--- a/src/Hakyll.hs
+++ b/src/Hakyll.hs
@@ -5,7 +5,8 @@
     , module Hakyll.Core.Configuration
     , module Hakyll.Core.Identifier
     , module Hakyll.Core.Identifier.Pattern
-    , module Hakyll.Core.ResourceProvider
+    , module Hakyll.Core.Resource
+    , module Hakyll.Core.Resource.Provider
     , module Hakyll.Core.Routes
     , module Hakyll.Core.Rules
     , module Hakyll.Core.UnixFilter
@@ -34,7 +35,8 @@
 import Hakyll.Core.Configuration
 import Hakyll.Core.Identifier
 import Hakyll.Core.Identifier.Pattern
-import Hakyll.Core.ResourceProvider
+import Hakyll.Core.Resource
+import Hakyll.Core.Resource.Provider
 import Hakyll.Core.Routes
 import Hakyll.Core.Rules
 import Hakyll.Core.UnixFilter
diff --git a/src/Hakyll/Core/Compiler.hs b/src/Hakyll/Core/Compiler.hs
--- a/src/Hakyll/Core/Compiler.hs
+++ b/src/Hakyll/Core/Compiler.hs
@@ -124,7 +124,8 @@
 import Hakyll.Core.Identifier.Pattern
 import Hakyll.Core.CompiledItem
 import Hakyll.Core.Writable
-import Hakyll.Core.ResourceProvider
+import Hakyll.Core.Resource
+import Hakyll.Core.Resource.Provider
 import Hakyll.Core.Compiler.Internal
 import Hakyll.Core.Store
 import Hakyll.Core.Rules.Internal
@@ -237,7 +238,7 @@
             -> Compiler b [a]
 requireAll_ pattern = fromDependencies (const getDeps) >>> fromJob requireAll_'
   where
-    getDeps = matches pattern . map unResource . resourceList
+    getDeps = filterMatches pattern . map unResource . resourceList
     requireAll_' = const $ CompilerM $ do
         deps <- getDeps . compilerResourceProvider <$> ask
         mapM (unCompilerM . getDependency) deps
diff --git a/src/Hakyll/Core/Compiler/Internal.hs b/src/Hakyll/Core/Compiler/Internal.hs
--- a/src/Hakyll/Core/Compiler/Internal.hs
+++ b/src/Hakyll/Core/Compiler/Internal.hs
@@ -26,7 +26,7 @@
 import Control.Arrow (Arrow, ArrowChoice, arr, first, left)
 
 import Hakyll.Core.Identifier
-import Hakyll.Core.ResourceProvider
+import Hakyll.Core.Resource.Provider
 import Hakyll.Core.Store
 import Hakyll.Core.Routes
 import Hakyll.Core.Logger
diff --git a/src/Hakyll/Core/Identifier/Pattern.hs b/src/Hakyll/Core/Identifier/Pattern.hs
--- a/src/Hakyll/Core/Identifier/Pattern.hs
+++ b/src/Hakyll/Core/Identifier/Pattern.hs
@@ -1,5 +1,13 @@
 -- | Module providing pattern matching and capturing on 'Identifier's.
+-- 'Pattern's come in two kinds:
 --
+-- * Simple glob patterns, like @foo\/*@;
+--
+-- * Custom, arbitrary predicates of the type @Identifier -> Bool@.
+--
+-- They both have advantages and disadvantages. By default, globs are used,
+-- unless you construct your 'Pattern' using the 'predicate' function.
+--
 -- A very simple pattern could be, for example, @foo\/bar@. This pattern will
 -- only match the exact @foo\/bar@ identifier.
 --
@@ -20,15 +28,17 @@
 --
 -- * @foo\/*.html@ will match all HTML files in the @foo\/@ directory.
 --
--- The 'match' function allows the user to get access to the elements captured
+-- The 'capture' function allows the user to get access to the elements captured
 -- by the capture elements in the pattern.
 --
 module Hakyll.Core.Identifier.Pattern
     ( Pattern
-    , parsePattern
-    , match
-    , doesMatch
+    , parseGlob
+    , predicate
+    , regex
     , matches
+    , filterMatches
+    , capture
     , fromCapture
     , fromCaptureString
     , fromCaptures
@@ -37,32 +47,39 @@
 import Data.List (isPrefixOf, inits, tails)
 import Control.Arrow ((&&&), (>>>))
 import Control.Monad (msum)
-import Data.Maybe (isJust)
-import Data.Monoid (mempty, mappend)
+import Data.Maybe (isJust, fromMaybe)
+import Data.Monoid (Monoid, mempty, mappend)
 
 import GHC.Exts (IsString, fromString)
+import Text.Regex.PCRE ((=~~))
 
 import Hakyll.Core.Identifier
 
 -- | One base element of a pattern
 --
-data PatternComponent = Capture
-                      | CaptureMany
-                      | Literal String
-                      deriving (Eq, Show)
+data GlobComponent = Capture
+                   | CaptureMany
+                   | Literal String
+                   deriving (Eq, Show)
 
 -- | Type that allows matching on identifiers
 --
-newtype Pattern = Pattern {unPattern :: [PatternComponent]}
-                deriving (Eq, Show)
+data Pattern = Glob [GlobComponent]
+             | Predicate (Identifier -> Bool)
 
 instance IsString Pattern where
-    fromString = parsePattern
+    fromString = parseGlob
 
+instance Monoid Pattern where
+    mempty = Predicate (const True)
+    g@(Glob _)  `mappend` x           = Predicate (matches g) `mappend` x
+    x           `mappend` g@(Glob _)  = x `mappend` Predicate (matches g)
+    Predicate f `mappend` Predicate g = Predicate $ \i -> f i && g i
+
 -- | Parse a pattern from a string
 --
-parsePattern :: String -> Pattern
-parsePattern = Pattern . parse' -- undefined -- Pattern . map toPattern . unIdentifier . parseIdentifier
+parseGlob :: String -> Pattern
+parseGlob = Glob . parse'
   where
     parse' str =
         let (chunk, rest) = break (`elem` "\\*") str
@@ -72,20 +89,34 @@
             ('*'  : xs)       -> Literal chunk : Capture : parse' xs
             xs                -> Literal chunk : Literal xs : []
 
--- | Match an identifier against a pattern, generating a list of captures
+-- | Create a 'Pattern' from an arbitrary predicate
 --
-match :: Pattern -> Identifier -> Maybe [Identifier]
-match p (Identifier i) = fmap (map Identifier) $ match' (unPattern p) i
+-- Example:
+--
+-- > predicate (\i -> matches "foo/*" i && not (matches "foo/bar" i))
+--
+predicate :: (Identifier -> Bool) -> Pattern
+predicate = Predicate
 
+-- | Create a 'Pattern' from a regex
+--
+-- Example:
+--
+-- > regex "^foo/[^x]*$
+--
+regex :: String -> Pattern
+regex str = predicate $ fromMaybe False . (=~~ str) . toFilePath
+
 -- | Check if an identifier matches a pattern
 --
-doesMatch :: Pattern -> Identifier -> Bool
-doesMatch p = isJust . match p
+matches :: Pattern -> Identifier -> Bool
+matches (Glob p)      = isJust . capture (Glob p)
+matches (Predicate p) = (p $)
 
 -- | Given a list of identifiers, retain only those who match the given pattern
 --
-matches :: Pattern -> [Identifier] -> [Identifier]
-matches p = filter (doesMatch p)
+filterMatches :: Pattern -> [Identifier] -> [Identifier]
+filterMatches = filter . matches
 
 -- | Split a list at every possible point, generate a list of (init, tail)
 -- cases. The result is sorted with inits decreasing in length.
@@ -93,30 +124,35 @@
 splits :: [a] -> [([a], [a])]
 splits = inits &&& tails >>> uncurry zip >>> reverse
 
--- | Internal verion of 'match'
+-- | Match a glob against a pattern, generating a list of captures
 --
-match' :: [PatternComponent] -> String -> Maybe [String]
-match' [] [] = Just []  -- An empty match
-match' [] _  = Nothing  -- No match
--- match' _  [] = Nothing   -- No match
-match' (Literal l : ms) str
+capture :: Pattern -> Identifier -> Maybe [Identifier]
+capture (Glob p) (Identifier i) = fmap (map Identifier) $ capture' p i
+capture (Predicate _) _         = Nothing
+
+-- | Internal verion of 'capture'
+--
+capture' :: [GlobComponent] -> String -> Maybe [String]
+capture' [] [] = Just []  -- An empty match
+capture' [] _  = Nothing  -- No match
+capture' (Literal l : ms) str
     -- Match the literal against the string
-    | l `isPrefixOf` str = match' ms $ drop (length l) str
+    | l `isPrefixOf` str = capture' ms $ drop (length l) str
     | otherwise          = Nothing
-match' (Capture : ms) str =
+capture' (Capture : ms) str =
     -- Match until the next /
     let (chunk, rest) = break (== '/') str
-    in msum $ [ fmap (i :) (match' ms (t ++ rest)) | (i, t) <- splits chunk ]
-match' (CaptureMany : ms) str =
+    in msum $ [ fmap (i :) (capture' ms (t ++ rest)) | (i, t) <- splits chunk ]
+capture' (CaptureMany : ms) str =
     -- Match everything
-    msum $ [ fmap (i :) (match' ms t) | (i, t) <- splits str ]
+    msum $ [ fmap (i :) (capture' ms t) | (i, t) <- splits str ]
     
 -- | Create an identifier from a pattern by filling in the captures with a given
 -- string
 --
 -- Example:
 --
--- > fromCapture (parsePattern "tags/*") (parseIdentifier "foo")
+-- > fromCapture (parseGlob "tags/*") (parseIdentifier "foo")
 --
 -- Result:
 --
@@ -128,7 +164,7 @@
 -- | Simplified version of 'fromCapture' which takes a 'String' instead of an
 -- 'Identifier'
 --
--- > fromCaptureString (parsePattern "tags/*") "foo"
+-- > fromCaptureString (parseGlob "tags/*") "foo"
 --
 -- Result:
 --
@@ -141,11 +177,19 @@
 -- given list of strings
 --
 fromCaptures :: Pattern -> [Identifier] -> Identifier
-fromCaptures (Pattern []) _ = mempty
-fromCaptures (Pattern (m : ms)) [] = case m of
-    Literal l -> Identifier l `mappend` fromCaptures (Pattern ms) []
-    _         -> error $  "Hakyll.Core.Identifier.Pattern.fromCaptures: "
+fromCaptures (Glob p)      = fromCaptures' p
+fromCaptures (Predicate _) = error $
+    "Hakyll.Core.Identifier.Pattern.fromCaptures: fromCaptures called on a " ++
+    "predicate instead of a glob"
+
+-- | Internally used version of 'fromCaptures'
+--
+fromCaptures' :: [GlobComponent] -> [Identifier] -> Identifier
+fromCaptures' []        _ = mempty
+fromCaptures' (m : ms) [] = case m of
+    Literal l -> Identifier l `mappend` fromCaptures' ms []
+    _         -> error $  "Hakyll.Core.Identifier.Pattern.fromCaptures': "
                        ++ "identifier list exhausted"
-fromCaptures (Pattern (m : ms)) ids@(i : is) = case m of
-    Literal l -> Identifier l `mappend` fromCaptures (Pattern ms) ids
-    _         -> i `mappend` fromCaptures (Pattern ms) is
+fromCaptures' (m : ms) ids@(i : is) = case m of
+    Literal l -> Identifier l `mappend` fromCaptures' ms ids
+    _         -> i `mappend` fromCaptures' ms is
diff --git a/src/Hakyll/Core/Resource.hs b/src/Hakyll/Core/Resource.hs
new file mode 100644
--- /dev/null
+++ b/src/Hakyll/Core/Resource.hs
@@ -0,0 +1,14 @@
+-- | Module exporting the simple 'Resource' type
+--
+module Hakyll.Core.Resource
+    ( Resource (..)
+    ) where
+
+import Hakyll.Core.Identifier
+
+-- | A resource
+--
+-- Invariant: the resource specified by the given identifier must exist
+--
+newtype Resource = Resource {unResource :: Identifier}
+                 deriving (Eq, Show, Ord)
diff --git a/src/Hakyll/Core/Resource/Provider.hs b/src/Hakyll/Core/Resource/Provider.hs
new file mode 100644
--- /dev/null
+++ b/src/Hakyll/Core/Resource/Provider.hs
@@ -0,0 +1,91 @@
+-- | This module provides an API for resource providers. Resource providers
+-- allow Hakyll to get content from resources; the type of resource depends on
+-- the concrete instance.
+--
+-- A resource is represented by the 'Resource' type. This is basically just a
+-- newtype wrapper around 'Identifier' -- but it has an important effect: it
+-- guarantees that a resource with this identifier can be provided by one or
+-- more resource providers.
+--
+-- Therefore, it is not recommended to read files directly -- you should use the
+-- provided 'Resource' methods.
+--
+module Hakyll.Core.Resource.Provider
+    ( ResourceProvider (..)
+    , resourceExists
+    , resourceDigest
+    , resourceModified
+    ) where
+
+import Control.Concurrent (MVar, readMVar, modifyMVar_)
+import Control.Monad ((<=<))
+import Data.Word (Word8)
+import Data.Map (Map)
+import qualified Data.Map as M
+
+import qualified Data.ByteString.Lazy as LB
+import OpenSSL.Digest.ByteString.Lazy (digest)
+import OpenSSL.Digest (MessageDigest (MD5))
+
+import Hakyll.Core.Identifier
+import Hakyll.Core.Store
+import Hakyll.Core.Resource
+
+-- | A value responsible for retrieving and listing resources
+--
+data ResourceProvider = ResourceProvider
+    { -- | A list of all resources this provider is able to provide
+      resourceList           :: [Resource]
+    , -- | Retrieve a certain resource as string
+      resourceString         :: Resource -> IO String
+    , -- | Retrieve a certain resource as lazy bytestring
+      resourceLazyByteString :: Resource -> IO LB.ByteString
+    , -- | Cache keeping track of modified items
+      resourceModifiedCache  :: MVar (Map Resource Bool)
+    }
+
+-- | Check if a given identifier has a resource
+--
+resourceExists :: ResourceProvider -> Identifier -> Bool
+resourceExists provider = flip elem $ map unResource $ resourceList provider
+
+-- | Retrieve a digest for a given resource
+--
+resourceDigest :: ResourceProvider -> Resource -> IO [Word8]
+resourceDigest provider = digest MD5 <=< resourceLazyByteString provider
+
+-- | Check if a resource was modified
+--
+resourceModified :: ResourceProvider -> Resource -> Store -> IO Bool
+resourceModified provider resource store = do
+    cache <- readMVar mvar
+    case M.lookup resource cache of
+        -- Already in the cache
+        Just m  -> return m
+        -- Not yet in the cache, check digests (if it exists)
+        Nothing -> do
+            m <- if resourceExists provider (unResource resource)
+                        then digestModified provider resource store
+                        else return False
+            modifyMVar_ mvar (return . M.insert resource m)
+            return m
+  where
+    mvar = resourceModifiedCache provider
+
+-- | Check if a resource digest was modified
+--
+digestModified :: ResourceProvider -> Resource -> Store -> IO Bool
+digestModified provider resource store = do
+    -- Get the latest seen digest from the store
+    lastDigest <- storeGet store itemName $ unResource resource
+    -- Calculate the digest for the resource
+    newDigest <- resourceDigest provider resource
+    -- Check digests
+    if Just newDigest == lastDigest
+        -- All is fine, not modified
+        then return False
+        -- Resource modified; store new digest
+        else do storeSet store itemName (unResource resource) newDigest
+                return True
+  where
+    itemName = "Hakyll.Core.ResourceProvider.digestModified"
diff --git a/src/Hakyll/Core/Resource/Provider/File.hs b/src/Hakyll/Core/Resource/Provider/File.hs
new file mode 100644
--- /dev/null
+++ b/src/Hakyll/Core/Resource/Provider/File.hs
@@ -0,0 +1,36 @@
+-- | A concrete 'ResourceProvider' that gets it's resources from the filesystem
+--
+module Hakyll.Core.Resource.Provider.File
+    ( fileResourceProvider
+    ) where
+
+import Control.Applicative ((<$>))
+import Control.Concurrent (newMVar)
+import qualified Data.Map as M
+
+import qualified Data.ByteString.Lazy as LB
+
+import Hakyll.Core.Resource
+import Hakyll.Core.Resource.Provider
+import Hakyll.Core.Identifier
+import Hakyll.Core.Util.File
+import Hakyll.Core.Configuration
+
+-- | Create a filesystem-based 'ResourceProvider'
+--
+fileResourceProvider :: HakyllConfiguration -> IO ResourceProvider
+fileResourceProvider configuration = do
+    -- Retrieve a list of identifiers
+    list <- map parseIdentifier . filter (not . ignoreFile configuration) <$>
+        getRecursiveContents False "."
+
+    -- MVar for the cache
+    mvar <- newMVar M.empty
+
+    -- Construct a resource provider
+    return ResourceProvider
+        { resourceList           = map Resource list
+        , resourceString         = readFile . toFilePath . unResource
+        , resourceLazyByteString = LB.readFile . toFilePath . unResource
+        , resourceModifiedCache  = mvar
+        }
diff --git a/src/Hakyll/Core/ResourceProvider.hs b/src/Hakyll/Core/ResourceProvider.hs
deleted file mode 100644
--- a/src/Hakyll/Core/ResourceProvider.hs
+++ /dev/null
@@ -1,75 +0,0 @@
--- | This module provides an API for resource providers. Resource providers
--- allow Hakyll to get content from resources; the type of resource depends on
--- the concrete instance.
---
--- A resource is represented by the 'Resource' type. This is basically just a
--- newtype wrapper around 'Identifier' -- but it has an important effect: it
--- guarantees that a resource with this identifier can be provided by one or
--- more resource providers.
---
--- Therefore, it is not recommended to read files directly -- you should use the
--- provided 'Resource' methods.
---
-module Hakyll.Core.ResourceProvider
-    ( Resource (..)
-    , ResourceProvider (..)
-    , resourceExists
-    , resourceDigest
-    , resourceModified
-    ) where
-
-import Control.Monad ((<=<))
-import Data.Word (Word8)
-
-import qualified Data.ByteString.Lazy as LB
-import OpenSSL.Digest.ByteString.Lazy (digest)
-import OpenSSL.Digest (MessageDigest (MD5))
-
-import Hakyll.Core.Identifier
-import Hakyll.Core.Store
-
--- | A resource
---
--- Invariant: the resource specified by the given identifier must exist
---
-newtype Resource = Resource {unResource :: Identifier}
-                 deriving (Eq, Show, Ord)
-
--- | A value responsible for retrieving and listing resources
---
-data ResourceProvider = ResourceProvider
-    { -- | A list of all resources this provider is able to provide
-      resourceList           :: [Resource]
-    , -- | Retrieve a certain resource as string
-      resourceString         :: Resource -> IO String
-    , -- | Retrieve a certain resource as lazy bytestring
-      resourceLazyByteString :: Resource -> IO LB.ByteString
-    }
-
--- | Check if a given identifier has a resource
---
-resourceExists :: ResourceProvider -> Identifier -> Bool
-resourceExists provider = flip elem $ map unResource $ resourceList provider
-
--- | Retrieve a digest for a given resource
---
-resourceDigest :: ResourceProvider -> Resource -> IO [Word8]
-resourceDigest provider = digest MD5 <=< resourceLazyByteString provider
-
--- | Check if a resource was modified
---
-resourceModified :: ResourceProvider -> Resource -> Store -> IO Bool
-resourceModified provider resource store = do
-    -- Get the latest seen digest from the store
-    lastDigest <- storeGet store itemName $ unResource resource
-    -- Calculate the digest for the resource
-    newDigest <- resourceDigest provider resource
-    -- Check digests
-    if Just newDigest == lastDigest
-        -- All is fine, not modified
-        then return False
-        -- Resource modified; store new digest
-        else do storeSet store itemName (unResource resource) newDigest
-                return True
-  where
-    itemName = "Hakyll.Core.ResourceProvider.resourceModified"
diff --git a/src/Hakyll/Core/ResourceProvider/FileResourceProvider.hs b/src/Hakyll/Core/ResourceProvider/FileResourceProvider.hs
deleted file mode 100644
--- a/src/Hakyll/Core/ResourceProvider/FileResourceProvider.hs
+++ /dev/null
@@ -1,29 +0,0 @@
--- | A concrete 'ResourceProvider' that gets it's resources from the filesystem
---
-module Hakyll.Core.ResourceProvider.FileResourceProvider
-    ( fileResourceProvider
-    ) where
-
-import Control.Applicative ((<$>))
-
-import qualified Data.ByteString.Lazy as LB
-
-import Hakyll.Core.ResourceProvider
-import Hakyll.Core.Identifier
-import Hakyll.Core.Util.File
-import Hakyll.Core.Configuration
-
--- | Create a filesystem-based 'ResourceProvider'
---
-fileResourceProvider :: HakyllConfiguration -> IO ResourceProvider
-fileResourceProvider configuration = do
-    -- Retrieve a list of identifiers
-    list <- map parseIdentifier . filter (not . ignoreFile configuration) <$>
-        getRecursiveContents False "."
-
-    -- Construct a resource provider
-    return ResourceProvider
-        { resourceList           = map Resource list
-        , resourceString         = readFile . toFilePath . unResource
-        , resourceLazyByteString = LB.readFile . toFilePath . unResource
-        }
diff --git a/src/Hakyll/Core/Routes.hs b/src/Hakyll/Core/Routes.hs
--- a/src/Hakyll/Core/Routes.hs
+++ b/src/Hakyll/Core/Routes.hs
@@ -30,7 +30,7 @@
     , runRoutes
     , idRoute
     , setExtension
-    , ifMatch
+    , matchRoute
     , customRoute
     , gsubRoute
     , composeRoutes
@@ -85,15 +85,15 @@
 setExtension extension = Routes $ fmap (`replaceExtension` extension)
                                 . unRoutes idRoute
 
--- | Modify a route: apply the route if the identifier matches the given
--- pattern, fail otherwise.
+-- | Apply the route if the identifier matches the given pattern, fail
+-- otherwise
 --
-ifMatch :: Pattern -> Routes -> Routes
-ifMatch pattern (Routes route) = Routes $ \id' ->
-    if doesMatch pattern id' then route id'
-                             else Nothing
+matchRoute :: Pattern -> Routes -> Routes
+matchRoute pattern (Routes route) = Routes $ \id' ->
+    if matches pattern id' then route id' else Nothing
 
--- | Create a custom route. This should almost always be used with 'ifMatch'.
+-- | Create a custom route. This should almost always be used with
+-- 'matchRoute'
 --
 customRoute :: (Identifier -> FilePath) -> Routes
 customRoute f = Routes $ Just . f
diff --git a/src/Hakyll/Core/Rules.hs b/src/Hakyll/Core/Rules.hs
--- a/src/Hakyll/Core/Rules.hs
+++ b/src/Hakyll/Core/Rules.hs
@@ -7,13 +7,18 @@
 -- A typical usage example would be:
 --
 -- > main = hakyll $ do
--- >     route   "posts/*" (setExtension "html")
--- >     compile "posts/*" someCompiler
+-- >     match "posts/*" $ do
+-- >         route   (setExtension "html")
+-- >         compile someCompiler
+-- >     match "css/*" $ do
+-- >         route   idRoute
+-- >         compile compressCssCompiler
 --
 {-# LANGUAGE GeneralizedNewtypeDeriving, OverloadedStrings #-}
 module Hakyll.Core.Rules
     ( RulesM
     , Rules
+    , match
     , compile
     , create
     , route
@@ -23,16 +28,17 @@
 
 import Control.Applicative ((<$>))
 import Control.Monad.Writer (tell)
-import Control.Monad.Reader (ask)
+import Control.Monad.Reader (ask, local)
 import Control.Arrow (second, (>>>), arr, (>>^))
 import Control.Monad.State (get, put)
-import Data.Monoid (mempty)
+import Data.Monoid (mempty, mappend)
 import qualified Data.Set as S
 
 import Data.Typeable (Typeable)
 import Data.Binary (Binary)
 
-import Hakyll.Core.ResourceProvider
+import Hakyll.Core.Resource
+import Hakyll.Core.Resource.Provider
 import Hakyll.Core.Identifier
 import Hakyll.Core.Identifier.Pattern
 import Hakyll.Core.Compiler.Internal
@@ -63,21 +69,32 @@
               -> Rules
 tellResources resources = RulesM $ tell $ RuleSet mempty mempty $ S.fromList resources
 
+-- | Only compile/route items satisfying the given predicate
+--
+match :: Pattern -> Rules -> Rules
+match pattern = RulesM . local addPredicate . unRulesM
+  where
+    addPredicate env = env
+        { rulesPattern = rulesPattern env `mappend` pattern
+        }
+
 -- | Add a compilation rule to the rules.
 --
--- This instructs all resources matching the given pattern to be compiled using
--- the given compiler. When no resources match the given pattern, nothing will
--- happen. In this case, you might want to have a look at 'create'.
+-- This instructs all resources to be compiled using the given compiler. When
+-- no resources match the current selection, nothing will happen. In this case,
+-- you might want to have a look at 'create'.
 --
 compile :: (Binary a, Typeable a, Writable a)
-        => Pattern -> Compiler Resource a -> Rules
-compile pattern compiler = RulesM $ do
-    identifiers <- matches pattern . map unResource . resourceList <$> ask
+        => Compiler Resource a -> Rules
+compile compiler = RulesM $ do
+    pattern <- rulesPattern <$> ask
+    provider <- rulesResourceProvider <$> ask
+    let ids = filterMatches pattern $ map unResource $ resourceList provider
     unRulesM $ do
-        tellCompilers $ flip map identifiers $ \identifier ->
+        tellCompilers $ flip map ids $ \identifier ->
             (identifier, constA (Resource identifier) >>> compiler)
-        tellResources $ map Resource identifiers
-
+        tellResources $ map Resource ids
+                   
 -- | Add a compilation rule
 --
 -- This sets a compiler for the given identifier. No resource is needed, since
@@ -91,10 +108,12 @@
 
 -- | Add a route.
 --
--- This adds a route for all items matching the given pattern.
+-- This adds a route for all items matching the current pattern.
 --
-route :: Pattern -> Routes -> Rules
-route pattern route' = tellRoute $ ifMatch pattern route'
+route :: Routes -> Rules
+route route' = RulesM $ do
+    pattern <- rulesPattern <$> ask
+    unRulesM $ tellRoute $ matchRoute pattern route'
 
 -- | Apart from regular compilers, one is also able to specify metacompilers.
 -- Metacompilers are a special class of compilers: they are compilers which
diff --git a/src/Hakyll/Core/Rules/Internal.hs b/src/Hakyll/Core/Rules/Internal.hs
--- a/src/Hakyll/Core/Rules/Internal.hs
+++ b/src/Hakyll/Core/Rules/Internal.hs
@@ -5,6 +5,7 @@
     ( CompileRule (..)
     , RuleSet (..)
     , RuleState (..)
+    , RuleEnvironment (..)
     , RulesM (..)
     , Rules
     , runRules
@@ -17,8 +18,10 @@
 import Data.Monoid (Monoid, mempty, mappend)
 import Data.Set (Set)
 
-import Hakyll.Core.ResourceProvider
+import Hakyll.Core.Resource
+import Hakyll.Core.Resource.Provider
 import Hakyll.Core.Identifier
+import Hakyll.Core.Identifier.Pattern
 import Hakyll.Core.Compiler.Internal
 import Hakyll.Core.Routes
 import Hakyll.Core.CompiledItem
@@ -55,10 +58,17 @@
     { rulesMetaCompilerIndex :: Int
     } deriving (Show)
 
+-- | Rule environment
+--
+data RuleEnvironment = RuleEnvironment
+    { rulesResourceProvider :: ResourceProvider
+    , rulesPattern          :: Pattern
+    }
+
 -- | The monad used to compose rules
 --
 newtype RulesM a = RulesM
-    { unRulesM :: ReaderT ResourceProvider (WriterT RuleSet (State RuleState)) a
+    { unRulesM :: ReaderT RuleEnvironment (WriterT RuleSet (State RuleState)) a
     } deriving (Monad, Functor, Applicative)
 
 -- | Simplification of the RulesM type; usually, it will not return any
@@ -70,6 +80,9 @@
 --
 runRules :: Rules -> ResourceProvider -> RuleSet
 runRules rules provider =
-    evalState (execWriterT $ runReaderT (unRulesM rules) provider) state
+    evalState (execWriterT $ runReaderT (unRulesM rules) env) state
   where
     state = RuleState {rulesMetaCompilerIndex = 0}
+    env = RuleEnvironment { rulesResourceProvider = provider
+                          , rulesPattern          = mempty
+                          }
diff --git a/src/Hakyll/Core/Run.hs b/src/Hakyll/Core/Run.hs
--- a/src/Hakyll/Core/Run.hs
+++ b/src/Hakyll/Core/Run.hs
@@ -23,8 +23,9 @@
 import Hakyll.Core.Util.File
 import Hakyll.Core.Compiler
 import Hakyll.Core.Compiler.Internal
-import Hakyll.Core.ResourceProvider
-import Hakyll.Core.ResourceProvider.FileResourceProvider
+import Hakyll.Core.Resource
+import Hakyll.Core.Resource.Provider
+import Hakyll.Core.Resource.Provider.File
 import Hakyll.Core.Rules.Internal
 import Hakyll.Core.DirectedGraph
 import Hakyll.Core.DirectedGraph.DependencySolver
@@ -99,9 +100,7 @@
          -> [Identifier]         -- ^ Identifiers to check
          -> IO (Set Identifier)  -- ^ Modified resources
 modified provider store ids = fmap S.fromList $ flip filterM ids $ \id' ->
-    if resourceExists provider id'
-        then resourceModified provider (Resource id') store
-        else return False
+    resourceModified provider (Resource id') store
 
 -- | Add a number of compilers and continue using these compilers
 --
diff --git a/src/Hakyll/Core/Writable/CopyFile.hs b/src/Hakyll/Core/Writable/CopyFile.hs
--- a/src/Hakyll/Core/Writable/CopyFile.hs
+++ b/src/Hakyll/Core/Writable/CopyFile.hs
@@ -12,7 +12,7 @@
 import Data.Typeable (Typeable)
 import Data.Binary (Binary)
 
-import Hakyll.Core.ResourceProvider
+import Hakyll.Core.Resource
 import Hakyll.Core.Writable
 import Hakyll.Core.Compiler
 import Hakyll.Core.Identifier
diff --git a/src/Hakyll/Web/CompressCss.hs b/src/Hakyll/Web/CompressCss.hs
--- a/src/Hakyll/Web/CompressCss.hs
+++ b/src/Hakyll/Web/CompressCss.hs
@@ -11,7 +11,7 @@
 import Control.Arrow ((>>^))
 
 import Hakyll.Core.Compiler
-import Hakyll.Core.ResourceProvider
+import Hakyll.Core.Resource
 import Hakyll.Core.Util.String
 
 -- | Compiler form of 'compressCss'
diff --git a/src/Hakyll/Web/Page.hs b/src/Hakyll/Web/Page.hs
--- a/src/Hakyll/Web/Page.hs
+++ b/src/Hakyll/Web/Page.hs
@@ -67,7 +67,7 @@
 
 import Hakyll.Core.Identifier
 import Hakyll.Core.Compiler
-import Hakyll.Core.ResourceProvider
+import Hakyll.Core.Resource
 import Hakyll.Web.Page.Internal
 import Hakyll.Web.Page.Read
 import Hakyll.Web.Page.Metadata
diff --git a/src/Hakyll/Web/Tags.hs b/src/Hakyll/Web/Tags.hs
--- a/src/Hakyll/Web/Tags.hs
+++ b/src/Hakyll/Web/Tags.hs
@@ -40,7 +40,6 @@
 import Prelude hiding (id)
 import Control.Category (id)
 import Control.Applicative ((<$>))
-import Data.Map (Map)
 import qualified Data.Map as M
 import Data.List (intersperse, intercalate)
 import Control.Arrow (arr, (&&&), (>>>), (***), (<<^), returnA)
@@ -65,7 +64,7 @@
 -- | Data about tags
 --
 data Tags a = Tags
-    { tagsMap :: Map String [Page a]
+    { tagsMap :: [(String, [Page a])]
     } deriving (Show, Typeable)
 
 instance Binary a => Binary (Tags a) where
@@ -91,7 +90,8 @@
              -> [Page a]              -- ^ Pages
              -> Tags a                -- ^ Resulting tags
 readTagsWith f pages = Tags
-    { tagsMap = foldl (M.unionWith (++)) M.empty (map readTagsWith' pages)
+    { tagsMap = M.toList $
+        foldl (M.unionWith (++)) M.empty (map readTagsWith' pages)
     }
   where
     -- Create a tag map for one page
@@ -122,7 +122,7 @@
 renderTags makeUrl makeItem concatItems = proc (Tags tags) -> do
     -- In tags' we create a list: [((tag, route), count)]
     tags' <- mapCompiler ((id &&& (getRouteFor <<^ makeUrl)) *** arr length)
-                -< M.toList tags
+                -< tags
 
     let -- Absolute frequencies of the pages
         freqs = map snd tags'
diff --git a/src/Hakyll/Web/Template.hs b/src/Hakyll/Web/Template.hs
--- a/src/Hakyll/Web/Template.hs
+++ b/src/Hakyll/Web/Template.hs
@@ -59,7 +59,7 @@
 
 import Hakyll.Core.Compiler
 import Hakyll.Core.Identifier
-import Hakyll.Core.ResourceProvider
+import Hakyll.Core.Resource
 import Hakyll.Web.Template.Internal
 import Hakyll.Web.Template.Read
 import Hakyll.Web.Page.Internal
