diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
 # Revision history for haskell-candid
 
+## 0.3 -- 2021-10-01
+
+* Candid pretty-printing: Try to invert field names using word list
+
 ## 0.2 -- 2021-06-17
 
 * Guess field named when only the hash is known
diff --git a/candid.cabal b/candid.cabal
--- a/candid.cabal
+++ b/candid.cabal
@@ -1,11 +1,11 @@
 cabal-version:      >=1.10
 name:               candid
-version:            0.2
+version:            0.3
 license:            Apache
 license-file:       LICENSE
 maintainer:         mail@joachim-breitner.de
 author:             Joachim Breitner
-homepage:           https://github.com/dfinity/candid
+homepage:           https://github.com/nomeata/haskell-candid
 synopsis:           Candid integration
 description:
     This package brings the Candid interface definition language to Haskell,
@@ -17,7 +17,7 @@
 
 category:           Codec
 build-type:         Simple
-extra-source-files: CHANGELOG.md
+extra-source-files: CHANGELOG.md words.txt
 
 library
     exposed-modules:
@@ -35,6 +35,7 @@
         Codec.Candid.Data
         Codec.Candid.Types
         Codec.Candid.FieldName
+        Codec.Candid.Hash
         Codec.Candid.TypTable
         Codec.Candid.Decode
         Codec.Candid.EncodeTextual
@@ -63,10 +64,11 @@
         unordered-containers >=0.2.10.0 && <0.3,
         row-types > 1.0.0.0 && < 1.1,
         constraints >=0.12 && <0.14,
-        prettyprinter >=1.6.2 && <1.8,
+        prettyprinter >=1.7 && <1.8,
         template-haskell >=2.14.0.0 && <2.17,
         base32 >=0.1.1.2 && <0.3,
-        split >=0.2.3.4 && <0.3
+        split >=0.2.3.4 && <0.3,
+        file-embed
 
 executable hcandid
     main-is:          hcandid.hs
diff --git a/hcandid.hs b/hcandid.hs
--- a/hcandid.hs
+++ b/hcandid.hs
@@ -9,8 +9,8 @@
 import Data.Char
 import System.IO
 import System.Exit
-import Data.Text.Prettyprint.Doc
-import Data.Text.Prettyprint.Doc.Util
+import Prettyprinter
+import Prettyprinter.Util
 
 err :: String -> IO b
 err s = hPutStr stderr s >> exitFailure
@@ -37,7 +37,7 @@
 decodeCandid :: BS.ByteString -> IO ()
 decodeCandid b = case decodeVals b of
   Left msg -> err msg
-  Right vs -> putDocW 80 (pretty vs) >> putStrLn ""
+  Right (_, vs) -> putDocW 80 (pretty vs) >> putStrLn ""
 
 encodeCandid :: String -> IO ()
 encodeCandid txt = case parseValues txt of
diff --git a/src/Codec/Candid.hs b/src/Codec/Candid.hs
--- a/src/Codec/Candid.hs
+++ b/src/Codec/Candid.hs
@@ -154,7 +154,7 @@
 -- >>> import Data.Text (Text)
 -- >>> import qualified Data.Text as T
 -- >>> import Data.Void (Void)
--- >>> import Data.Text.Prettyprint.Doc (pretty)
+-- >>> import Prettyprinter (pretty)
 -- >>> import qualified Data.ByteString.Lazy.Char8 as BS
 -- >>> :set -XScopedTypeVariables
 
diff --git a/src/Codec/Candid/Class.hs b/src/Codec/Candid/Class.hs
--- a/src/Codec/Candid/Class.hs
+++ b/src/Codec/Candid/Class.hs
@@ -42,7 +42,7 @@
 import Data.Word
 import Data.Int
 import Data.Void
-import Data.Text.Prettyprint.Doc
+import Prettyprinter
 import Language.Haskell.TH (mkName, tupleDataName)
 import Language.Haskell.TH.Lib
   ( appT, tupleT, varT, litT, strTyLit
diff --git a/src/Codec/Candid/Coerce.hs b/src/Codec/Candid/Coerce.hs
--- a/src/Codec/Candid/Coerce.hs
+++ b/src/Codec/Candid/Coerce.hs
@@ -11,7 +11,7 @@
   )
   where
 
