diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,13 @@
+POSIX-Socket
+===========
+
+[![Travis](https://img.shields.io/travis/mvv/posix-socket/master.svg)](https://travis-ci.org/mvv/posix-socket) [![Hackage](https://img.shields.io/hackage/v/posix-socket.svg)](http://hackage.haskell.org/package/posix-socket)
+
+This package provides bindings to the POSIX socket API.
+
+Installation
+------------
+The usual:
+
+	$ cabal install
+
diff --git a/posix-socket.cabal b/posix-socket.cabal
--- a/posix-socket.cabal
+++ b/posix-socket.cabal
@@ -1,5 +1,5 @@
 Name: posix-socket
-Version: 0.2
+Version: 0.3
 Category: System
 Stability: experimental
 Synopsis: Bindings to the POSIX socket API
@@ -17,8 +17,9 @@
 
 Extra-Source-Files:
   include/posix-socket.macros.h
+  README.md
 
-Tested-With: GHC==7.10.3, GHC==8.0.2, GHC==8.2.2
+Tested-With: GHC==7.10.3, GHC==8.0.2, GHC==8.2.2, GHC==8.4.1
 
 Cabal-Version: >= 1.6.0
 Build-Type: Simple
diff --git a/src/System/Posix/Socket.hsc b/src/System/Posix/Socket.hsc
--- a/src/System/Posix/Socket.hsc
+++ b/src/System/Posix/Socket.hsc
@@ -4,6 +4,7 @@
 {-# LANGUAGE TemplateHaskell #-}
 {-# LANGUAGE DeriveDataTypeable #-}
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE EmptyDataDecls #-}
 {-# LANGUAGE DoAndIfThenElse #-}
 {-# LANGUAGE TupleSections #-}
 {-# LANGUAGE TypeFamilies #-}
@@ -31,9 +32,12 @@
   , SockProto(..)
   , defaultSockProto
   , SockOpt(..)
-  , SO_ERROR(..)
-  , SO_KEEPALIVE(..)
-  , SO_REUSEADDR(..)
+  , SO_ERROR
+  , pattern SO_ERROR
+  , SO_KEEPALIVE
+  , pattern SO_KEEPALIVE
+  , SO_REUSEADDR
+  , pattern SO_REUSEADDR
   , SockOps(..)
   , MsgFlags(..)
   , pattern MSG_PEEK
@@ -77,6 +81,9 @@
   , sendTo'
   , sendTo
   -- ** Closing
+  , pattern SHUT_RD
+  , pattern SHUT_WR
+  , pattern SHUT_RDWR
   , shutdown
   , close
   ) where
@@ -173,7 +180,7 @@
 class SockAddr (SockFamilyAddr f) ⇒ SockFamily f where
   type SockFamilyAddr f
   -- | Socket family code.
-  sockFamilyCode ∷ f → CInt
+  sockFamilyCode ∷ Proxy f → CInt
 
 -- | Socket type.
 newtype SockType = SockType CInt deriving (Typeable, Eq, Ord, Show, Storable)
@@ -219,16 +226,19 @@
   -- | Whether option is writable
   type SockOptWritable o ∷ Bool
   -- | Convert to FFI-level value
-  sockOptRaw   ∷ o → SockOptValue o → SockOptRaw o
+  sockOptRaw   ∷ Proxy o → SockOptValue o → SockOptRaw o
   -- | Convert from FFI-level value
-  sockOptValue ∷ o → SockOptRaw o → SockOptValue o
+  sockOptValue ∷ Proxy o → SockOptRaw o → SockOptValue o
   -- | Option protocol level
-  sockOptLevel ∷ o → CInt
+  sockOptLevel ∷ Proxy o → CInt
   -- | Option code
-  sockOptCode  ∷ o → CInt
+  sockOptCode  ∷ Proxy o → CInt
 
-data SO_ERROR = SO_ERROR deriving (Typeable, Eq, Show)
+data SO_ERROR deriving Typeable
 
+pattern SO_ERROR ∷ Proxy SO_ERROR
+pattern SO_ERROR = Proxy
+
 instance SockOpt SO_ERROR where
   type SockOptValue    SO_ERROR = Errno
   type SockOptRaw      SO_ERROR = CInt
@@ -239,8 +249,11 @@
   sockOptLevel _ = #const SOL_SOCKET
   sockOptCode  _ = #const SO_ERROR
 
-data SO_KEEPALIVE = SO_KEEPALIVE deriving (Typeable, Eq, Show)
+data SO_KEEPALIVE deriving Typeable
 
+pattern SO_KEEPALIVE ∷ Proxy SO_KEEPALIVE
+pattern SO_KEEPALIVE = Proxy
+
 instance SockOpt SO_KEEPALIVE where
   type SockOptValue    SO_KEEPALIVE = Bool
   type SockOptRaw      SO_KEEPALIVE = CInt
@@ -252,8 +265,11 @@
   sockOptLevel _ = #const SOL_SOCKET
   sockOptCode  _ = #const SO_KEEPALIVE
 
-data SO_REUSEADDR = SO_REUSEADDR deriving (Typeable, Eq, Show)
+data SO_REUSEADDR deriving Typeable
 
+pattern SO_REUSEADDR ∷ Proxy SO_REUSEADDR
+pattern SO_REUSEADDR = Proxy
+
 instance SockOpt SO_REUSEADDR where
   type SockOptValue    SO_REUSEADDR = Bool
   type SockOptRaw      SO_REUSEADDR = CInt
@@ -322,7 +338,7 @@
   where size = sockAddrSize addr
 
 peekAddrOfSize ∷ SockFamily f
-               ⇒ f → Ptr (SockFamilyAddr f) → Ptr #{itype socklen_t}
+               ⇒ Proxy f → Ptr (SockFamilyAddr f) → Ptr #{itype socklen_t}
                → IO (SockFamilyAddr f)
 peekAddrOfSize fam p pSize = do
   outSize ← fromIntegral <$> peek pSize
@@ -332,7 +348,7 @@
   peekSockAddr p outSize
 
 withAddr ∷ SockFamily f
-         ⇒ f
+         ⇒ Proxy f
          → SockFamilyAddr f
          → (Ptr (SockFamilyAddr f) → #{itype socklen_t} → IO α)
          → IO α
@@ -347,7 +363,7 @@
 -- | Create a socket. The underlying file descriptor is non-blocking. All
 -- blocking operations are done via the GHC event manager. See /socket(2)/.
 socket ∷ (SockFamily f, MonadBase IO μ)
-       ⇒ f → SockType → SockProto → μ (Socket f)
+       ⇒ Proxy f → SockType → SockProto → μ (Socket f)
 socket f (SockType t) p = liftBase $ do
   fd ← throwErrnoIfMinus1 "socket" $
          c_socket (sockFamilyCode f)
@@ -360,7 +376,7 @@
   fmap Socket $ newMVar $ Fd fd
 
 getFdOpt ∷ ∀ o . (SockOpt o, SockOptReadable o ~ 'True)
-         ⇒ Fd → o → IO (SockOptValue o)
+         ⇒ Fd → Proxy o → IO (SockOptValue o)
 getFdOpt fd o =
   alloca $ \p →
     with (fromIntegral $ sizeOf (undefined ∷ SockOptRaw o)) $ \pSize → do
@@ -370,12 +386,12 @@
 
 -- | Get socket option value. See /getsockopt(2)/.
 getSockOpt ∷ (SockOpt o, SockOptReadable o ~ 'True, MonadBase IO μ)
-           ⇒ Socket f → o → μ (SockOptValue o)
+           ⇒ Socket f → Proxy o → μ (SockOptValue o)
 getSockOpt s o = withSocketFd s $ \fd → getFdOpt fd o
 
 -- | Set socket option value. See /setsockopt(2)/.
 setSockOpt ∷ (SockOpt o, SockOptWritable o ~ 'True, MonadBase IO μ)
-           ⇒ Socket f → o → SockOptValue o → μ ()
+           ⇒ Socket f → Proxy o → SockOptValue o → μ ()
 setSockOpt s o v = withSocketFd s $ \fd →
     with raw $ \p →
       throwErrnoIfMinus1_ "setSockOpt" $
@@ -387,7 +403,7 @@
 bind ∷ ∀ f μ . (SockFamily f, MonadBase IO μ)
      ⇒ Socket f → SockFamilyAddr f → μ ()
 bind s addr = withSocketFd s $ \fd →
-  withAddr (undefined ∷ f) addr $ \p size →
+  withAddr (Proxy ∷ Proxy f) addr $ \p size →
     throwErrnoIfMinus1_ "bind" $ c_bind fd p $ fromIntegral size
 
 -- | Connect socket to the specified address. This operation blocks.
@@ -395,7 +411,7 @@
 connect ∷ ∀ f μ . (SockFamily f, MonadBase IO μ)
         ⇒ Socket f → SockFamilyAddr f → μ ()
 connect s addr = withSocketFd s $ \fd →
-    withAddr (undefined ∷ f) addr $ \p size →
+    withAddr (Proxy ∷ Proxy f) addr $ \p size →
       doConnect fd p $ fromIntegral size
   where doConnect fd p size = do
           r ← c_connect fd p size
@@ -417,7 +433,7 @@
 tryConnect ∷ ∀ f μ . (SockFamily f, MonadBase IO μ)
            ⇒ Socket f → SockFamilyAddr f → μ Bool
 tryConnect s addr = withSocketFd s $ \fd →
-  withAddr (undefined ∷ f) addr $ \p size → do
+  withAddr (Proxy ∷ Proxy f) addr $ \p size → do
     r ← c_connect fd p $ fromIntegral size
     if r == -1
       then do
@@ -453,7 +469,7 @@
                 doAccept fd p pSize
               _ → throwErrno "accept"
           else do
-            addr ← peekAddrOfSize (undefined ∷ f) p pSize
+            addr ← peekAddrOfSize (Proxy ∷ Proxy f) p pSize
             let accFd = Fd cfd
 #ifndef HAVE_ACCEPT_WITH_FLAGS
             onException (setNonBlockingFD accFd True) (closeFd accFd)
@@ -478,7 +494,7 @@
             e | e == eAGAIN || e == eWOULDBLOCK → return Nothing
             _ → throwErrno "accept"
         else do
-          addr ← peekAddrOfSize (undefined ∷ f) p pSize
+          addr ← peekAddrOfSize (Proxy ∷ Proxy f) p pSize
           let accFd = Fd cfd
 #ifndef HAVE_ACCEPT_WITH_FLAGS
           onException (setNonBlockingFD accFd True) (closeFd accFd)
@@ -492,7 +508,7 @@
   allocaMaxAddr (Proxy ∷ Proxy (SockFamilyAddr f)) $ \p size →
     with size $ \pSize → do
       throwErrnoIfMinus1_ "getLocalAddr" $ c_getsockname fd p pSize
-      peekAddrOfSize (undefined ∷ f) p pSize
+      peekAddrOfSize (Proxy ∷ Proxy f) p pSize
 
 -- | Get the remote address. See /getpeername(2)/.
 getRemoteAddr ∷ ∀ f μ . (SockFamily f, MonadBase IO μ)
@@ -501,7 +517,7 @@
   allocaMaxAddr (Proxy ∷ Proxy (SockFamilyAddr f)) $ \p size →
     with size $ \pSize → do
       throwErrnoIfMinus1_ "getRemoteAddr" $ c_getpeername fd p pSize
-      peekAddrOfSize (undefined ∷ f) p pSize
+      peekAddrOfSize (Proxy ∷ Proxy f) p pSize
 
 -- | Check if socket has out-of-band data. See /sockatmark(3)/.
 hasOobData ∷ MonadBase IO μ ⇒ Socket f → μ Bool
@@ -516,7 +532,7 @@
 throwInval loc = throwCustomErrno loc eINVAL
 
 recvBufsFromFd ∷ ∀ f . SockFamily f
-               ⇒ f → Fd → [(Ptr Word8, Int)] → MsgFlags
+               ⇒ Proxy f → Fd → [(Ptr Word8, Int)] → MsgFlags
                → IO (Maybe (SockFamilyAddr f), Int, MsgFlags)
 recvBufsFromFd _ fd bufs' flags = do
   let (bufs, bufs'') = partition ((> 0) . snd) bufs'
@@ -565,7 +581,7 @@
               ⇒ Socket f → [(Ptr Word8, Int)] → MsgFlags
               → μ (Maybe (SockFamilyAddr f), Int, MsgFlags)
 recvBufsFrom' s bufs flags = withSocketFd s $ \fd →
-  recvBufsFromFd (undefined ∷ f) fd bufs flags
+  recvBufsFromFd (Proxy ∷ Proxy f) fd bufs flags
 
 -- | Receive a message from a connected socket, possibly utilizing multiple
 -- memory buffers. See /recvmsg(2)/.
@@ -617,12 +633,12 @@
              → μ (SockFamilyAddr f, Int, MsgFlags)
              -- ^ Received message source address, length, and flags
 recvBufsFrom s bufs flags = withSocketFd s $ \fd → do
-  (mAddr, n, flags') ← recvBufsFromFd (undefined ∷ f) fd bufs flags
+  (mAddr, n, flags') ← recvBufsFromFd (Proxy ∷ Proxy f) fd bufs flags
   let getpeername =
         allocaMaxAddr (Proxy ∷ Proxy (SockFamilyAddr f)) $ \p size →
           with size $ \pSize → do
             throwErrnoIfMinus1_ "recv" $ c_getpeername fd p pSize
-            peekAddrOfSize (undefined ∷ f) p pSize
+            peekAddrOfSize (Proxy ∷ Proxy f) p pSize
   (, n, flags') <$> maybe getpeername return mAddr
 
 -- | Receive a message from an unconnected socket. This operation blocks.
@@ -697,7 +713,7 @@
           doSend
     case mAddr of
       Just addr →
-        withAddr (undefined ∷ f) addr $ \pAddr addrLen → do
+        withAddr (Proxy ∷ Proxy f) addr $ \pAddr addrLen → do
           #{poke struct msghdr, msg_name}    pHdr pAddr
           #{poke struct msghdr, msg_namelen} pHdr addrLen
           cont
@@ -828,6 +844,18 @@
        → SockFamilyAddr f -- ^ Message destination address
        → μ Int            -- ^ The number of bytes sent
 sendTo s bs addr = sendTo' s bs noFlags addr
+
+-- | An alias for 'RecvSockOps'.
+pattern SHUT_RD ∷ SockOps
+pattern SHUT_RD = RecvSockOps
+
+-- | An alias for 'SendSockOps'.
+pattern SHUT_WR ∷ SockOps
+pattern SHUT_WR = SendSockOps
+
+-- | An alias for 'AllSockOps'.
+pattern SHUT_RDWR ∷ SockOps
+pattern SHUT_RDWR = AllSockOps
 
 -- | Shut down a part of a full-duplex connection. See /shutdown(2)/.
 shutdown ∷ MonadBase IO μ ⇒ Socket f → SockOps → μ ()
diff --git a/src/System/Posix/Socket/Inet.hsc b/src/System/Posix/Socket/Inet.hsc
--- a/src/System/Posix/Socket/Inet.hsc
+++ b/src/System/Posix/Socket/Inet.hsc
@@ -2,6 +2,8 @@
 {-# LANGUAGE CPP #-}
 {-# LANGUAGE ForeignFunctionInterface #-}
 {-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE EmptyDataDecls #-}
+{-# LANGUAGE PatternSynonyms #-}
 {-# LANGUAGE TypeFamilies #-}
 {-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE TypeSynonymInstances #-}
@@ -9,11 +11,14 @@
 
 -- | Internet address families.
 module System.Posix.Socket.Inet
-  ( AF_INET(..)
-  , AF_INET6(..)
+  ( AF_INET
+  , pattern AF_INET
+  , AF_INET6
+  , pattern AF_INET6
   ) where
 
 import Data.Typeable (Typeable)
+import Data.Proxy (Proxy(..))
 import Network.IP.Addr
 import Control.Applicative ((<$>), (<*>))
 import Foreign.C.Types (CSize)
@@ -25,8 +30,11 @@
 #include <netinet/ip6.h>
 
 -- | IPv4 socket family.
-data AF_INET = AF_INET deriving (Typeable, Eq, Show)
+data AF_INET deriving Typeable
 
+pattern AF_INET ∷ Proxy AF_INET
+pattern AF_INET = Proxy
+
 instance SockAddr Inet4Addr where
   sockAddrMaxSize _ = #{size struct sockaddr_in}
   sockAddrSize    _ = #{size struct sockaddr_in}
@@ -46,7 +54,10 @@
   sockFamilyCode _ = #const AF_INET
 
 -- | IPv6 socket family.
-data AF_INET6 = AF_INET6 deriving (Typeable, Eq, Show)
+data AF_INET6 deriving Typeable
+
+pattern AF_INET6 ∷ Proxy AF_INET6
+pattern AF_INET6 = Proxy
 
 instance SockAddr Inet6Addr where
   sockAddrMaxSize _ = #{size struct sockaddr_in6}
diff --git a/src/System/Posix/Socket/Local.hsc b/src/System/Posix/Socket/Local.hsc
--- a/src/System/Posix/Socket/Local.hsc
+++ b/src/System/Posix/Socket/Local.hsc
@@ -2,13 +2,16 @@
 {-# LANGUAGE CPP #-}
 {-# LANGUAGE ForeignFunctionInterface #-}
 {-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE EmptyDataDecls #-}
+{-# LANGUAGE PatternSynonyms #-}
 {-# LANGUAGE TypeFamilies #-}
 
 -- | Local address family.
 module System.Posix.Socket.Local
   ( LocalAddr(..)
   , aLocalAddr
-  , AF_LOCAL(..)
+  , AF_LOCAL
+  , pattern AF_LOCAL
   ) where
 
 import Data.Typeable (Typeable)
@@ -27,7 +30,10 @@
 #include <sys/un.h>
 
 -- | Local socket family.
-data AF_LOCAL = AF_LOCAL deriving (Typeable, Eq, Show)
+data AF_LOCAL deriving Typeable
+
+pattern AF_LOCAL ∷ Proxy AF_LOCAL
+pattern AF_LOCAL = Proxy
 
 -- | Local socket address.
 data LocalAddr = LocalAddr ByteString
