diff --git a/src/main/scala/fpspeedrun/Magma.scala b/src/main/scala/fpspeedrun/Magma.scala new file mode 100644 index 0000000..e0122ef --- /dev/null +++ b/src/main/scala/fpspeedrun/Magma.scala @@ -0,0 +1,25 @@ +package fpspeedrun + +trait Magma[T] { + def combine(x: T, y: T): T +} + +sealed trait FreeMagma[T] +case class Leaf[T](x: T) extends FreeMagma[T] +case class Branch[T](x: FreeMagma[T], y: FreeMagma[T]) extends FreeMagma[T] + +object FreeMagma { + + def apply[T](x: T, y: T*): FreeMagma[T] = y match { + case Seq() => Leaf(x) + case Seq(a) => Branch(Leaf(x), Leaf(a)) + case ys => + val list = (x +: ys).reverse + val first +: second +: rest = list + + rest.foldLeft(Branch(Leaf(second), Leaf(first)))((acc, e) => Branch(Leaf(e), acc)) + } + + implicit def freeMagmaMagma[T]: Magma[FreeMagma[T]] = (x: FreeMagma[T], y: FreeMagma[T]) => Branch(x, y) + +} \ No newline at end of file diff --git a/src/main/scala/fpspeedrun/syntax/package.scala b/src/main/scala/fpspeedrun/syntax/package.scala index 6e99f14..2bc8be0 100644 --- a/src/main/scala/fpspeedrun/syntax/package.scala +++ b/src/main/scala/fpspeedrun/syntax/package.scala @@ -1,6 +1,8 @@ package fpspeedrun.syntax import fpspeedrun._ +import scala.annotation.tailrec + object eq extends Eq.ToEqOps object ord extends Ord.ToOrdOps @@ -45,6 +47,23 @@ object semigroup extends Semigroup.ToSemigroupOps { def first: First[T] = First(x) def last: Last[T] = Last(x) } + + implicit class FreeMagmaOps[T](val x: FreeMagma[T]) extends AnyVal { + @tailrec final def flat(x: List[FreeMagma[T]]): List[Leaf[T]] = { + val list: List[FreeMagma[T]] = x.flatMap { + case l@Leaf(_) => List(l) + case Branch(l, r) => List(l, r) + } + + if (x.size == list.size) x.map(_.asInstanceOf[Leaf[T]]) + else flat(list) + } + + def reduceAll(implicit sg: Semigroup[T]): T = x match { + case Leaf(a) => a + case b@Branch(_, _) => flat(List(b)) map { case Leaf(l) => l } reduce sg.combine + } + } } object monoid extends Monoid.ToMonoidOps