diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright Author name here (c) 2017
+Copyright John Ky (c) 2017-2018
 
 All rights reserved.
 
diff --git a/hw-ip.cabal b/hw-ip.cabal
--- a/hw-ip.cabal
+++ b/hw-ip.cabal
@@ -1,11 +1,9 @@
--- This file has been generated from package.yaml by hpack version 0.20.0.
+-- This file has been generated from package.yaml by hpack version 0.18.1.
 --
 -- see: https://github.com/sol/hpack
---
--- hash: 0e009dc6f8e47e478d23c2015665d88494848c9a86e5c1b03550fe2351cda87e
 
 name:           hw-ip
-version:        0.1.0.0
+version:        0.2.0.0
 synopsis:       Library for manipulating IP addresses and CIDR blocks
 description:    Please see README.md
 category:       Network
@@ -30,10 +28,16 @@
   hs-source-dirs:
       src
   build-depends:
-      base >=4.7 && <5
+      attoparsec
+    , generic-lens
+    , hw-bits
+    , text
+    , base >=4.7 && <5
   exposed-modules:
       HaskellWorks.Data.Network.Ip
   other-modules:
+      HaskellWorks.Data.Network.Ip.Internal
+      HaskellWorks.Data.Network.Ip.Type
       Paths_hw_ip
   default-language: Haskell2010
 
@@ -44,12 +48,15 @@
       test
   ghc-options: -threaded -rtsopts -with-rtsopts=-N
   build-depends:
-      base
+      attoparsec
+    , generic-lens
+    , hw-bits
+    , text
+    , base
     , hedgehog
-    , hspec
     , hw-hspec-hedgehog
     , hw-ip
+    , hspec
   other-modules:
       HaskellWorks.Data.Network.IpSpec
-      Paths_hw_ip
   default-language: Haskell2010
