diff --git a/ip.cabal b/ip.cabal
--- a/ip.cabal
+++ b/ip.cabal
@@ -1,5 +1,5 @@
 name:                ip
-version:             0.8.5
+version:             0.8.6
 synopsis:            Library for IP and MAC addresses
 homepage:            https://github.com/andrewthad/haskell-ip#readme
 license:             BSD3
@@ -20,8 +20,8 @@
   typeclasses of its own. Neither does it prefix functions with the name
   of the type that they work on. Instead, functions of the same name are
   exported by several different modules, and it is expected that end users
-  disambiguate by imported these modules qualified. For example,
-  `Data.IPv4.Text` and `Data.IPv4.ByteString.Char8` have nearly identical
+  disambiguate by importing these modules qualified. For example,
+  `Data.IPv4.ByteString.Char8` and `Data.IPv4.Text` have nearly identical
   export lists.
   .
   The only module intended to be imported unqualified is `Net.Types`. The
@@ -32,6 +32,7 @@
   .
   * `yesod-ip`: Provides orphan instances needed to work with yesod and
     persistent. Also, provides a `yesod-form` helper.
+  .
   * `impure-containers`: Provides a trie that can be used for looking
     up which subnet an IP address belongs in.
 
diff --git a/src/Net/Internal.hs b/src/Net/Internal.hs
--- a/src/Net/Internal.hs
+++ b/src/Net/Internal.hs
@@ -236,11 +236,11 @@
 
 -- | Given the size of the mask, return the
 --   total number of ips in the subnet. This
---   only works for IPv4 addresses because 
---   an IPv6 subnet can have up to 2^128 
+--   only works for IPv4 addresses because
+--   an IPv6 subnet can have up to 2^128
 --   addresses.
 countAddrs :: Word8 -> Word64
-countAddrs w = 
+countAddrs w =
   let amountToShift = if w > 32
         then 0
         else 32 - fromIntegral w
@@ -285,20 +285,23 @@
 -- r1,r2,r3,r4,r5,r6 :: Word32
 -- r1 = fromOctets' 0 0 0 0
 
-macTextParser :: (Word16 -> Word16 -> Word32 -> Word32 -> Word32 -> Word32 -> a) -> AT.Parser a
-macTextParser f = f
+macTextParser :: Maybe Char -> (Word16 -> Word16 -> Word32 -> Word32 -> Word32 -> Word32 -> a) -> AT.Parser a
+macTextParser separator f = f
   <$> (AT.hexadecimal >>= limitSize)
-  <*  AT.char ':'
+  <*  parseSeparator
   <*> (AT.hexadecimal >>= limitSize)
-  <*  AT.char ':'
+  <*  parseSeparator
   <*> (AT.hexadecimal >>= limitSize)
-  <*  AT.char ':'
+  <*  parseSeparator
   <*> (AT.hexadecimal >>= limitSize)
-  <*  AT.char ':'
+  <*  parseSeparator
   <*> (AT.hexadecimal >>= limitSize)
-  <*  AT.char ':'
+  <*  parseSeparator
   <*> (AT.hexadecimal >>= limitSize)
   where
+  parseSeparator = case separator of
+    Just c -> AT.char c
+    Nothing -> return 'x' -- character is unused
   limitSize i =
     if i > 255
       then fail "All octets in a mac address must be between 00 and FF"
@@ -322,12 +325,13 @@
   <> TBuilder.hexadecimal (255 .&. b)
   where colon = TBuilder.singleton ':'
 
