{-# LANGUAGE GADTs #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Control.Monad.Eff.Writer (
  Writer(..),
  tell, writer,
  runWriter
) where

import Control.Monad.Eff
import Data.Monoid

data Writer o a where
  Put :: o -> Writer o ()

tell :: Member (Writer o) r => o -> Eff r ()
tell o = send (Put o)

writer :: Member (Writer o) r => (a, o) -> Eff r a
writer (a, o) = do
  tell o
  return a

runWriter :: Monoid o => Eff (Writer o ': r) a -> Eff r (a, o)
runWriter = handleRelay ret handle
  where
    ret :: Monoid o => a -> Eff r (a, o)
    ret a = return (a, mempty)

    handle :: Monoid o => Handler (Writer o) r (a, o)
    handle (Put o) k = do
      (a, os) <- k ()
      return (a, o <> os)