diff --git a/CHANGELOG b/CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,7 @@
+0.0.2:
+	* Remove magic numbers from weighing code, better accuracy
+	* Add additional `io` combinator
+
 0.0.1:
 	* Support GHC 8.
 
diff --git a/src/Weigh.hs b/src/Weigh.hs
--- a/src/Weigh.hs
+++ b/src/Weigh.hs
@@ -13,9 +13,10 @@
   ,Weigh
   ,Weight(..)
   -- * Simple combinators
-  ,action
   ,func
+  ,io
   ,value
+  ,action
   -- * Validating combinators
   ,validateAction
   ,validateFunc
@@ -59,7 +60,7 @@
 -- | An action to run.
 data Action =
   forall a b. (NFData a) =>
-  Action {_actionRun :: !(b -> IO a)
+  Action {_actionRun :: !(Either (b -> IO a) (b -> a))
          ,_actionArg :: !b
          ,actionCheck :: Weight -> Maybe String}
 
@@ -95,6 +96,7 @@
                          errors
                    exitWith (ExitFailure (-1))
 
+
 --------------------------------------------------------------------------------
 -- User DSL
 
@@ -104,22 +106,24 @@
 func :: (NFData a) => String -> (b -> a) -> b -> Weigh ()
 func name !f !x = validateFunc name f x (const Nothing)
 
+-- | Weigh a function applied to an argument.
+--
+-- Implemented in terms of 'validateFunc'.
+io :: (NFData a) => String -> (b -> IO a) -> b -> Weigh ()
+io name !f !x = validateAction name f x (const Nothing)
+
 -- | Weigh a value.
 --
 -- Implemented in terms of 'action'.
 value :: NFData a => String -> a -> Weigh ()
-value name !v = action name (return v)
+value name !v = func name id v
 
 -- | Weigh an IO action.
 --
 -- Implemented in terms of 'validateAction'.
 action :: NFData a
        => String -> IO a -> Weigh ()
-action name !m =
-  validateAction name
-                 (const m)
-                 ()
-                 (const Nothing)
+action name !m = io name (const m) ()
 
 -- | Make a validator that set sthe maximum allocations.
 maxAllocs :: Int64 -> (Weight -> Maybe String)
@@ -135,14 +139,14 @@
   :: (NFData a)
   => String -> (b -> IO a) -> b -> (Weight -> Maybe String) -> Weigh ()
 validateAction name !m !arg !validate =
-  Weigh (tell [(name,Action m arg validate)])
+  Weigh (tell [(name,Action (Left m) arg validate)])
 
 -- | Weigh a function, validating the result
 validateFunc
   :: (NFData a)
   => String -> (b -> a) -> b -> (Weight -> Maybe String) -> Weigh ()
 validateFunc name !f !x !validate =
-  Weigh (tell [(name,Action (return . f) x validate)])
+  Weigh (tell [(name,Action (Right f) x validate)])
 
 --------------------------------------------------------------------------------
 -- Internal measuring actions
@@ -158,7 +162,10 @@
         Just act ->
           do case act of
                Action !run arg _ ->
-                 do (bytes,gcs) <- weighAction run arg
+                 do (bytes,gcs) <-
+                      case run of
+                        Right f -> weighFunc f arg
+                        Left m -> weighAction m arg
                     print (Weight {weightLabel = label
                                   ,weightAllocatedBytes = bytes
                                   ,weightGCs = gcs})
@@ -183,13 +190,43 @@
          let !r = read out
          in return r
 
--- | Weigh an action. This function is heavily documented inside.
+-- | Weigh a pure function. This function is heavily documented inside.
+weighFunc
+  :: (NFData a)
+  => (b -> a)         -- ^ A function whose memory use we want to measure.
+  -> b                -- ^ Argument to the function. Doesn't have to be forced.
+  -> IO (Int64,Int64) -- ^ Bytes allocated and garbage collections.
+weighFunc run !arg =
+  do performGC
+     -- The above forces getGCStats data to be generated NOW.
+     !bootupStats <- getGCStats
+     -- We need the above to subtract "program startup" overhead. This
+     -- operation itself adds n bytes for the size of GCStats, but we
+     -- subtract again that later.
+     let !_ = force (run arg)
+     performGC
+     -- The above forces getGCStats data to be generated NOW.
+     !actionStats <- getGCStats
+     let reflectionGCs = 1 -- We performed an additional GC.
+         actionBytes =
+           (bytesAllocated actionStats - bytesAllocated bootupStats) -
+           -- We subtract the size of "bootupStats", which will be
+           -- included after we did the performGC.
+           ghcStatsSizeInBytes
+         actionGCs = numGcs actionStats - numGcs bootupStats - reflectionGCs
+         -- If overheadBytes is too large, we conservatively just
+         -- return zero. It's not perfect, but this library is for
+         -- measuring large quantities anyway.
+         actualBytes = max 0 actionBytes
+     return (actualBytes,actionGCs)
+
+-- | Weigh a pure function. This function is heavily documented inside.
 weighAction
   :: (NFData a)
   => (b -> IO a)      -- ^ A function whose memory use we want to measure.
   -> b                -- ^ Argument to the function. Doesn't have to be forced.
   -> IO (Int64,Int64) -- ^ Bytes allocated and garbage collections.
-weighAction !run !arg =
+weighAction run !arg =
   do performGC
      -- The above forces getGCStats data to be generated NOW.
      !bootupStats <- getGCStats
@@ -207,13 +244,10 @@
            -- included after we did the performGC.
            ghcStatsSizeInBytes
          actionGCs = numGcs actionStats - numGcs bootupStats - reflectionGCs
-         -- I believe that 24 is the cost to allocate and force the
-         -- thunk. This may change with GHC version in the future.
-         overheadBytes = 24
          -- If overheadBytes is too large, we conservatively just
          -- return zero. It's not perfect, but this library is for
          -- measuring large quantities anyway.
-         actualBytes = max 0 (actionBytes - overheadBytes)
+         actualBytes = max 0 actionBytes
      return (actualBytes,actionGCs)
 
 --------------------------------------------------------------------------------
diff --git a/src/test/Main.hs b/src/test/Main.hs
--- a/src/test/Main.hs
+++ b/src/test/Main.hs
@@ -13,9 +13,21 @@
 main :: IO ()
 main =
   mainWith (do integers
+               ioactions
                ints
                struct
                packing)
+
+-- | Weigh IO actions.
+ioactions :: Weigh ()
+ioactions =
+  do action "integers count IO CAF 0" (return (count 0))
+     io "integers count IO func 0" (return . count) 0
+     action "integers count IO CAF 1" (return (count 1))
+     io "integers count IO func 1" (return . count) 1
+  where count :: Integer -> ()
+        count 0 = ()
+        count a = count (a - 1)
 
 -- | Just counting integers.
 integers :: Weigh ()
diff --git a/weigh.cabal b/weigh.cabal
--- a/weigh.cabal
+++ b/weigh.cabal
@@ -1,5 +1,5 @@
 name:                weigh
-version:             0.0.1
+version:             0.0.2
 synopsis:            Measure allocations of a Haskell functions/values
 description:         Please see README.md
 homepage:            https://github.com/fpco/weigh#readme
