diff --git a/Deflate.hs b/Deflate.hs
new file mode 100644
--- /dev/null
+++ b/Deflate.hs
@@ -0,0 +1,19 @@
+import Codec.Compression.Zlib(decompress)
+import Data.ByteString.Lazy(readFile, writeFile)
+import Data.List(isSuffixOf)
+import Prelude hiding (readFile, writeFile)
+import System.Environment
+
+main :: IO ()
+main =
+  do args <- getArgs
+     case args of
+       [ifile] ->
+         if ".z" `isSuffixOf` ifile
+           then do bstr <- readFile ifile
+                   case decompress bstr of
+                     Nothing -> putStrLn "Decompression failure."
+                     Just o  -> writeFile (take (length ifile - 2) ifile) o
+           else putStrLn "Unexpected file name."
+       _ ->
+         putStrLn "USAGE: deflate [filename]"
diff --git a/pure-zlib.cabal b/pure-zlib.cabal
--- a/pure-zlib.cabal
+++ b/pure-zlib.cabal
@@ -1,5 +1,5 @@
 name:                pure-zlib
-version:             0.3
+version:             0.4
 synopsis:            A Haskell-only implementation of zlib / DEFLATE
 homepage:            http://github.com/GaloisInc/pure-zlib
 license:             BSD3
@@ -30,7 +30,20 @@
                       Codec.Compression.Zlib.HuffmanTree,
                       Codec.Compression.Zlib.Monad,
                       Codec.Compression.Zlib.OutputWindow
+  default-extensions:
+                      BangPatterns,
+                      MultiParamTypeClasses,
+                      MultiWayIf
 
+executable deflate
+  default-language:   Haskell2010
+  main-is:            Deflate.hs
+  ghc-options:        -Wall
+  build-depends:
+                      base                       >= 4.7   && < 5.0,
+                      bytestring                 >= 0.10  && < 0.11,
+                      pure-zlib                  >= 0.3   && < 0.5
+
 test-suite test-zlib
   type:               exitcode-stdio-1.0
   main-is:            Test.hs
@@ -47,3 +60,8 @@
                       test-framework             >= 0.8   && < 0.10,
                       test-framework-hunit       >= 0.3   && < 0.5,
                       test-framework-quickcheck2 >= 0.3   && < 0.5
+
+source-repository head
+  type: git
+  location: git://github.com/GaloisInc/pure-zlib.git
+
diff --git a/src/Codec/Compression/Zlib/Monad.hs b/src/Codec/Compression/Zlib/Monad.hs
--- a/src/Codec/Compression/Zlib/Monad.hs
+++ b/src/Codec/Compression/Zlib/Monad.hs
@@ -34,11 +34,11 @@
 import MonadLib.Monads
 
 data DecompressState = DecompressState {
-       dcsNextBitNo     :: Int
-     , dcsCurByte       :: Word8
-     , dcsAdler32       :: AdlerState
-     , dcsInput         :: ByteString
-     , dcsOutput        :: OutputWindow
+       dcsNextBitNo     :: !Int
+     , dcsCurByte       :: !Word8
+     , dcsAdler32       :: !AdlerState
+     , dcsInput         :: !ByteString
+     , dcsOutput        :: !OutputWindow
      }
 
 type DeflateM = State DecompressState
diff --git a/src/Codec/Compression/Zlib/OutputWindow.hs b/src/Codec/Compression/Zlib/OutputWindow.hs
--- a/src/Codec/Compression/Zlib/OutputWindow.hs
+++ b/src/Codec/Compression/Zlib/OutputWindow.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE BangPatterns #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
 {-# OPTIONS_GHC -fno-warn-orphans #-}
 module Codec.Compression.Zlib.OutputWindow(
@@ -21,8 +22,8 @@
 import Data.Word
 
 data OutputWindow = OutputWindow {
-       owCommitted :: FingerTree Int SBS.ByteString
-     , owRecent    :: Builder
+       owCommitted :: !(FingerTree Int SBS.ByteString)
+     , owRecent    :: !Builder
      }
 
 instance Monoid Int where
@@ -36,13 +37,13 @@
 emptyWindow = OutputWindow empty mempty
 
 addByte :: OutputWindow -> Word8 -> OutputWindow
-addByte ow b = ow{ owRecent = owRecent ow <> word8 b }
+addByte !ow !b = ow{ owRecent = owRecent ow <> word8 b }
 
 addChunk :: OutputWindow -> ByteString -> OutputWindow
-addChunk ow bs = ow{ owRecent = owRecent ow <> lazyByteString bs }
+addChunk !ow !bs = ow{ owRecent = owRecent ow <> lazyByteString bs }
 
 addOldChunk :: OutputWindow -> Int -> Int64 -> (OutputWindow, ByteString)
-addOldChunk ow dist len = (OutputWindow output (lazyByteString chunk), chunk)
+addOldChunk !ow !dist !len = (OutputWindow output (lazyByteString chunk), chunk)
  where
   output      = owCommitted ow |> BS.toStrict (toLazyByteString (owRecent ow))
   dropAmt     = measure output - dist
