diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,11 @@
+# Version 0.3
+
+* BREAKING CHANGE: `mkDiTextStderr` and `mkDiStringHandle` return a `Di String
+  [String] String` now.
+
+* Made compatible with GHC 8.4.
+
+
 # Version 0.2
 
 * BREAKING CHANGE: `Di` now takes a new type argument `level`.
diff --git a/LICENSE.txt b/LICENSE.txt
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -1,4 +1,4 @@
-Copyright (c) 2017, Renzo Carbonara
+Copyright (c) 2017-2018, Renzo Carbonara
 
 All rights reserved.
 
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -8,5 +8,5 @@
 file to learn about the legal terms and conditions for this library.
 
 Find documentation for this library in the top-level
-[`Di`](https://github.com/k0001/di/blob/master/di/src/Di.hs) module.
+[`Di`](https://github.com/k0001/di/blob/master/src/Di.hs) module.
 
diff --git a/di.cabal b/di.cabal
--- a/di.cabal
+++ b/di.cabal
@@ -1,8 +1,8 @@
 name: di
-version: 0.2
+version: 0.3
 author: Renzo Carbonara
 maintainer: renλren.zone
-copyright: Renzo Carbonara 2017
+copyright: Renzo Carbonara 2017-2018
 license: BSD3
 license-file: LICENSE.txt
 extra-source-files: README.md CHANGELOG.md
diff --git a/src/Di/Backend.hs b/src/Di/Backend.hs
--- a/src/Di/Backend.hs
+++ b/src/Di/Backend.hs
@@ -6,56 +6,77 @@
  ) where
 
 import Control.Monad.IO.Class (MonadIO, liftIO)
-import Data.Monoid (mconcat, mappend, (<>))
+import Data.Monoid (mconcat, mappend)
 import Data.String (IsString(fromString))
 import qualified Data.Time as Time
 import Prelude hiding (log, filter)
 import qualified System.IO as IO
+import Data.Semigroup (Semigroup(..))
 
 import Di.Core (Di, mkDi, contrapath)
 
 --------------------------------------------------------------------------------
 
--- | Strings separated by a forward slash. Doesn't contain white space.
+-- | Strings separated by a forward slash.
 --
+-- The string doesn't contain any of @[\'/'\, \' \', \'\\n\', \'\\r\']@.
+--
 -- Use 'fromString' (GHC's @OverloadedStrings@ extension) to construct a
 -- 'StringPath'.
-newtype StringPath = StringPath { unStringPath :: String }
+newtype StringPath = UnsafeStringPath { unStringPath :: String }
   deriving (Eq, Ord, Show)
 
 instance IsString StringPath where
   fromString = stringPathSingleton
   {-# INLINE fromString #-}
 
+-- | Ocurrences of one of @[\'/'\, \' \', \'\\n\', \'\\r\']@ in the given
+-- 'String' will be replaced by @\'_\'@.
 stringPathSingleton :: String -> StringPath
-stringPathSingleton = \s -> StringPath (map f s)
+stringPathSingleton = \s -> UnsafeStringPath (map f s)
   where f :: Char -> Char
-        f = \case '/'  -> '.'
+        f = \case '/'  -> '_'
                   ' '  -> '_'
                   '\n' -> '_'
                   '\r' -> '_'
                   c    -> c
 
-instance Monoid StringPath where
-  mempty = StringPath ""
-  mappend (StringPath "") b = b
-  mappend a (StringPath "") = a
-  mappend (StringPath a) (StringPath b) = StringPath (a <> "/" <> b)
+instance Semigroup StringPath where
+  UnsafeStringPath "" <> b = b
+  a <> UnsafeStringPath "" = a
+  UnsafeStringPath a <> UnsafeStringPath b = UnsafeStringPath (a <> "/" <> b)
 
+instance Monoid StringPath where
+  mempty = UnsafeStringPath ""
+  {-# INLINE mempty #-}
+  mappend = (<>)
+  {-# INLINE mappend #-}
 -- | 'String's are written to 'IO.Handle' using the 'IO.Handle''s locale
 -- encoding.
+--
+-- The @level@ and @message@ are plain 'String's.
+--
+-- The @path@ is a list of 'String's which will be separated by @\'/\'@ when
+-- rendered. Ocurrences of one of @[\'/'\, \' \', \'\\n\', \'\\r\']@ in those
+-- 'String's will be replaced by @\'_\'@.
+--
+-- @
+-- > 'log' ('push' [\"cuatro\"] ('push' [\"uno dos\", \"tres\"] di)) \"WARNING\" \"Hello!\"
+-- WARNING 2018-05-03T09:15:54.819379740000Z uno_dos\/tres\/cuatro: Hello!
+-- @
 mkDiStringHandle
   :: (MonadIO m)
   => IO.Handle
-  -> m (Di String String String)
+  -> m (Di String [String] String)
 mkDiStringHandle h = liftIO $ do
     IO.hSetBuffering h IO.LineBuffering
-    fmap (contrapath stringPathSingleton) $ mkDi $ \ts l p m -> do
-       IO.hPutStrLn h $ mconcat
-          [ l, " ", renderIso8601 ts
-          , if p == mempty then "" else (" " <> unStringPath p)
-          , ": ", noBreaks m ]
-       IO.hFlush h
+    fmap (contrapath (mconcat . map stringPathSingleton)) $ do
+       mkDi $ \ts l p m -> do
+          IO.hPutStrLn h $ mconcat
+             [ l, " ", renderIso8601 ts
+             , if p == mempty then "" else (" " <> unStringPath p)
+             , ": ", noBreaks m ]
+          IO.hFlush h
   where
     noBreaks :: String -> String
     noBreaks = concatMap $ \case
@@ -63,8 +84,11 @@
       '\r' -> "\\r"
       c -> [c]
 
--- | 'String' is written to 'IO.stderr' using the system's locale encoding.
-mkDiStringStderr :: MonadIO m => m (Di String String String)
+-- |
+-- @
+-- 'mkDiStringStderr'  ==  'mkDiStringHandle' 'IO.stderr'
+-- @
+mkDiStringStderr :: MonadIO m => m (Di String [String] String)
 mkDiStringStderr = mkDiStringHandle IO.stderr
 
 --------------------------------------------------------------------------------
diff --git a/src/Di/Core.hs b/src/Di/Core.hs
--- a/src/Di/Core.hs
+++ b/src/Di/Core.hs
@@ -47,7 +47,7 @@
 -- type safety, as well as the composability of the @level@, @path@ and @msg@
 -- values. In particular, all of @level@, @path@ and @msg@ are contravariant
 -- values, which in practice means including a precise 'Di' into a more general
--- 'Di' is always possible (see the 'contralevel@, 'contrapath' and 'contramsg'
+-- 'Di' is always possible (see the 'contralevel', 'contrapath' and 'contramsg'
 -- functions).
 --
 -- Messages of undesired importance levels can be muted by using 'filter'.
@@ -62,7 +62,7 @@
 --
 -- 'Di' is pronounced as \"dee" (not \"die" nor \"dye" nor \"day"). \"Di" is
 -- the spanish word for an imperative form of the verb \"decir", which in
--- english means "to say".
+-- english means "to say", which clearly has something to do with logging.
 data Di level path msg = Di
   { _diLog :: Time.UTCTime -> level -> path -> msg -> IO ()
     -- ^ Low level logging function.
@@ -124,7 +124,7 @@
 
 -- | Block until all messages being logged have finished processing.
 --
--- Mabually calling 'flush' is not usually necessary, but, if at some point you
+-- Manually calling 'flush' is not usually necessary, but, if at some point you
 -- want to ensure that all messages logged until then have properly rendered to
 -- the underlying backend, then 'flush' will block until that happens.
 flush :: MonadIO m => Di level path msg -> m ()