-macFromText :: (Word16 -> Word16 -> Word32 -> Word32 -> Word32 -> Word32 -> a) -> Text -> Maybe a
-macFromText f = rightToMaybe . macFromText' f
+macFromText :: Maybe Char -> (Word16 -> Word16 -> Word32 -> Word32 -> Word32 -> Word32 -> a) -> Text -> Maybe a
+macFromText separator f = rightToMaybe . macFromText' separator f
 {-# INLINE macFromText #-}
 
-macFromText' :: (Word16 -> Word16 -> Word32 -> Word32 -> Word32 -> Word32 -> a) -> Text -> Either String a
-macFromText' f = AT.parseOnly (macTextParser f <* AT.endOfInput)
+macFromText' :: Maybe Char -> (Word16 -> Word16 -> Word32 -> Word32 -> Word32 -> Word32 -> a) -> Text -> Either String a
+macFromText' separator f =
+  AT.parseOnly (macTextParser separator f <* AT.endOfInput)
 {-# INLINE macFromText' #-}
 
 twoDigits :: ByteString
diff --git a/src/Net/Mac/ByteString/Char8.hs b/src/Net/Mac/ByteString/Char8.hs
--- a/src/Net/Mac/ByteString/Char8.hs
+++ b/src/Net/Mac/ByteString/Char8.hs
@@ -3,47 +3,77 @@
   , decode
   , builder
   , parser
+  , parserWith
   ) where
 
-import Net.Types (Mac(..))
-import Net.Mac (fromOctetsNoCast)
+import Net.Types (Mac(..),MacEncoding(..),MacDecoding(..))
+import Net.Mac (fromOctets)
 import Data.ByteString (ByteString)
 import Data.Attoparsec.ByteString.Char8 (Parser)
 import Data.ByteString.Lazy.Builder (Builder)
 import Net.Internal (rightToMaybe)
 import Data.Text.Encoding (encodeUtf8, decodeUtf8')
+import Data.Word (Word8)
+import Data.Bits (unsafeShiftL)
 import Control.Monad
 import qualified Data.ByteString.Builder as Builder
+import qualified Data.Attoparsec.ByteString as ABW
 import qualified Data.Attoparsec.ByteString.Char8 as AB
 import qualified Net.Mac.Text as MacText
 
--- | This is a bad implementation that should be rewritten
+-- | This is a mediocre implementation that should
+--   be rewritten.
 encode :: Mac -> ByteString
 encode = encodeUtf8 . MacText.encode
 
--- | This is a bad implementation that should be rewritten
+-- | This is a mediocre implementation that should
+--   be rewritten.
 decode :: ByteString -> Maybe Mac
 decode = MacText.decode <=< rightToMaybe . decodeUtf8'
 
+-- | Make a bytestring builder from a 'Mac' address
+--   using a colon as the separator.
 builder :: Mac -> Builder
 builder = Builder.byteString . encode
 
+-- | Parser for a 'Mac' address using with a colon as the
+--   separator (i.e. @FA:43:B2:C0:0F:99@).
 parser :: Parser Mac
-parser = fromOctetsNoCast
-  <$> (AB.hexadecimal >>= limitSize)
-  <*  AB.char ':'
-  <*> (AB.hexadecimal >>= limitSize)
-  <*  AB.char ':'
-  <*> (AB.hexadecimal >>= limitSize)
-  <*  AB.char ':'
-  <*> (AB.hexadecimal >>= limitSize)
-  <*  AB.char ':'
-  <*> (AB.hexadecimal >>= limitSize)
-  <*  AB.char ':'
-  <*> (AB.hexadecimal >>= limitSize)
+parser = parserWith defDecoding
+
+-- | Parser for a 'Mac' address using to the provided
+--   settings.
+parserWith :: MacDecoding -> Parser Mac
+parserWith (MacDecoding separator) = fromOctets
+  <$> parseTwoHex
+  <*  parseSeparator
+  <*> parseTwoHex
+  <*  parseSeparator
+  <*> parseTwoHex
+  <*  parseSeparator
+  <*> parseTwoHex
+  <*  parseSeparator
+  <*> parseTwoHex
+  <*  parseSeparator
+  <*> parseTwoHex
   where
-  limitSize i =
-    if i > 255
-      then fail "All octets in a mac address must be between 00 and FF"
-      else return i
+  parseSeparator = case separator of
+    Just c -> ABW.word8 c
+    Nothing -> return 60 -- character is unused
+
+parseTwoHex :: Parser Word8
+parseTwoHex = do
+  a <- ABW.anyWord8 >>= parseWord8Hex
+  b <- ABW.anyWord8 >>= parseWord8Hex
+  return (unsafeShiftL a 1 + b)
+
+parseWord8Hex :: Word8 -> Parser Word8
+parseWord8Hex w
+  | w >= 48 && w <= 57  = return (w - 48)
+  | w >= 65 && w <= 70  = return (w - 55)
+  | w >= 97 && w <= 102 = return (w - 87)
+  | otherwise = fail "invalid hexadecimal character"
+
+defDecoding :: MacDecoding
+defDecoding = MacDecoding (Just 58)
 
diff --git a/src/Net/Mac/Text.hs b/src/Net/Mac/Text.hs
--- a/src/Net/Mac/Text.hs
+++ b/src/Net/Mac/Text.hs
@@ -1,15 +1,20 @@
 module Net.Mac.Text
   ( encode
+  , encodeWith
   , decode
+  , decodeWith
   , decodeEither
+  , decodeEitherWith
   , builder
   , parser
-  , encodeWith
+  , parserWith
   ) where
 
-import Net.Types (Mac(..),MacEncoding(..))
+import Net.Types (Mac(..),MacEncoding(..),MacDecoding(..))
 import Net.Mac (fromOctetsNoCast)
 import Data.Text (Text)
+import Data.Word (Word8)
+import Data.Char (chr)
 import qualified Net.Internal as Internal
 import qualified Data.Attoparsec.Text as AT
 import qualified Data.Text.Lazy.Builder as TBuilder
@@ -17,19 +22,38 @@
 encode :: Mac -> Text
 encode (Mac a b) = Internal.macToTextPreAllocated 58 False a b
 
+decodeEitherWith :: MacDecoding -> Text -> Either String Mac
+decodeEitherWith (MacDecoding separator) =
+  Internal.macFromText' (fmap w8ToChar separator) fromOctetsNoCast
+
 decodeEither :: Text -> Either String Mac
-decodeEither = Internal.macFromText' fromOctetsNoCast
+decodeEither = decodeEitherWith defDecoding
 
 decode :: Text -> Maybe Mac
-decode = Internal.rightToMaybe . decodeEither
+decode = decodeWith defDecoding
 
+decodeWith :: MacDecoding -> Text -> Maybe Mac
+decodeWith d = Internal.rightToMaybe . decodeEitherWith d
+
+-- decodeWith ::
+
 builder :: Mac -> TBuilder.Builder
 builder (Mac a b) = Internal.macToTextBuilder a b
 
 parser :: AT.Parser Mac
-parser = Internal.macTextParser fromOctetsNoCast
+parser = parserWith defDecoding
 
+parserWith :: MacDecoding -> AT.Parser Mac
+parserWith (MacDecoding separator) =
+  Internal.macTextParser (fmap w8ToChar separator) fromOctetsNoCast
+
 encodeWith :: MacEncoding -> Mac -> Text
 encodeWith (MacEncoding separator isUpperCase) (Mac a b) =
   Internal.macToTextPreAllocated separator isUpperCase a b
+
+defDecoding :: MacDecoding
+defDecoding = MacDecoding (Just 58)
+
+w8ToChar :: Word8 -> Char
+w8ToChar = chr . fromIntegral
 
diff --git a/src/Net/Types.hs b/src/Net/Types.hs
--- a/src/Net/Types.hs
+++ b/src/Net/Types.hs
@@ -12,6 +12,7 @@
   , IPv4Range(..)
   , Mac(..)
   , MacEncoding(..)
+  , MacDecoding(..)
   ) where
 
 import qualified Net.Internal         as Internal
@@ -61,6 +62,10 @@
   , macEncodingUpperCase :: {-# UNPACK #-} !Bool
   } deriving (Eq,Ord,Show,Read,Generic)
 
+newtype MacDecoding = MacDecoding
+  { macDecodingSeparator :: Maybe Word8
+  }
+
 instance Hashable Mac
 
 instance ToJSON Mac where
@@ -68,7 +73,7 @@
 
 instance FromJSON Mac where
   parseJSON = Internal.attoparsecParseJSON
-    (Internal.macTextParser macFromOctets' <* AT.endOfInput)
+    (Internal.macTextParser (Just ':') macFromOctets' <* AT.endOfInput)
 
 macFromOctets' :: Word16 -> Word16 -> Word32 -> Word32 -> Word32 -> Word32 -> Mac
 macFromOctets' a b c d e f = Mac
