diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,18 @@
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
diff --git a/Setup.lhs b/Setup.lhs
new file mode 100644
--- /dev/null
+++ b/Setup.lhs
@@ -0,0 +1,4 @@
+import Distribution.Simple
+
+main = defaultMain
+
diff --git a/src/Data/Strings.hs b/src/Data/Strings.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Strings.hs
@@ -0,0 +1,633 @@
+{-# LANGUAGE TypeSynonymInstances,
+             FlexibleInstances,
+             Trustworthy #-}
+
+-- | Functions for working with strings, including 'Text', 'ByteString', etc.
+--
+-- This module aims at offering a consistent interface across all the available
+-- string types. It currently offers instances for the ordinary Haskell 'String'
+-- type, 'Text', lazy 'LazyT.Text', 'ByteString', and lazy 'LazyB.ByteString'.
+--
+-- If especially provides functions for some types, which are otherwise not
+-- available nativly (such as 'breakOnSubstring' is not available for the lazy
+-- 'LazyT.Text' type, is offered by 'sBreak' and 'strBreak').
+module Data.Strings (
+
+    text,
+    lazyText,
+    bytes,
+    lazyBytes,
+
+    Str (..),
+    Strings (..)
+
+) where
+
+
+import Data.Int
+import Data.Word
+import Data.String hiding (fromString)
+import qualified Data.String as S
+import qualified Data.Char as C
+
+import qualified Data.ByteString.Char8 as B8
+import qualified Data.ByteString as B
+import Data.ByteString (ByteString)
+import qualified Data.ByteString.Lazy.Char8 as LazyB8
+import qualified Data.ByteString.Lazy as LazyB
+import qualified Data.Text as T
+import Data.Text (Text)
+import qualified Data.Text.Lazy as LazyT
+import qualified Data.List as L
+import qualified Data.Text.Encoding as Enc
+import qualified Data.Text.Lazy.Encoding as LazyEnc
+
+-- | Returns @True@ exactly for space (0x20), and horizontal tab (0x09).
+isSpace x
+    | x == ' '  = True
+    | x == '\t' = True
+    | otherwise = False
+
+-- | Returns @True@ exactly for space (0x20), horizontal tab (0x09), carriage return (0x0D), and line feed (0x0A).
+isWhiteSpace x
+    | x == ' '  = True
+    | x == '\t' = True
+    | x == '\r' = True
+    | x == '\n' = True
+    | otherwise = False
+
+
+-- | Create a 'Text' object form an ordinary Haskell 'String'.
+text :: String -> Text
+text = S.fromString
+
+-- | Create a lazy 'LazyT.Text' object from an ordinary Haskell 'String'.
+lazyText :: String -> LazyT.Text
+lazyText = S.fromString
+
+-- | Create a 'ByteString' object form an ordinary Haskell 'String'.
+-- This function will encode a String using the UTF-8 character encoding.
+bytes :: String -> ByteString
+bytes = sFromUnicodeString
+
+-- | Create a lazy 'LazyB.ByteString' object from an ordinary Haskell 'String'.
+-- This function will encode a String using the UTF-8 character encoding.
+lazyBytes :: String -> LazyB.ByteString
+lazyBytes = sFromUnicodeString
+
+-- | The 'Str' class provides functions for working with arbitrary Strings.
+-- It is basically the same interface as provided by the 'Strings' class.
+-- However, every input string is a Haskell String here, thus easing the
+-- usage of different string types with native Haskell String literals.
+--
+-- For example @strAppend "suffix"@ works with any string type for which
+-- an instance of @Str@ is defined. In order to maximize the ease of use
+-- of this library, the functions are prefixed with @str@.
+--
+-- The complexity and efficiency of these functions depends on the underlying
+-- string type being used.
+class (Eq a, Strings a) => Str a where
+
+    -- | Check whether the given string is empty or not. 'null' generalised.
+    strNull :: a -> Bool
+
+    -- | 'length' generalised.
+    strLen :: a -> Int
+
+    -- | 'head' generalised. This function is undefined if 'strNull' would return @True@.
+    strHead :: a -> Char
+
+    -- | 'last' generalised. This function is undefined if 'strNull' would return @True@.
+    strLast :: a -> Char
+
+    -- | 'init' generalised. This function is undefined if 'strNull' would return @True@.
+    strInit :: a -> a
+
+    -- | 'tail' generalised. This function is undefined if 'strNull' would return @True@.
+    strTail :: a -> a
+
+    -- | 'take' generalised.
+    strTake :: Int -> a -> a
+
+    -- | 'drop' generalised.
+    strDrop :: Int -> a -> a
+    
+    -- | Replace a substring with another string.
+    strReplace :: String -- ^ Needle.
+               -> String -- ^ Replacement.
+               -> a      -- ^ Haystack.
+               -> a      -- ^ Result: @Haystack@ with @Needle@ replaced by @Replacement@.
+
+    -- | Breaks the string on the first occurence of the given substring.
+    --
+    -- > strBreak "xx" "1x2xx3xx4" = ("1x2", "xx3xx4")
+    strBreak :: String -> a -> (a, a)
+
+    -- | Like 'strBreak', but the string to break on is excluded from the result.
+    --
+    -- > strSplit "xx" "1x2xx3xx4" = ("1x2", "3xx4")
+    strSplit :: String -> a -> (a, a)
+
+    -- | Split a string into multiple fragments, separated by the given substring.
+    --
+    -- > strSplitAll "xx" "1x2xx3xx4" = ["1x2", "3", "4"]
+    strSplitAll :: String -> a -> [a]
+
+    -- | @concat@ generalised.
+    strConcat :: [a] -> a
+
+    -- | Glue together multiple strings by a given Haskell 'String'.
+    --
+    -- > strJoin x = concat . intersperse x
+    strJoin :: String -> [a] -> a
+
+    -- | Appends the given Haskell 'String' to the string. @++@ generalised.
+    --
+    -- > strAppend " world" "hello" = "hello world"
+    strAppend :: String -- The String to append.
+              -> a      -- The string to append to.
+              -> a      -- The concatenation of the two strings.
+
+    -- | Strips white space characters off both ends of the string.
+    strTrim :: a -> a
+
+    -- | Appends the given character n times to the left, such that
+    -- the resulting string has the given length.
+    --
+    -- > strPadLeft '0' 8 "4711" == "00004711"
+    strPadLeft  :: Char -> Int -> a -> a
+
+    -- | Appends the given character n times to the right, such that
+    -- the resulting string has the given length.
+    --
+    -- > strPadRight '0' 8 "4711" == "47110000"
+    strPadRight :: Char -> Int -> a -> a
+
+    -- | Appends the given character n times to both sides, such that
+    -- the resulting string has the given length.
+    --
+    -- > strPadBoth '0' 8 "4711" == "00471100"
+    strPadBoth  :: Char -> Int -> a -> a
+
+    -- | Reverse the string.
+    strReverse :: a -> a
+
+    -- | Check if the given Haskell String equals the string.
+    strEq :: String -- ^ The Haskell String.
+          -> a      -- ^ The string.
+          -> Bool   -- @True@ iff @fromString haskellString == string@
+
+    -- | Check if the string starts with the given Haskell String.
+    strStartsWith :: a -> String -> Bool
+
+    -- | Check if the string ends with the given Haskell String.
+    strEndsWith :: a -> String -> Bool
+
+    -- | Create a string from a Haskell String.
+    fromString :: String -> a
+
+    -- | Create a string from a Haskell String. If the string does not support unicode,
+    -- the Haskell String is encoded using UTF-8.
+    fromUnicodeString :: String -> a
+
+    -- | Convert the string into a Haskell String.
+    toString :: a -> String
+
+    -- | Convert the string into a list of bytes.
+    toWord8 :: a -> [Word8]
+
+    strNull = sNull
+    strLen = sLen
+
+    strHead = sHead
+    strLast = sLast
+
+    strInit = sInit
+    strTail = sTail
+    strTake = sTake
+    strDrop = sDrop
+
+    strReplace n r = sReplace (fromString n) (fromString r)
+
+    strBreak n = sBreak (fromString n)
+    strSplit d s = (a, b)
+      where (a, b, _) = sSplit (fromString d) s
+    strSplitAll n = sSplitAll (fromString n)
+
+    strConcat = sConcat
+    strJoin d = let d' = fromUnicodeString d in strConcat . L.intersperse d'
+    strAppend x xs = sConcat [xs, fromString x]
+    strTrim = sTrim
+
+    strPadLeft  = sPadLeft
+    strPadRight = sPadRight
+    strPadBoth  = sPadBoth
+
+    strReverse = sReverse
+
+    strStartsWith s pref = sStartsWith s (fromString pref)
+    strEndsWith   s suff = sEndsWith   s (fromString suff)
+
+    strEq s1 s2 = fromUnicodeString s1 == s2
+
+    fromString = sFromString
+    fromUnicodeString = sFromUnicodeString
+
+    toString = sToString
+    toWord8 = sToWord8
+
+{-
+instance IsString Data.ByteString.ByteString where
+    fromString = Enc.encodeUtf8 . Data.Text.pack
+
+instance IsString Data.ByteString.Lazy.ByteString where
+    fromString = LazyEnc.encodeUtf8 . Data.Text.Lazy.pack
+-}
+
+instance Str Text where
+instance Str LazyT.Text where
+instance Str ByteString where
+instance Str LazyB.ByteString where
+instance Str String where
+
+-- | The goal of this class is to offer the same interface for various
+-- types of strings ('ByteString', 'Text', Haskell 'String', etc.).
+-- If a certain type offers a native implementation for a given function,
+-- 'Strings' uses it. If not, a default implementation is given.
+--
+-- All of these functions are prefixed with @s@ to avoid nameclashes
+-- with existing functions from the prelude.
+--
+-- The complexity and efficiency of these functions depends on the underlying
+-- string type being used.
+class Strings a where
+
+    -- | Check whether the given string is empty or not. @null@ generalised.
+    sNull :: a -> Bool
+
+    -- | The empty string.
+    sEmpty :: a
+
+    -- | 'length' generalised.
+    sLen :: a -> Int
+
+    -- | 'head' generalised. This function is undefined if 'strNull' would return @True@.
+    sHead :: a -> Char
+
+    -- | 'last' generalised. This function is undefined if 'strNull' would return @True@.
+    sLast :: a -> Char
+
+    -- | 'init' generalised. This function is undefined if 'strNull' would return @True@.
+    sInit :: a -> a
+
+    -- | 'tail' generalised. This function is undefined if 'strNull' would return @True@.
+    sTail :: a -> a
+
+    -- | 'take' generalised.
+    sTake :: Int -> a -> a
+
+    -- | 'drop' generalised.
+    sDrop :: Int -> a -> a
+
+    -- | 'takeWhile' generalised.
+    sTakeWhile :: (Char -> Bool) -> a -> a
+
+    -- | 'dropWhile' generalised.
+    sDropWhile :: (Char -> Bool) -> a -> a
+
+    -- | Replace a substring with another string.
+    sReplace :: a -> a -> a -> a
+
+    -- | Breaks the string on the first occurence of the given substring.
+    --
+    -- > strBreak "xx" "1x2xx3xx4" = ("1x2", "xx3xx4")
+    sBreak :: a -> a -> (a, a)
+
+    -- | Like 'sBreak', but the string to break on is excluded from the result.
+    --
+    -- > strSplit "xx" "1x2xx3xx4" = ("1x2", "3xx4")
+    sSplit :: a -> a -> (a, a, Bool)
+
+    -- | Split a string into multiple fragments, separated by the given substring.
+    --
+    -- > strSplitAll "xx" "1x2xx3xx4" = ["1x2", "3", "4"]
+    sSplitAll :: a -> a -> [a]
+
+    -- | Check if the string starts with the given string.
+    sStartsWith :: a -> a -> Bool
+
+    -- | Check if the string ends with the given string.
+    sEndsWith   :: a -> a -> Bool
+
+    -- | Concatenate all the strings in the list to a single string.
+    sConcat :: [a] -> a
+
+    -- | Strips white space characters off both ends of the string.
+    sTrim :: a -> a
+
+    -- | Appends the given character n times to the left, such that
+    -- the resulting string has the given length.
+    --
+    -- > strPadLeft '0' 8 "4711" == "00004711"
+    sPadLeft  :: Char -> Int -> a -> a
+
+    -- | Appends the given character n times to the right, such that
+    -- the resulting string has the given length.
+    --
+    -- > strPadRight '0' 8 "4711" == "47110000"
+    sPadRight :: Char -> Int -> a -> a
+
+    -- | Appends the given character n times to both sides, such that
+    -- the resulting string has the given length.
+    --
+    -- > strPadBoth '0' 8 "4711" == "00471100"
+    sPadBoth  :: Char -> Int -> a -> a
+
+    -- 'reverse' the string.
+    sReverse :: a -> a
+
+    -- | Create a string from a Haskell String.
+    sFromString :: String -> a
+
+    -- | Create a string from a Haskell String. If the string does not support unicode,
+    -- the Haskell String is encoded using UTF-8.
+    sFromUnicodeString :: String -> a
+
+    -- | Convert the string into a Haskell String.
+    sToString :: a -> String
+
+    -- | Convert the string into a list of bytes.
+    sToWord8 :: a -> [Word8]
+
+    sBreak d src = search 0 src
+      where
+        search a b | a `seq` b `seq` False = undefined
+        search n s
+            | sNull s         = (src, sEmpty)
+            | sStartsWith s d = (sTake n src, s)
+            | otherwise       = search (n+1) (sTail s)
+
+    sReplace n r = sConcat . replace n r
+      where
+        replace n r h = if sNull b
+                        then (if c then [a, r] else [a])
+                        else a : r : replace n r b
+          where (a, b, c) = sSplit n h
+
+    sSplit d s = (a, sDrop (sLen d) b, not $ sNull b)
+      where (a, b) = sBreak d s
+
+    sSplitAll d s = if sNull b
+                      then (if c then [a, b] else [a])
+                      else a : sSplitAll d b
+      where (a, b, c) = sSplit d s
+
+    sPadLeft  c n s = let len    = sLen s
+                          padLen = max 0 (n - len)
+                          padStr = sFromString $ replicate padLen c
+                      in  sConcat [padStr, s]
+                      
+    sPadRight c n s = let len    = sLen s
+                          padLen = max 0 (n - len)
+                          padStr = sFromString $ replicate padLen c
+                      in  sConcat [s, padStr]      
+
+    sPadBoth  c n s = let len     = sLen s
+                          padLen  = (max 0 (n - len))
+                          padLenR = padLen `quot` 2
+                          padLenL = padLen - padLenR
+                          padStrL = sFromString $ replicate padLenL c
+                          padStrR = sFromString $ replicate padLenR c
+                      in  sConcat [padStrL, s, padStrR]
+
+
+instance Strings ByteString where
+
+    sNull  = B.null
+    sEmpty = B.empty
+
+    sHead = toEnum . fromIntegral . B.head
+    sLast = toEnum . fromIntegral . B.last
+
+    sInit = B.init
+    sTail = B.tail
+
+    sTake = B.take
+    sTakeWhile f = B.takeWhile (f . chr)
+    sDrop = B.drop
+    sDropWhile f = B.dropWhile (f . chr)
+
+    sBreak = B.breakSubstring
+
+    sStartsWith = flip B.isPrefixOf
+    sEndsWith = flip B.isSuffixOf
+
+    sConcat = B.concat
+    sLen = B.length
+
+    sTrim s = let (a, b) = B.span    (isWhiteSpace . chr) s
+                  (c, d) = B.spanEnd (isWhiteSpace . chr) b
+              in c
+
+    sReverse = B.reverse
+
+    sFromString = B8.pack
+    sFromUnicodeString = Enc.encodeUtf8 . T.pack
+
+    sToString = B8.unpack
+    sToWord8 = B.unpack
+
+
+instance Strings LazyB.ByteString where
+
+    sNull  = LazyB.null
+    sEmpty = LazyB.empty
+
+    sHead = toEnum . fromIntegral . LazyB.head
+    sLast = toEnum . fromIntegral . LazyB.last
+
+    sInit = LazyB.init
+    sTail = LazyB.tail
+
+    sTake = LazyB.take . fromIntegral
+    sTakeWhile f = LazyB.takeWhile (f . chr)
+    sDrop = LazyB.drop . fromIntegral
+    sDropWhile f = LazyB.dropWhile (f . chr)
+
+    sStartsWith = flip LazyB.isPrefixOf
+    sEndsWith = flip LazyB.isSuffixOf
+
+    sConcat = LazyB.concat
+    sLen = fromIntegral . LazyB.length
+
+    sTrim = LazyB.fromChunks . (:[]) . sTrim . B.concat . LazyB.toChunks
+
+    sReverse = LazyB.reverse
+
+    sFromString = LazyB8.pack
+    sFromUnicodeString = LazyEnc.encodeUtf8 . LazyT.pack
+
+    sToString = LazyB8.unpack
+    sToWord8 = LazyB.unpack
+
+
+instance Strings Text where
+
+    sNull  = T.null
+    sEmpty = T.empty
+
+    sHead = T.head
+    sLast = T.last
+
+    sInit = T.init
+    sTail = T.tail
+
+    sTake = T.take
+    sTakeWhile = T.takeWhile
+    sDrop = T.drop
+    sDropWhile = T.dropWhile
+
+    sReplace = T.replace
+    sBreak = T.breakOn
+
+    sStartsWith = flip T.isPrefixOf
+    sEndsWith = flip T.isSuffixOf
+
+    sConcat = T.concat
+    sLen = T.length
+
+    sTrim = T.strip
+
+    sPadLeft  = flip T.justifyLeft
+    sPadRight = flip T.justifyRight
+    sPadBoth  = flip T.center
+
+    sReverse = T.reverse
+
+    sFromUnicodeString = S.fromString
+    sFromString = S.fromString
+
+    sToString = T.unpack
+    sToWord8 = B.unpack . Enc.encodeUtf8
+
+
+instance Strings LazyT.Text where
+
+    sNull  = LazyT.null
+    sEmpty = LazyT.empty
+
+    sHead = LazyT.head
+    sLast = LazyT.last
+
+    sInit = LazyT.init
+    sTail = LazyT.tail
+
+    sTake = LazyT.take . fromIntegral
+    sTakeWhile = LazyT.takeWhile
+    sDrop = LazyT.drop . fromIntegral
+    sDropWhile = LazyT.dropWhile
+
+    sBreak = LazyT.breakOn
+
+    sStartsWith = flip LazyT.isPrefixOf
+    sEndsWith = flip LazyT.isSuffixOf
+
+    sConcat = LazyT.concat
+    sLen = fromIntegral . LazyT.length
+
+    sTrim = LazyT.strip
+
+    sPadLeft  = flip (LazyT.justifyLeft . fromIntegral)
+    sPadRight = flip (LazyT.justifyRight . fromIntegral)
+    sPadBoth  = flip (LazyT.center . fromIntegral)
+
+    sReverse = LazyT.reverse
+
+    sFromUnicodeString = S.fromString
+    sFromString = S.fromString
+
+    sToString = LazyT.unpack
+    sToWord8 = LazyB.unpack . LazyEnc.encodeUtf8
+
+
+instance Strings String where
+
+    sNull = null
+    sEmpty = []
+
+    sHead = head
+    sLast = last
+
+    sInit = init
+    sTail = tail
+
+    sTake = take
+    sTakeWhile = takeWhile
+    sDrop = drop
+    sDropWhile = dropWhile
+
+    sStartsWith = flip L.isPrefixOf
+    sEndsWith = flip L.isSuffixOf
+
+    sConcat = concat
+    sLen = length
+
+    sTrim = T.unpack . T.strip . T.pack
+
+    sReverse = reverse
+
+    sFromString = id
+    sFromUnicodeString = id
+
+    sToString = id
+    sToWord8 = B.unpack . Enc.encodeUtf8 . T.pack
+
+-- | Utility class for working with characters.
+class Chars a where
+    -- | Retrieve the value of the given character.
+    ord :: Char -> a
+
+    -- | Construct a character form the given value.
+    chr :: a -> Char
+
+instance Chars Int where
+    ord = fromIntegral . fromEnum
+    chr = toEnum . fromIntegral
+
+instance Chars Word8 where
+    ord = fromIntegral . fromEnum
+    chr = toEnum . fromIntegral
+
+instance Chars Word16 where
+    ord = fromIntegral . fromEnum
+    chr = toEnum . fromIntegral
+
+instance Chars Word32 where
+    ord = fromIntegral . fromEnum
+    chr = toEnum . fromIntegral
+
+instance Chars Word64 where
+    ord = fromIntegral . fromEnum
+    chr = toEnum . fromIntegral
+
+instance Chars Int8 where
+    ord = fromIntegral . fromEnum
+    chr = toEnum . fromIntegral
+
+instance Chars Int16 where
+    ord = fromIntegral . fromEnum
+    chr = toEnum . fromIntegral
+
+instance Chars Int32 where
+    ord = fromIntegral . fromEnum
+    chr = toEnum . fromIntegral
+
+instance Chars Int64 where
+    ord = fromIntegral . fromEnum
+    chr = toEnum . fromIntegral
+
+instance Chars Integer where
+    ord = fromIntegral . fromEnum
+    chr = toEnum . fromIntegral
+
+
diff --git a/strings.cabal b/strings.cabal
new file mode 100644
--- /dev/null
+++ b/strings.cabal
@@ -0,0 +1,18 @@
+Name:               strings
+Version:            1.0.0
+Synopsis:           Functions for working with strings, including Text, ByteString, etc.
+Description:        Functions for working with strings, including Text, ByteString, etc.
+License:            MIT
+License-File:       LICENSE
+Author:             Julian Fleischer
+Maintainer:         julian.fleischer@fu-berlin.de
+Build-Type:         Simple
+Cabal-Version:      >= 1.4
+Category:           Text, String, Utility
+Stability:          provisional
+
+Library
+    Exposed-Modules:    Data.Strings
+    Build-Depends:      base >= 4 && < 5, bytestring >= 0.9 && < 0.10, text >= 0.10
+    Hs-Source-Dirs:     src
+
