package scalaz

////
/**
 * Universally quantified [[scalaz.Monoid]].
 */
////
trait PlusEmpty[F[_]] extends Plus[F] { self =>
  ////
  def empty[A]: F[A]

  // derived functions

  def monoid[A]: Monoid[F[A]] = new Monoid[F[A]] {
    def append(f1: F[A], f2: => F[A]): F[A] = plus(f1, f2)
    def zero: F[A] = empty[A]
  }

  trait EmptyLaw extends PlusLaw {
    def rightPlusIdentity[A](f1: F[A])(implicit FA: Equal[F[A]]): Boolean =
      FA.equal(plus(f1, empty[A]), f1)

    def leftPlusIdentity[A](f1: F[A])(implicit FA: Equal[F[A]]): Boolean =
      FA.equal(plus(empty[A], f1), f1)
  }

  def plusEmptyLaw = new EmptyLaw {}

  ////
  val plusEmptySyntax = new scalaz.syntax.PlusEmptySyntax[F] { def F = PlusEmpty.this }
}

object PlusEmpty {
  @inline def apply[F[_]](implicit F: PlusEmpty[F]): PlusEmpty[F] = F

  ////
  implicit def liftPlusEmpty[M[_], N[_]](implicit M: Monad[M], P: PlusEmpty[N]): PlusEmpty[({ type λ[α] = M[N[α]] })] = new PlusEmpty[({ type λ[α] = M[N[α]] })] {
    def empty[A] = M.point(P.empty[A])
    def plus[A](a: M[N[A]], b: => M[N[A]]): M[N[A]] = {
      M.bind(a) { a0 => M.map(b) { P.plus(a0, _) } }
    }
  }
  ////
}