Theory Writer_Monad

section ‹Writer monad›

theory Writer_Monad
imports Monad
begin

subsection ‹Monoid class›

class monoid = "domain" +
  fixes mempty :: "'a"
  fixes mappend :: "'a  'a  'a"
  assumes mempty_left: "ys. mappendmemptyys = ys"
  assumes mempty_right: "xs. mappendxsmempty = xs"
  assumes mappend_assoc:
    "xs ys zs. mappend(mappendxsys)zs = mappendxs(mappendyszs)"

subsection ‹Writer monad type›

text ‹Below is the standard Haskell definition of a writer monad
type; it is an isomorphic copy of the lazy pair type \texttt{(a, w)}.
›

text_raw ‹
\begin{verbatim}
newtype Writer w a = Writer { runWriter :: (a, w) }
\end{verbatim}
›

text ‹Since HOLCF does not have a pre-defined lazy pair type, we
will base this formalization on an equivalent, more direct definition:
›

text_raw ‹
\begin{verbatim}
data Writer w a = Writer w a
\end{verbatim}
›

text ‹We can directly translate the above Haskell type definition
using tycondef›. \medskip›

tycondef 'a'w writer = Writer (lazy "'w") (lazy "'a")

lemma coerce_writer_abs [simp]: "coerce(writer_absx) = writer_abs(coercex)"
apply (simp add: writer_abs_def coerce_def)
apply (simp add: emb_prj_emb prj_emb_prj DEFL_eq_writer)
done

lemma coerce_Writer [simp]:
  "coerce(Writerwx) = Writer(coercew)(coercex)"
unfolding Writer_def by simp

lemma fmapU_writer_simps [simp]:
  "fmapUf(::udom'w writer) = "
  "fmapUf(Writerwx) = Writerw(fx)"
unfolding fmapU_writer_def writer_map_def fix_const
apply simp
apply (simp add: Writer_def)
done

subsection ‹Class instance proofs›

instance writer :: ("domain") "functor"
proof
  fix f g :: "udom  udom" and xs :: "udom'a writer"
  show "fmapUf(fmapUgxs) = fmapU(Λ x. f(gx))xs"
    by (induct xs rule: writer.induct) simp_all
qed

instantiation writer :: (monoid) monad
begin

fixrec bindU_writer ::
    "udom'a writer  (udom  udom'a writer)  udom'a writer"
  where "bindU_writer(Writerwx)f =
    (case fx of Writerw'y  Writer(mappendww')y)"

lemma bindU_writer_strict [simp]: "bindUk = (::udom'a writer)"
by fixrec_simp

definition
  "returnU = Writermempty"

instance proof
  fix f :: "udom  udom" and m :: "udom'a writer"
  show "fmapUfm = bindUm(Λ x. returnU(fx))"
    by (induct m rule: writer.induct)
       (simp_all add: returnU_writer_def mempty_right)
next
  fix f :: "udom  udom'a writer" and x :: "udom"
  show "bindU(returnUx)f = fx"
    by (cases "fx" rule: writer.exhaust)
       (simp_all add: returnU_writer_def mempty_left)
next
  fix m :: "udom'a writer" and f g :: "udom  udom'a writer"
  show "bindU(bindUmf)g = bindUm(Λ x. bindU(fx)g)"
    apply (induct m rule: writer.induct, simp)
    apply (case_tac "fa" rule: writer.exhaust, simp)
    apply (case_tac "gaa" rule: writer.exhaust, simp)
    apply (simp add: mappend_assoc)
    done
qed

end

subsection ‹Transfer properties to polymorphic versions›

lemma fmap_writer_simps [simp]:
  "fmapf(::'a'w writer) = "
  "fmapf(Writerwx :: 'a'w writer) = Writerw(fx)"
unfolding fmap_def [where 'f="'w writer"]
by (simp_all add: coerce_simp)

lemma return_writer_def: "return = Writermempty"
unfolding return_def returnU_writer_def
by (simp add: coerce_simp eta_cfun)

lemma bind_writer_simps [simp]:
  "bind( :: 'a'w::monoid writer)f = "
  "bind(Writerwx :: 'a'w::monoid writer)k =
    (case kx of Writerw'y  Writer(mappendww')y)"
unfolding bind_def
apply (simp add: coerce_simp)
apply (cases "kx" rule: writer.exhaust)
apply (simp_all add: coerce_simp)
done

lemma join_writer_simps [simp]:
  "join = ( :: 'a'w::monoid writer)"
  "join(Writerw(Writerw'x)) = Writer(mappendww')x"
unfolding join_def by simp_all

subsection ‹Extra operations›

definition tell :: "'w  unit('w::monoid writer)"
  where "tell = (Λ w. Writerw())"

end