diff --git a/io-memoize.cabal b/io-memoize.cabal
--- a/io-memoize.cabal
+++ b/io-memoize.cabal
@@ -1,18 +1,16 @@
 name:                io-memoize
-version:             1.0.0.0
+version:             1.1.0.0
 synopsis:            Memoize IO actions
 description:
   Transform an IO action into a similar IO action
   that performs the original action only once.
   .
   You can choose to perform the original action
-  in one of three ways:
+  in one of two ways:
   .
   (1) lazily (might never be performed)
   .
-  (2) eagerly
-  .
-  (3) concurrently (eager)
+  (2) eagerly (concurrent)
   .
   Special thanks to shachaf and headprogrammingczar from #haskell irc
   for helping me reason about the behavior of this library.
@@ -22,7 +20,7 @@
 license-file:        LICENSE
 author:              Dan Burton
 maintainer:          danburton.email@gmail.com
-copyright:           (c) 2012 Dan Burton
+copyright:           (c) 2014 Dan Burton
 
 category:            System
 build-type:          Simple
@@ -30,8 +28,8 @@
 
 library
   hs-source-dirs:    src
-  exposed-modules:   System.IO.Memoize
-  build-depends:     base == 4.*, spawn
+  exposed-modules:   System.IO.Memoize, Control.Concurrent.Cache
+  build-depends:     base == 4.*, async >= 2.0
   ghc-options:       -Wall
 
 source-repository head
diff --git a/src/Control/Concurrent/Cache.hs b/src/Control/Concurrent/Cache.hs
new file mode 100644
--- /dev/null
+++ b/src/Control/Concurrent/Cache.hs
@@ -0,0 +1,29 @@
+module Control.Concurrent.Cache (Cache, newCache, fetch) where
+
+import Control.Concurrent.MVar
+import Data.IORef
+
+-- | A thread-safe write-once cache.
+newtype Cache a = Cache {
+  -- | Fetch the value stored in the cache,
+  -- or call the supplied fallback and store the result,
+  -- if the cache is empty.
+  fetch :: IO a -> IO a
+}
+
+-- | Create an empty cache.
+newCache :: IO (Cache a)
+newCache = do
+  b <- newMVar True
+  r <- newIORef undefined
+  return (cache b r)
+
+cache :: MVar Bool -> IORef a -> Cache a
+cache b r = Cache $ \action -> do
+  modifyMVar_ b $ \isEmpty ->
+    if isEmpty
+      then do v <- action
+              writeIORef r v
+              return False
+      else return False
+  readIORef r
diff --git a/src/System/IO/Memoize.hs b/src/System/IO/Memoize.hs
--- a/src/System/IO/Memoize.hs
+++ b/src/System/IO/Memoize.hs
@@ -1,59 +1,60 @@
--- | Memoize IO actions,
--- performing them at most once,
+-- | Memoize IO actions, performing them at most once,
 -- but recalling their result for subsequent invocations.
--- This library provides three sequencing strategies:
--- lazy ('ioMemo'), eager ('ioMemo''), and concurrent ('ioMemoPar').
--- 
--- The lazy and eager approaches give stronger sequencing guarantees.
+-- This library provides two sequencing strategies:
+-- lazy ('once'), and concurrent ('eagerlyOnce').
 -- 
--- The following property holds: @join . ioMemo === id@.
--- The same is true for @ioMemo'@ and @ioMemoPar@.
+-- The following property holds: @join . once === id@.
+-- The same is true for @eagerlyOnce@.
 -- 
--- Also, for the three memoizers in this library,
--- the memory allocated for the result will not
+-- The memory allocated for a memoized result will obviously not
 -- be available for garbage collection until the corresponding
--- memoized action is also available for garbage collection,
--- unless your compiler performs deep magicks.
+-- memoized action is also available for garbage collection.
 module System.IO.Memoize (
-    ioMemo
+    once
+  , eagerlyOnce
+  , ioMemo
   , ioMemo'
   , ioMemoPar
   ) where
 
-import Control.Concurrent.Spawn
-import Control.Concurrent.MVar
-import Data.IORef
+import Control.Concurrent.Async (async, wait)
+import Control.Concurrent.Cache (newCache, fetch)
 
--- | Memoize an IO action.
--- The action will be performed
+-- | Memoize an IO action. The action will be performed
 -- the first time that it its value is demanded;
--- all subsequent invocations
--- will simply recall the value acquired
--- from the first call.
--- If the value is never demanded,
+-- all subsequent invocations will simply recall the value acquired
+-- from the first call. If the value is never demanded,
 -- then the action will never be performed.
 -- 
--- This is basically a safe version of
--- 'System.IO.Unsafe.unsafeInterleaveIO'.
--- This function is also thread-safe:
--- it is guaranteed that the action passed in
--- will be performed exactly 0 or 1 times
--- by this code. Exceptions will be propagated
--- to the caller.
+-- This is basically a safe version of 'System.IO.Unsafe.unsafeInterleaveIO'.
+-- Exceptions will be propagated to the caller, and the action will be retried
+-- at each invocation, only until it has successfully completed once.
 -- 
 -- Example usage:
 -- 
--- >>> getLine' <- ioMemo getLine
+-- >>> getLine' <- once getLine
 -- 
 -- >>> replicateM 3 getLine'
 -- Hello
 -- ["Hello", "Hello", "Hello"]
-ioMemo :: IO a -> IO (IO a)
-ioMemo action = do
-  memo <- newIOMemoizer
-  return (memo action)
+once :: IO a -> IO (IO a)
+once action = do
+  cache <- newCache
+  return (fetch cache action)
 
 -- | Memoize an IO action.
+-- The action will be started immediately in a new thread.
+-- Attempts to access the result will block until the action is finished.
+-- If the action produces an error, then attempts to access its value
+-- will re-raise the same error each time.
+eagerlyOnce :: IO a -> IO (IO a)
+eagerlyOnce action = do
+  thread <- async action
+  return (wait thread)
+
+
+{-# DEPRECATED ioMemo' "Please just call the action directly." #-}
+-- | Memoize an IO action.
 -- The action will be performed immediately;
 -- all subsequent invocations
 -- will recall the value acquired.
@@ -62,30 +63,10 @@
   v <- action
   return (return v)
 
--- | Memoize an IO action.
--- The action will be performed immediately
--- in a spawned thread.
--- Attempts to access the result
--- will block until the action is finished.
--- 
--- This is simply a synonym for 'Control.Concurrent.Spawn.spawn'
-ioMemoPar :: IO a -> IO (IO a)
-ioMemoPar = spawn
-
-
-
-newIOMemoizer :: IO (IO a -> IO a)
-newIOMemoizer = do
-  b <- newMVar True
-  r <- newIORef undefined
-  return (ioMemoizer b r)
+{-# DEPRECATED ioMemo "Please use 'once'." #-}
+ioMemo :: IO a -> IO (IO a)
+ioMemo = once
 
-ioMemoizer :: MVar Bool -> IORef a -> IO a -> IO a
-ioMemoizer b r action = do
-  modifyMVar_ b $ \isEmpty ->
-    if isEmpty
-      then do v <- action
-              writeIORef r v
-              return False
-      else return False
-  readIORef r
+{-# DEPRECATED ioMemoPar "Please use 'eagerlyOnce'." #-}
+ioMemoPar :: IO a -> IO (IO a)
+ioMemoPar = eagerlyOnce