diff --git a/src/HaskellWorks/Data/Network/Ip.hs b/src/HaskellWorks/Data/Network/Ip.hs
--- a/src/HaskellWorks/Data/Network/Ip.hs
+++ b/src/HaskellWorks/Data/Network/Ip.hs
@@ -1,59 +1,39 @@
-{-# LANGUAGE BangPatterns               #-}
-{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE BangPatterns          #-}
+{-# LANGUAGE DuplicateRecordFields #-}
 
 module HaskellWorks.Data.Network.Ip
-  ( Ipv4Address(..)
-  , Ipv4NetMask(..)
-  , Ipv4Block(..)
+  ( Z.Ipv4Address(Ipv4Address)
+  , Z.Ipv4NetMask(Ipv4NetMask)
+  , Z.Ipv4Block(Ipv4Block)
   , bitPower
   , blockSize
   , isCanonical
   , splitBlock
   ) where
 
-import Data.Bits
 import Data.Word
-
-newtype Ipv4Address = Ipv4Address
-  { unIpv4Address :: Word32
-  } deriving (Enum, Eq, Ord)
-
-instance Show Ipv4Address where
-  showsPrec _ (Ipv4Address w) =
-    shows ((w `shiftR` 24) .&. 0xff) . ('.':) .
-    shows ((w `shiftR` 16) .&. 0xff) . ('.':) .
-    shows ((w `shiftR`  8) .&. 0xff) . ('.':) .
-    shows ( w              .&. 0xff)
-
-newtype Ipv4NetMask = Ipv4NetMask
-  { unIpv4NetMask :: Word8
-  } deriving (Enum, Eq, Ord, Show)
-
-data Ipv4Block = Ipv4Block
-  { ipv4BlockBase :: Ipv4Address
-  , ipv4BlockMask :: Ipv4NetMask
-  } deriving (Eq, Ord)
+import GHC.Generics
+import HaskellWorks.Data.Bits.BitWise
 
-instance Show Ipv4Block where
-  showsPrec _ (Ipv4Block b (Ipv4NetMask m)) = shows b . ('/':) . shows m
+import qualified HaskellWorks.Data.Network.Ip.Type as Z
 
-bitPower :: Ipv4NetMask -> Int
-bitPower (Ipv4NetMask m) = fromIntegral (32 - m)
+bitPower :: Z.Ipv4NetMask -> Word64
+bitPower (Z.Ipv4NetMask m) = fromIntegral (32 - m)
 
-isCanonical :: Ipv4Block -> Bool
-isCanonical (Ipv4Block (Ipv4Address b) m) = ((b `shiftR` bitPower m) `shiftL` bitPower m) == b
+isCanonical :: Z.Ipv4Block -> Bool
+isCanonical (Z.Ipv4Block (Z.Ipv4Address b) m) = ((b .>. bitPower m) .<. bitPower m) == b
 
-splitBlock :: Ipv4Block -> Maybe (Ipv4Block, Ipv4Block)
-splitBlock (Ipv4Block (Ipv4Address b) (Ipv4NetMask m)) =
+splitBlock :: Z.Ipv4Block -> Maybe (Z.Ipv4Block, Z.Ipv4Block)
+splitBlock (Z.Ipv4Block (Z.Ipv4Address b) (Z.Ipv4NetMask m)) =
   if m >= 0 && m < 32
     then  let !hm       = m + 1
-              !halfMask = Ipv4NetMask hm
-              !c        = fromIntegral ((0x100000000 :: Word64) `shiftR` fromIntegral (m + 1))
+              !halfMask = Z.Ipv4NetMask hm
+              !c        = fromIntegral ((0x100000000 :: Word64) .>. fromIntegral (m + 1))
           in  Just
-              ( Ipv4Block (Ipv4Address  b     ) halfMask
-              , Ipv4Block (Ipv4Address (b + c)) halfMask
+              ( Z.Ipv4Block (Z.Ipv4Address  b     ) halfMask
+              , Z.Ipv4Block (Z.Ipv4Address (b + c)) halfMask
               )
     else  Nothing
 
-blockSize :: Ipv4Block -> Int
-blockSize (Ipv4Block _ m) = 2 ^ bitPower m
+blockSize :: Z.Ipv4Block -> Int
+blockSize (Z.Ipv4Block _ m) = 2 ^ bitPower m
diff --git a/src/HaskellWorks/Data/Network/Ip/Internal.hs b/src/HaskellWorks/Data/Network/Ip/Internal.hs
new file mode 100644
--- /dev/null
+++ b/src/HaskellWorks/Data/Network/Ip/Internal.hs
@@ -0,0 +1,47 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module HaskellWorks.Data.Network.Ip.Internal where
+
+import Control.Applicative
+import Control.Monad
+import Data.Char
+import Data.Word
+import HaskellWorks.Data.Bits.BitWise
+
+import qualified Data.Attoparsec.Text as AP
+
+fourOctetsToWord32 :: Word8 -> Word8 -> Word8 -> Word8 -> Word32
+fourOctetsToWord32 a b c d =
+  (fromIntegral a .<. 24) .|.
+  (fromIntegral b .<. 16) .|.
+  (fromIntegral c .<.  8) .|.
+   fromIntegral d
+{-# INLINE fourOctetsToWord32 #-}
+
+infixl 2 #<*>#
+
+(#<*>#) :: AP.Parser Word8 -> AP.Parser Word8 -> AP.Parser Word8
+(#<*>#) pa pb = paste <$> pa <*> pb
+  where paste a b = a * 10 + b
+
+octet :: AP.Parser Word8
+octet = ((d12 #<*># d5 ) #<*># d05)
+  <|>   ((d12 #<*># d04) #<*># d09)
+  <|>   ( d19 #<*># d09)
+  <|>   d09
+  where d5  = fromIntegral . (+ (-48)) . ord <$> AP.satisfy (== '5')
+        d04 = fromIntegral . (+ (-48)) . ord <$> AP.satisfy (\c -> c >= '0' && c <= '4')
+        d05 = fromIntegral . (+ (-48)) . ord <$> AP.satisfy (\c -> c >= '0' && c <= '5')
+        d09 = fromIntegral . (+ (-48)) . ord <$> AP.satisfy (\c -> c >= '0' && c <= '9')
+        d19 = fromIntegral . (+ (-48)) . ord <$> AP.satisfy (\c -> c >= '1' && c <= '9')
+        d12 = fromIntegral . (+ (-48)) . ord <$> AP.satisfy (\c -> c >= '1' && c <= '2')
+
+ipv4Address :: AP.Parser Word32
+ipv4Address = fourOctetsToWord32
+  <$> (octet <* AP.char '.')
+  <*> (octet <* AP.char '.')
+  <*> (octet <* AP.char '.')
+  <*>  octet
+
+whitespace :: AP.Parser ()
+whitespace = void $ many (AP.satisfy isSpace)
diff --git a/src/HaskellWorks/Data/Network/Ip/Type.hs b/src/HaskellWorks/Data/Network/Ip/Type.hs
new file mode 100644
--- /dev/null
+++ b/src/HaskellWorks/Data/Network/Ip/Type.hs
@@ -0,0 +1,51 @@
+{-# LANGUAGE DeriveGeneric              #-}
+{-# LANGUAGE DuplicateRecordFields      #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE InstanceSigs               #-}
+
+module HaskellWorks.Data.Network.Ip.Type
+  ( Ipv4Address(..)
+  , Ipv4NetMask(..)
+  , Ipv4Block(..)
+  ) where
+
+import Data.Word
+import GHC.Generics
+import HaskellWorks.Data.Bits.BitWise
+
+import qualified Data.Attoparsec.Text                  as AP
+import qualified Data.Text                             as T
+import qualified HaskellWorks.Data.Network.Ip.Internal as I
+import qualified Text.ParserCombinators.ReadPrec       as RP
+
+newtype Ipv4Address = Ipv4Address
+  { word :: Word32
+  } deriving (Enum, Eq, Ord, Generic)
+
+instance Show Ipv4Address where
+  showsPrec _ (Ipv4Address w) =
+    shows ((w .>. 24) .&. 0xff) . ('.':) .
+    shows ((w .>. 16) .&. 0xff) . ('.':) .
+    shows ((w .>.  8) .&. 0xff) . ('.':) .
+    shows ( w         .&. 0xff)
+
+instance Read Ipv4Address where
+  readsPrec :: Int -> String -> [(Ipv4Address, String)]
+  readsPrec _ s = case AP.parseWith (return mempty) (I.whitespace *> I.ipv4Address) (T.pack s) of
+    Just result -> case result of
+      AP.Done i r   -> [(Ipv4Address r, T.unpack i)]
+      AP.Partial _  -> []
+      AP.Fail a b c -> []
+    Nothing -> []
+
+newtype Ipv4NetMask = Ipv4NetMask
+  { word :: Word8
+  } deriving (Enum, Eq, Ord, Show, Generic)
+
+data Ipv4Block = Ipv4Block
+  { base :: !Ipv4Address
+  , mask :: !Ipv4NetMask
+  } deriving (Eq, Ord)
+
+instance Show Ipv4Block where
+  showsPrec _ (Ipv4Block b (Ipv4NetMask m)) = shows b . ('/':) . shows m
