diff --git a/ip.cabal b/ip.cabal
--- a/ip.cabal
+++ b/ip.cabal
@@ -1,5 +1,5 @@
 name: ip
-version: 1.4.0
+version: 1.4.1
 synopsis: Library for IP and MAC addresses
 homepage: https://github.com/andrewthad/haskell-ip#readme
 license: BSD3
@@ -47,13 +47,14 @@
     Data.ByteString.Builder.Fixed
   build-depends:
       base >= 4.9.1.0 && < 5
-    , attoparsec >= 0.13 && < 0.14
     , aeson >= 1.0 && < 1.5
+    , attoparsec >= 0.13 && < 0.14
+    , bytestring >= 0.10 && < 0.11
+    , deepseq >= 1.4 && < 1.5 
     , hashable >= 1.2 && < 1.3
+    , primitive >= 0.6 && < 0.7
     , text >= 1.2  && < 1.3
-    , bytestring >= 0.10 && < 0.11
     , vector >= 0.11 && < 0.13
-    , primitive >= 0.6 && < 0.7
   ghc-options: -Wall -O2
   default-language: Haskell2010
 
diff --git a/src/Net/IP.hs b/src/Net/IP.hs
--- a/src/Net/IP.hs
+++ b/src/Net/IP.hs
@@ -1,3 +1,5 @@
+{-# LANGUAGE DeriveGeneric #-}
+
 {-# OPTIONS_GHC -Wall #-}
 
 {-| An IP data type representing either an IPv4 address or
@@ -43,7 +45,9 @@
   ) where
 
 import Prelude hiding (print)
+import Control.DeepSeq (NFData)
 import Data.Bits
+import GHC.Generics (Generic)
 import Net.IPv6 (IPv6(..))
 import Net.IPv4 (IPv4(..))
 import Text.Read (Read(..))
@@ -98,7 +102,9 @@
 --   of this type. All functions and typeclass methods that convert
 --   'IP' values to text will display it as an 'IPv4' address if possible.
 newtype IP = IP { getIP :: IPv6 }
-  deriving (Eq,Ord)
+  deriving (Eq,Ord,Generic)
+
+instance NFData IP
 
 instance Show IP where
   showsPrec p = case_ (showsPrec p) (showsPrec p)
diff --git a/src/Net/IPv4.hs b/src/Net/IPv4.hs
--- a/src/Net/IPv4.hs
+++ b/src/Net/IPv4.hs
@@ -84,6 +84,7 @@
     -- $interoperability
   ) where
 
+import Control.DeepSeq (NFData)
 import Control.Monad
 import Control.Monad.ST (ST,runST)
 import Data.Aeson (FromJSON(..),ToJSON(..))
@@ -357,6 +358,8 @@
 newtype IPv4 = IPv4 { getIPv4 :: Word32 }
   deriving (Eq,Ord,Enum,Bounded,Hashable,Generic,Prim,Storable)
 
+instance NFData IPv4
+
 instance Show IPv4 where
   showsPrec p addr = showParen (p > 10)
     $ showString "ipv4 "
@@ -858,7 +861,7 @@
   , ipv4RangeLength :: {-# UNPACK #-} !Word8
   } deriving (Eq,Ord,Show,Read,Generic)
 
-
+instance NFData IPv4Range
 instance Hashable IPv4Range
 
 instance ToJSON IPv4Range where
diff --git a/src/Net/IPv6.hs b/src/Net/IPv6.hs
--- a/src/Net/IPv6.hs
+++ b/src/Net/IPv6.hs
@@ -1,11 +1,12 @@
 {-# LANGUAGE BangPatterns        #-}
+{-# LANGUAGE CPP                 #-}
+{-# LANGUAGE DeriveGeneric       #-}
 {-# LANGUAGE InstanceSigs        #-}
 {-# LANGUAGE LambdaCase          #-}
 {-# LANGUAGE MagicHash           #-}
 {-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE StandaloneDeriving  #-}
 {-# LANGUAGE UnboxedTuples       #-}
-{-# LANGUAGE DeriveGeneric       #-}
-{-# LANGUAGE CPP                 #-}
 
 {-# OPTIONS_GHC -Wall #-}
 
@@ -53,15 +54,18 @@
 import qualified Net.IPv4 as IPv4
 
 import Control.Applicative
+import Control.DeepSeq (NFData)
 import Control.Monad.Primitive
 import Control.Monad.ST
 import Data.Bits
-import Data.ByteString (ByteString)
 import Data.Char (chr)
 import Data.List (intercalate, group)
 import Data.Primitive.Addr
 import Data.Primitive.ByteArray
 import Data.Primitive.Types (Prim(..))
+#if !MIN_VERSION_base(4,11,0)
+import Data.Semigroup ((<>))
+#endif
 import Data.Text (Text)
 import Data.Word
 import GHC.Enum (predError, succError)
@@ -70,12 +74,10 @@
 import Numeric (showHex)
 import Prelude hiding (any, print)
 import Text.ParserCombinators.ReadPrec (prec,step)
-import Text.Printf (printf)
 import Text.Read (Read(..),Lexeme(Ident),lexP,parens)
 import qualified Data.Aeson as Aeson
 import qualified Data.Attoparsec.Text as AT
 import qualified Data.Attoparsec.Text as Atto
-import qualified Data.ByteString.Char8 as BC8
 import qualified Data.Text as Text
 import qualified Data.Text.IO as TIO
 
@@ -94,8 +96,10 @@
 data IPv6 = IPv6
   { ipv6A :: {-# UNPACK #-} !Word64
   , ipv6B :: {-# UNPACK #-} !Word64
-  } deriving (Eq,Ord)
+  } deriving (Eq,Ord,Generic)
 
+instance NFData IPv6
+
 -- | Since 'IPv6' has more inhabitants than 'Int', the
 -- implementation of 'fromEnum' discards information.
 -- Currently, 'enumFromThen' and 'enumFromThenTo' emit
@@ -496,6 +500,8 @@
   { ipv6RangeBase   :: {-# UNPACK #-} !IPv6
   , ipv6RangeLength :: {-# UNPACK #-} !Word8
   } deriving (Eq,Ord,Show,Read,Generic)
+
+instance NFData IPv6Range
 
 mask :: Word8 -> Word64
 mask w = if w > 63
diff --git a/src/Net/Mac.hs b/src/Net/Mac.hs
--- a/src/Net/Mac.hs
+++ b/src/Net/Mac.hs
@@ -44,39 +44,42 @@
   ) where
 
 import Prelude hiding (print)
-import Data.Word
+
+import Control.DeepSeq (NFData)
+import Data.Aeson (FromJSON(..),ToJSON(..))
+import Data.Aeson (ToJSONKey(..),FromJSONKey(..))
+import Data.Aeson (ToJSONKeyFunction(..),FromJSONKeyFunction(..))
 import Data.Bits ((.|.),unsafeShiftL,unsafeShiftR,(.&.))
-import Data.Text (Text)
-import Data.Word (Word8)
-import Data.Word.Synthetic.Word12 (Word12)
-import Data.Monoid
 import Data.ByteString (ByteString)
-import Data.Aeson (FromJSON(..),ToJSON(..))
-import Data.Hashable (Hashable)
-import GHC.Generics (Generic)
 import Data.Char (ord,chr)
+import Data.Hashable (Hashable)
 import Data.Primitive.Types (Prim(..))
+#if !MIN_VERSION_base(4,11,0)
+import Data.Semigroup ((<>))
+#endif
+import Data.Text (Text)
+import Data.Word
+import Data.Word (Word8)
+import Data.Word.Synthetic.Word12 (Word12)
+import GHC.Enum (predError, succError)
 import GHC.Exts
-import Text.Read (Read(..),Lexeme(Ident),lexP,parens)
-import Text.ParserCombinators.ReadPrec (prec,step)
+import GHC.Generics (Generic)
 import GHC.Word (Word16(W16#))
-import qualified Data.ByteString as B
-import qualified Data.ByteString.Unsafe as BU
-import qualified Data.ByteString.Builder as BB
+import Text.ParserCombinators.ReadPrec (prec,step)
+import Text.Read (Read(..),Lexeme(Ident),lexP,parens)
+
+import qualified Data.Aeson as Aeson
+import qualified Data.Aeson.Types as Aeson
+import qualified Data.Attoparsec.ByteString as AB
 import qualified Data.Attoparsec.ByteString as ABW
 import qualified Data.Attoparsec.Text as AT
-import qualified Data.Attoparsec.ByteString as AB
-import qualified Data.Text.Lazy.Builder as TBuilder
-import qualified Data.Text.Builder.Fixed as TFB
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Builder as BB
 import qualified Data.ByteString.Builder.Fixed as BFB
-import qualified Data.Aeson as Aeson
-import qualified Data.Aeson.Types as Aeson
+import qualified Data.ByteString.Unsafe as BU
+import qualified Data.Text.Builder.Fixed as TFB
 import qualified Data.Text.IO as TIO
-
-#if MIN_VERSION_aeson(1,0,0) 
-import Data.Aeson (ToJSONKey(..),FromJSONKey(..),
-  ToJSONKeyFunction(..),FromJSONKeyFunction(..))
-#endif
+import qualified Data.Text.Lazy.Builder as TBuilder
 
 -- $setup
 --
@@ -548,6 +551,8 @@
 newtype Mac = Mac Word64
   deriving (Eq,Ord,Generic)
 
+instance NFData Mac
+
 -- | This only preserves the lower 6 bytes of the 8-byte word that backs a mac address.
 -- It runs slower than it would if it used a full 8-byte word, but it consumes less
 -- space. When storing millions of mac addresses, this is a good trade to make. When
@@ -598,7 +603,6 @@
           s1 -> go (ix# +# 1#) s1
         else s0
 
-
 macToWord16A# :: Mac -> Word#
 macToWord16A# (Mac w) = case word64ToWord16 (unsafeShiftR w 32) of
   W16# x -> x
@@ -638,6 +642,20 @@
     Ident "mac" <- lexP
     w <- step readPrec
     return (mac w)
+
+instance Bounded Mac where
+  minBound = Mac 0
+  maxBound = Mac 0xFFFFFFFFFFFF
+
+instance Enum Mac where
+  succ m@(Mac x)
+    | m == maxBound = succError "Mac"
+    | otherwise = Mac (x + 1)
+  pred m@(Mac x)
+    | m == minBound = predError "Mac"
+    | otherwise = Mac (x - 1)
+  toEnum i = Mac (toEnum i)
+  fromEnum (Mac x) = fromEnum x
 
 print :: Mac -> IO ()
 print = TIO.putStrLn . encode
