{-# LANGUAGE DeriveFunctor, TypeSynonymInstances, FlexibleInstances, TypeFamilies #-}
-- |
-- The `build` function can be used to construct multi-line string literals in
-- a monadic way:
--
-- > {-# LANGUAGE OverloadedStrings #-}
-- >
-- > import Data.String.Builder
-- >
-- > mystring :: String
-- > mystring = build $ do
-- >   "foo"
-- >   "bar"
-- >   "baz"
module Data.String.Builder (
-- * Functions
  build
, literal
-- * Types
, Builder
, BuilderM
) where

import           Control.Applicative
import           Control.Monad
import           Data.Monoid
import           Data.String

-- | A writer monad for string literals.
data BuilderM a = BuilderM a ShowS
  deriving Functor

instance Applicative BuilderM where
  pure = return
  (<*>) = ap

instance Monad BuilderM where
  return a            = BuilderM a id
  BuilderM a xs >>= f = case f a of
    BuilderM b ys -> BuilderM b (xs . ys)

type Builder = BuilderM ()

instance Monoid Builder where
  mempty = return ()
  a `mappend` b = a >> b

-- | Add a literal string.
literal :: String -> Builder
literal = BuilderM () . showString

instance (a ~ ()) => IsString (BuilderM a) where
  fromString s = literal s >> literal "\n"

-- | Run a builder.
build :: Builder -> String
build (BuilderM () s) = s ""