-import Data.Text.Prettyprint.Doc
+import Prettyprinter
 import qualified Data.Vector as V
 import qualified Data.ByteString.Lazy as BS
 import qualified Data.Map as M
diff --git a/src/Codec/Candid/Encode.hs b/src/Codec/Candid/Encode.hs
--- a/src/Codec/Candid/Encode.hs
+++ b/src/Codec/Candid/Encode.hs
@@ -20,7 +20,7 @@
 import Data.Void
 import Control.Monad.RWS.Lazy
 import Data.Serialize.LEB128
-import Data.Text.Prettyprint.Doc
+import Prettyprinter
 
 import Codec.Candid.Data
 import Codec.Candid.TypTable
diff --git a/src/Codec/Candid/FieldName.hs b/src/Codec/Candid/FieldName.hs
--- a/src/Codec/Candid/FieldName.hs
+++ b/src/Codec/Candid/FieldName.hs
@@ -14,17 +14,15 @@
   ) where
 
 import qualified Data.Text as T
-import qualified Data.Text.Encoding as T
-import qualified Data.ByteString.Lazy as BS
-import Data.Text.Prettyprint.Doc
+import Prettyprinter
 import Data.String
-import Data.Maybe
 import Data.Word
-import Data.Char
 import Numeric.Natural
 import Data.Function
 import Text.Read (readMaybe)
 
