diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+0.6.2.0 Lars Petersen <info@lars-petersen.net> 2016-08-15
+
+ * Added functions for constructing internet addresses without the need for IO
+   (`inetAddressToTuple`, `inetAddressFromTuple`, `inet6AddressToTuple`,
+    `inet6AddressFromTuple`) as proposed by Erik Rantapaa.
+
 0.6.1.0 Lars Petersen <info@lars-petersen.net> 2016-08-11
 
  * A potential race condition has been fixed (issue #18): `c_get_last_error`
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
--- a/CONTRIBUTORS.txt
+++ b/CONTRIBUTORS.txt
@@ -1,3 +1,4 @@
+Erik Rantapaa (issue #19)
 Mathieu Boespflug (issue #15)
 Ben Gamari (issue #10)
 Niklas Hambüchen (issue #9)
diff --git a/socket.cabal b/socket.cabal
--- a/socket.cabal
+++ b/socket.cabal
@@ -1,8 +1,8 @@
 name:                socket
-version:             0.6.1.0
+version:             0.6.2.0
 synopsis:            An extensible socket library.
 description:
-  This library is a minimal cross platform interface for
+  This library is a minimal cross-platform interface for
   BSD style networking.
 
 license:             MIT
@@ -72,6 +72,8 @@
       base >= 4.7 && < 5
     , tasty >= 0.11
     , tasty-hunit
+    , tasty-quickcheck
+    , QuickCheck >= 2.9 && <3
     , async
     , bytestring
     , socket
@@ -91,6 +93,8 @@
       base >= 4.7 && < 5
     , tasty >= 0.11
     , tasty-hunit
+    , tasty-quickcheck
+    , QuickCheck >= 2.9 && <3
     , async
     , bytestring
     , socket
diff --git a/src/System/Socket/Family/Inet.hsc b/src/System/Socket/Family/Inet.hsc
--- a/src/System/Socket/Family/Inet.hsc
+++ b/src/System/Socket/Family/Inet.hsc
@@ -17,7 +17,12 @@
   , InetPort
     -- ** SocketAddress Inet
   , SocketAddress (SocketAddressInet, inetAddress, inetPort)
-  -- * Special Addresses
+  -- * Custom addresses
+  -- ** inetAddressFromTuple
+  , inetAddressFromTuple
+  -- ** inetAddressToTuple
+  , inetAddressToTuple
+  -- * Special addresses
   -- ** inetAllHostsGroup
   , inetAllHostsGroup
   -- ** inetAny
@@ -66,8 +71,11 @@
 
 -- | To avoid errors with endianess it was decided to keep this type abstract.
 --
---   Hint: Use the `Foreign.Storable.Storable` instance if you really need to access. It exposes it
---   exactly as found within an IP packet (big endian if you insist
+--   Use `inetAddressFromTuple` and `inetAddressToTuple` for constructing and
+--   deconstructing custom addresses.
+--
+--   Hint: Use the `Foreign.Storable.Storable` instance.
+--   It exposes it exactly as found within an IP packet (big endian if you insist
 --   on interpreting it as a number).
 --
 --   Another hint: Use `System.Socket.getAddressInfo` for parsing and suppress
@@ -81,6 +89,25 @@
 
 newtype InetPort = InetPort Word16
       deriving (Eq, Ord, Show, Num)
+
+-- | Constructs a custom `InetAddress`.
+--
+--   > inetAddressFromTuple (127,0,0,1) == inetLoopback
+inetAddressFromTuple :: (Word8, Word8, Word8, Word8) -> InetAddress
+inetAddressFromTuple (w0, w1, w2, w3)
+  = InetAddress $ foldl1' (\x y->x*256+y) [f w0, f w1, f w2, f w3]
+  where
+    f = fromIntegral
+
+-- | Deconstructs an `InetAddress`.
+inetAddressToTuple :: InetAddress -> (Word8, Word8, Word8, Word8)
+inetAddressToTuple (InetAddress a)
+  = (w0, w1, w2, w3)
+  where
+    w0 = fromIntegral $ rem (quot a $ 256*256*256) 256
+    w1 = fromIntegral $ rem (quot a $     256*256) 256
+    w2 = fromIntegral $ rem (quot a $         256) 256
+    w3 = fromIntegral $ rem       a $              256
 
 -- | @0.0.0.0@
 inetAny             :: InetAddress
diff --git a/src/System/Socket/Family/Inet6.hsc b/src/System/Socket/Family/Inet6.hsc
--- a/src/System/Socket/Family/Inet6.hsc
+++ b/src/System/Socket/Family/Inet6.hsc
@@ -22,16 +22,22 @@
     -- ** SocketAddress Inet6
   , SocketAddress (SocketAddressInet6, inet6Address, inet6Port,
                                        inet6FlowInfo, inet6ScopeId)
-  -- * Special Addresses
-  -- ** inet6Any
+    -- * Custom addresses
+    -- ** inet6AddressFromTuple
+  , inet6AddressFromTuple
+    -- ** inet6AddressToTuple
+  , inet6AddressToTuple
+    -- * Special addresses
+    -- ** inet6Any
   , inet6Any
-  -- ** inet6Loopback
+    -- ** inet6Loopback
   , inet6Loopback
-  -- * Socket Options
-  -- ** V6Only
+    -- * Socket options
+    -- ** V6Only
   , V6Only (..)
   ) where
 
+import Data.Bits ((.|.))
 import Data.Word
 import Control.Applicative as A
 
@@ -67,7 +73,10 @@
 
 -- | To avoid errors with endianess it was decided to keep this type abstract.
 --
---   Hint: Use the `Foreign.Storable.Storable` instance if you really need to access. It exposes it
+--   Use `inet6AddressFromTuple` and `inet6AddressToTuple` for constructing and
+--   deconstructing custom addresses.
+--
+--   Hint: Use the `Foreign.Storable.Storable` instance. It exposes it
 --   exactly as found within an IP packet (big endian if you insist
 --   on interpreting it as a number).
 --
@@ -90,6 +99,33 @@
 
 newtype Inet6ScopeId  = Inet6ScopeId Word32
       deriving (Eq, Ord, Show, Num)
+
+-- | Deconstructs an `Inet6Address`.
+inet6AddressToTuple :: Inet6Address -> (Word16,Word16,Word16,Word16,Word16,Word16,Word16,Word16)
+inet6AddressToTuple (Inet6Address hb lb) =
+  (w0 hb, w1 hb, w2 hb, w3 hb, w0 lb, w1 lb, w2 lb, w3 lb)
+  where
+    w0, w1, w2, w3 :: Word64 -> Word16
+    w0 x = fromIntegral $ rem (quot x $ 65536 * 65536 * 65536) 65536
+    w1 x = fromIntegral $ rem (quot x $         65536 * 65536) 65536
+    w2 x = fromIntegral $ rem (quot x $                 65536) 65536
+    w3 x = fromIntegral $ rem       x                          65536
+
+-- | Constructs a custom `Inet6Address`.
+--
+--   > inet6AddressFromTuple (0,0,0,0,0,0,0,1) == inet6Loopback
+inet6AddressFromTuple :: (Word16,Word16,Word16,Word16,Word16,Word16,Word16,Word16) -> Inet6Address
+inet6AddressFromTuple (w0, w1, w2, w3, w4, w5, w6, w7) =
+  Inet6Address hb lb
+  where
+    hb =  fromIntegral w0 * 65536 * 65536 * 65536
+      .|. fromIntegral w1 *         65536 * 65536
+      .|. fromIntegral w2 *                 65536
+      .|. fromIntegral w3
+    lb =  fromIntegral w4 * 65536 * 65536 * 65536
+      .|. fromIntegral w5 *         65536 * 65536
+      .|. fromIntegral w6 *                 65536
+      .|. fromIntegral w7
 
 -- | @::@
 inet6Any      :: Inet6Address
diff --git a/test/test.hs b/test/test.hs
--- a/test/test.hs
+++ b/test/test.hs
@@ -15,6 +15,7 @@
 
 import Test.Tasty
 import Test.Tasty.HUnit
+import Test.Tasty.QuickCheck as QC
 
 import System.Socket
 import System.Socket.Family.Inet
@@ -25,14 +26,21 @@
 import System.Socket.Protocol.UDP
 
 main :: IO ()
-main  = defaultMain $ testGroup "System.Socket"
-  [ group00
-  , group01
-  , group02
-  , group03
-  , group07
-  , group80
-  , group99 ]
+main  = defaultMain $ testGroup "socket"
+  [ testGroup "System.Socket"
+    [ group00
+    , group01
+    , group02
+    , group03
+    , group07
+    , group80
+    , group99
+    ]
+  , testGroup "System.Socket.Inet" [
+      group200
+    , group201
+    ]
+  ]
 
 port :: InetPort
 port  = 39000
@@ -358,4 +366,24 @@
             _ | e == eaiNoName -> return ()
             _                  -> assertFailure "expected eaiNoName"
 
+  ]
+
+group200 :: TestTree
+group200 = testGroup "System.Socket.Family.Inet" [
+
+    testCase "inetAddressFromTuple (127,0,0,1) == inetLoopback" $
+      assertEqual "" ( inetAddressFromTuple (127,0,0,1) ) inetLoopback
+
+  , QC.testProperty  "inetAddressToTuple (inetAddressFromTuple x) == x" $ \x->
+      inetAddressToTuple (inetAddressFromTuple x) === x
+  ]
+
+group201 :: TestTree
+group201 = testGroup "System.Socket.Family.Inet6" [
+
+    testCase "inet6AddressFromTuple (0,0,0,0,0,0,0,1) == inet6Loopback" $
+      assertEqual "" ( inet6AddressFromTuple (0,0,0,0,0,0,0,1) ) inet6Loopback
+
+  , QC.testProperty  "inet6AddressToTuple (inet6AddressFromTuple x) == x" $ \x->
+      inet6AddressToTuple (inet6AddressFromTuple x) === x
   ]