+import Codec.Candid.Hash
+
 -- | A type for a Candid field name. Essentially a 'Word32' with maybe a textual label attached
 data FieldName = FieldName
     { fieldHash :: Word32 -- ^ Extract the raw field hash value
@@ -39,39 +37,6 @@
 -- | Create a 'FieldName' from the raw hash
 hashedField :: Word32 -> FieldName
 hashedField h = FieldName h Nothing
-
--- | The Candid field label hashing algorithm
-candidHash :: T.Text -> Word32
-candidHash s = BS.foldl (\h c -> h * 223 + fromIntegral c) 0 $ BS.fromStrict $ T.encodeUtf8 s
-
--- | Inversion of the Candid field label hash
-invertHash :: Word32 -> Maybe T.Text
-invertHash w32 | w32 < 32 = Nothing
-    -- leave small numbers alone, tend to be tuple indicies
-invertHash w32 = listToMaybe guesses
-  where
-    x = fromIntegral w32 :: Word64
-    chars = ['a'..'z'] ++ ['_']
-    ords = 0 : map (fromIntegral . ord) chars
-    non_mod x = x - (x `mod` 2^(32::Int))
-    guesses =
-        [ T.pack $ reverse guess
-        | c8 <- ords, c7 <- ords, c6 <- ords, c5 <- ords
-        -- It seems that 8 characters are enough to invert anything
-        -- (based on quickchecking)
-        -- Set up so that short guesses come first
-        , let high_chars = c5 * 223^(4::Int) + c6 * 223^(5::Int) + c7 * 223^(6::Int) + c8 * 223^(7::Int)
-        , let guess = simple $ x + non_mod high_chars
-        , all (`elem` chars) guess
-        ]
-
-    -- inverts the Hash if the hash was created without modulos
-    -- returns string in reverse order
-    simple :: Word64 -> String
-    simple 0 = ""
-    simple x = chr (fromIntegral b) : simple a
-      where (a, b) = x `divMod` 223
-
 
 instance Eq FieldName where
     (==) = (==) `on` fieldHash
diff --git a/src/Codec/Candid/Hash.hs b/src/Codec/Candid/Hash.hs
new file mode 100644
--- /dev/null
+++ b/src/Codec/Candid/Hash.hs
@@ -0,0 +1,59 @@
+{-# LANGUAGE TemplateHaskell #-}
+-- | The hash algorithm used for Candid field names
+--
+-- Also includes a function that tries to reverse the hash, first using an
+-- English word list, and then a brute force approach.
+module Codec.Candid.Hash
+  ( candidHash
+  , invertHash
+  ) where
+
+import qualified Data.Text as T
+import qualified Data.Text.Encoding as T
+import qualified Data.ByteString.Lazy as BS
+import qualified Data.IntMap as M
+import Data.Maybe
+import Data.Char
+import Data.Word
+import Data.FileEmbed
+
+-- | The Candid field label hashing algorithm
+candidHash :: T.Text -> Word32
+candidHash s = BS.foldl (\h c -> h * 223 + fromIntegral c) 0 $ BS.fromStrict $ T.encodeUtf8 s
+
+-- | Inversion of the Candid field label hash
+invertHash :: Word32 -> Maybe T.Text
+invertHash w32 | w32 < 32 = Nothing
+    -- leave small numbers alone, tend to be tuple indicies
+invertHash w32 | Just t <- M.lookup (fromIntegral w32) m  = Just t
+    -- try the word list
+invertHash w32 = listToMaybe guesses
+  where
+    x = fromIntegral w32 :: Word64
+    chars = ['a'..'z'] ++ ['_']
+    ords = 0 : map (fromIntegral . ord) chars
+    non_mod x = x - (x `mod` 2^(32::Int))
+    guesses =
+        [ T.pack $ reverse guess
+        | c8 <- ords, c7 <- ords, c6 <- ords, c5 <- ords
+        -- It seems that 8 characters are enough to invert anything
+        -- (based on quickchecking)
+        -- Set up so that short guesses come first
+        , let high_chars = c5 * 223^(4::Int) + c6 * 223^(5::Int) + c7 * 223^(6::Int) + c8 * 223^(7::Int)
+        , let guess = simple $ x + non_mod high_chars
+        , all (`elem` chars) guess
+        ]
+
+    -- inverts the Hash if the hash was created without modulos
+    -- returns string in reverse order
+    simple :: Word64 -> String
+    simple 0 = ""
+    simple x = chr (fromIntegral b) : simple a
+      where (a, b) = x `divMod` 223
+
+-- Word list obtained from https://github.com/dwyl/english-words
+ws :: T.Text
+ws = $(embedStringFile "words.txt")
+
+m :: M.IntMap T.Text
+m = M.fromList [ (fromIntegral (candidHash w), w) | w <- T.lines ws]
diff --git a/src/Codec/Candid/Infer.hs b/src/Codec/Candid/Infer.hs
--- a/src/Codec/Candid/Infer.hs
+++ b/src/Codec/Candid/Infer.hs
@@ -6,7 +6,7 @@
 import Control.Monad
 import Data.Void
 import Data.List
-import Data.Text.Prettyprint.Doc
+import Prettyprinter
 
 import Codec.Candid.Types
 
diff --git a/src/Codec/Candid/TypTable.hs b/src/Codec/Candid/TypTable.hs
--- a/src/Codec/Candid/TypTable.hs
+++ b/src/Codec/Candid/TypTable.hs
@@ -16,7 +16,7 @@
 import qualified Data.Map as M
 import Control.Monad.State.Lazy
 import Data.Void
-import Data.Text.Prettyprint.Doc
+import Prettyprinter
 import Data.DList (singleton, DList)
 import Data.Graph
 import Data.Foldable
diff --git a/src/Codec/Candid/Types.hs b/src/Codec/Candid/Types.hs
--- a/src/Codec/Candid/Types.hs
+++ b/src/Codec/Candid/Types.hs
@@ -15,7 +15,7 @@
 import Data.Char
 import Numeric
 
-import Data.Text.Prettyprint.Doc
+import Prettyprinter
 
 import Codec.Candid.Data
 import Codec.Candid.FieldName
diff --git a/test/test.hs b/test/test.hs
--- a/test/test.hs
+++ b/test/test.hs
@@ -36,7 +36,7 @@
 import GHC.Word
 import Numeric.Natural
 import GHC.Generics (Generic)
-import Data.Text.Prettyprint.Doc
+import Prettyprinter
 import Data.Row
 import Data.Proxy
 import qualified Data.Row.Records as R
@@ -411,6 +411,9 @@
         QC.forAll (QC.choose (0,4)) $ \len ->
         QC.forAll (T.pack <$> QC.vectorOf len (QC.elements ('_':['a'..'z']))) $ \s ->
         candidHash s >= 32 QC.==>
+        invertHash (candidHash s) QC.=== Just s
+    , QC.testProperty "long dictionary name" $
+        let s = "precriticized" in
         invertHash (candidHash s) QC.=== Just s
     , QC.testProperty "all hashes find something" $
         QC.forAll QC.arbitraryBoundedIntegral $ \w ->
diff --git a/words.txt b/words.txt
new file mode 100644
# file too large to diff: words.txt
