diff --git a/src/main/scala/CustomFolds.scala b/src/main/scala/CustomFolds.scala new file mode 100644 index 0000000..c212c15 --- /dev/null +++ b/src/main/scala/CustomFolds.scala @@ -0,0 +1,19 @@ +import cats.Eval +import fpspeedrun.Fooldable._ +import fpspeedrun.Fooldable.ops._ +import cats.instances.int._ +import cats.instances.bigInt._ + +object CustomFolds { + + def main(args: Array[String]): Unit = { + val s = Stream.from(1) + println(s.take(100000).map(BigInt(_)).foldMapLazy(x => Eval.now(x)).value) + println(s.take(100000).foldl("0")(_ + _.toString).take(50)) + println(s.take(100000).foldr(Eval.now("x")) { (a, lb) => + lb.map(_ + a.toString) + }.value.take(50)) + + println(s.map(BigInt(_)).sumN(100000)) + } +} diff --git a/src/main/scala/fpspeedrun/Calc.scala b/src/main/scala/fpspeedrun/Calc.scala index d39a89e..62551f8 100644 --- a/src/main/scala/fpspeedrun/Calc.scala +++ b/src/main/scala/fpspeedrun/Calc.scala @@ -27,9 +27,6 @@ trait Calc[A] { def pow(x: A, p: Int): A = Calc.fastPow(x, p)(this) } -// TODO create the real FreeNum -sealed trait Expr[A] - object Calc extends StdCalcInstances[Calc] { import ops._ @@ -41,12 +38,28 @@ object Calc extends StdCalcInstances[Calc] { go(x, calc.one, p) } + // TODO create the real FreeCalc + type Expr[A] = List[(Int, List[A])] //Sum of products + //TODO implement this implicit val freeCalc: FreeConstruct[Calc, Expr] = new FreeConstruct[Calc, Expr] { - override def embed[T](x: T): Expr[T] = ??? - override def instance[T]: Num[Expr[T]] = ??? - override def mapInterpret[A, B](fa: Expr[A])(f: A => B)(implicit instance: Calc[B]): B = ??? + override def embed[T](x: T): Expr[T] = List(1 -> List(x)) + override def instance[T]: Calc[Expr[T]] = new Calc[Expr[T]] { + override def fromInt(x: Int): Expr[T] = List(x -> Nil) + override def plus(x: Expr[T], y: Expr[T]): Expr[T] = x ::: y + override def times(x: Expr[T], y: Expr[T]): Expr[T] = + for { + (k1, p1) <- x + (k2, p2) <- y + } yield { + (k1 * k2) -> (p1 ::: p2) + } + } + override def mapInterpret[A, B](fa: Expr[A])(f: A => B)(implicit instance: Calc[B]): B = + fa.foldLeft(instance.zero) { + case (acc, (k, p)) => acc + (instance.fromInt(k) * p.map(f).foldLeft(instance.one)(instance.times)) + } } } diff --git a/src/main/scala/fpspeedrun/Eq.scala b/src/main/scala/fpspeedrun/Eq.scala index 1f0649b..69ee061 100644 --- a/src/main/scala/fpspeedrun/Eq.scala +++ b/src/main/scala/fpspeedrun/Eq.scala @@ -36,6 +36,12 @@ object Eq extends StdEqInstances { } } -trait StdEqInstances extends StdOrdInstances[Eq]{ - implicit def eitherEq[A: Eq, B: Eq]: Eq[Either[A, B]] = ??? +trait StdEqInstances extends StdOrdInstances[Eq] { + import fpspeedrun.Eq.ops._ + implicit def eitherEq[A: Eq, B: Eq]: Eq[Either[A, B]] = (x: Either[A, B], y: Either[A, B]) => + (x, y) match { + case (Left(x1), Left(y1)) => x1 === y1 + case (Right(x1), Right(y1)) => x1 === y1 + case _ => false + } } diff --git a/src/main/scala/fpspeedrun/Fooldable.scala b/src/main/scala/fpspeedrun/Fooldable.scala new file mode 100644 index 0000000..8a8f3bc --- /dev/null +++ b/src/main/scala/fpspeedrun/Fooldable.scala @@ -0,0 +1,64 @@ +package fpspeedrun + +import cats.Eval +import cats.Eval._ +import fpspeedrun.Fooldable.LazyMonoid +import simulacrum.typeclass + +@typeclass +trait Fooldable[F[_]] { + def foldMapLazy[A, B: LazyMonoid](fa: F[A])(f: A => Eval[B]): Eval[B] + def foldl[A, B](fa: F[A], acc: B)(f: (B, A) => B): B = { + implicit val combiner: cats.Monoid[B => Eval[B]] = new cats.Monoid[B => Eval[B]] { + override def empty: B => Eval[B] = Eval.now + override def combine(x: B => Eval[B], y: B => Eval[B]): B => Eval[B] = b => Eval.defer(x(b).flatMap(y(_))) + } + foldMapLazy(fa) { a => + Eval.now { + b: B => Eval.now(f(b, a)) + } + }.flatMap(_ (acc)).value + } + + def foldr[A, B](fa: F[A], lacc: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = { + implicit val combiner: cats.Monoid[Eval[Eval[B] => Eval[B]]] = new cats.Monoid[Eval[Eval[B] => Eval[B]]] { + override def empty: Eval[Eval[B] => Eval[B]] = Eval.now(identity) + + override def combine(x: Eval[Eval[B] => Eval[B]], y: Eval[Eval[B] => Eval[B]]): Eval[Eval[B] => Eval[B]] = + Eval.later { b: Eval[B] => + for { + xf <- x + yf <- y + xx <- xf(yf(b)) + } yield xx + } + } + + foldMapLazy[A, Eval[B] => Eval[B]](fa) { a => + Eval.now { lb: Eval[B] => + f(a, lb) + } + }.flatMap(_ (lacc)) + } + + def sumN[A: cats.Monoid](xs: F[A])(n: Int): A = + foldr[A, Int => Eval[A]](xs, Eval.now(_ => Eval.now(cats.Monoid[A].empty))) { (x, acc) => + Eval.later { k: Int => + if (k == 0) Eval.now(cats.Monoid[A].empty) + else for { + f <- acc + s <- f(k - 1) + } yield cats.Monoid[A].combine(s, x) + } + }.flatMap(_ (n)).value +} + +object Fooldable { + type LazyMonoid[A] = cats.Monoid[Eval[A]] + + implicit val streamFooldable: Fooldable[Stream] = new Fooldable[Stream] { + override def foldMapLazy[A, B: LazyMonoid](fa: Stream[A])(f: A => Eval[B]): Eval[B] = + if (fa.isEmpty) cats.Monoid[Eval[B]].empty + else cats.Monoid[Eval[B]].combine(f(fa.head), Eval.defer(foldMapLazy(fa.tail)(f))) + } +} diff --git a/src/main/scala/fpspeedrun/Magma.scala b/src/main/scala/fpspeedrun/Magma.scala index 91a48b8..6205716 100644 --- a/src/main/scala/fpspeedrun/Magma.scala +++ b/src/main/scala/fpspeedrun/Magma.scala @@ -1,6 +1,10 @@ package fpspeedrun +import cats.{ Eval, Foldable } + import simulacrum.typeclass +import scala.annotation.tailrec + @typeclass trait Magma[T] { def merge(x: T, y: T): T @@ -11,17 +15,71 @@ object Magma extends StdMagmaInstances { new FreeConstruct[Magma, BinTree] { override def embed[T](x: T): BinTree[T] = Leaf(x) override def instance[T]: Magma[BinTree[T]] = Branch(_, _) - override def mapInterpret[A, B](fa: BinTree[A])(f: A => B)(implicit instance: Magma[B]): B = - fa match { - case Leaf(a) => f(a) - case Branch(fmx, fmy) => instance.merge(mapInterpret(fmx)(f), mapInterpret(fmy)(f)) + override def mapInterpret[A, B](fa: BinTree[A])(f: A => B)(implicit instance: Magma[B]): B = { + @tailrec def collector(queue: List[BinTree[A]], acc: B): B = queue match { + case Nil => acc + case Branch(l, r) :: qs => collector(l :: r :: qs, acc) + case Leaf(l) :: qs => collector(qs, instance.merge(acc, f(l))) + } + + @tailrec def reducer(queue: List[BinTree[A]]): B = queue match { + case Branch(l, r) :: qs => reducer(l :: r :: qs) + case Leaf(l) :: qs => collector(qs, f(l)) } + + reducer(fa :: Nil) + } } } -sealed trait BinTree[T] +sealed trait BinTree[T] { + private def nodesCount: Int = + implicitly[FreeConstruct[Magma, BinTree]].mapInterpret(this)(_ => 1)(_ + _) + + override def toString: String = this match { + case Leaf(x) => s"Leaf($x)" + case Branch(l, r) => s"Branch(${l.nodesCount} node(s), ${r.nodesCount} node(s))" + } +} final case class Leaf[T](x: T) extends BinTree[T] final case class Branch[T](x: BinTree[T], y: BinTree[T]) extends BinTree[T] +object BinTree { + + // Right-bound +// def apply[T](x: T, xs: T*): BinTree[T] = xs match { +// case Seq() => Leaf(x) +// case _ => +// val reversed = (x +: xs).reverse +// reversed.tail.foldLeft(Leaf(reversed.head): BinTree[T])((acc, x) => Branch(Leaf(x), acc)) +// } + + // Left-bound + def apply[T](x: T, xs: T*): BinTree[T] = + xs.foldLeft(Leaf(x): BinTree[T])((acc, x) => Branch(acc, Leaf(x))) + + implicit val binTreeFoldable: Foldable[BinTree] = new Foldable[BinTree] { + override def foldLeft[A, B](fa: BinTree[A], b: B)(f: (B, A) => B): B = { + def loop(current: BinTree[A], acc: B): Eval[B] = current match { + case Leaf(x) => Eval.now(f(acc, x)) + case Branch(l, r) => + for { + left <- Eval.defer(loop(l, acc)) + both <- Eval.defer(loop(r, left)) + } yield both + } + loop(fa, b).value + } + + override def foldRight[A, B](fa: BinTree[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = { + def loop(current: BinTree[A], acc: Eval[B]): Eval[B] = current match { + case Leaf(x) => f(x, acc) + case Branch(l, r) => loop(l, Eval.defer(loop(r, acc))) + } + loop(fa, lb) + } + } +} + trait StdMagmaInstances extends StdSemigroupInstances[Magma] diff --git a/src/main/scala/fpspeedrun/Monoid.scala b/src/main/scala/fpspeedrun/Monoid.scala index f648e8d..0561921 100644 --- a/src/main/scala/fpspeedrun/Monoid.scala +++ b/src/main/scala/fpspeedrun/Monoid.scala @@ -1,5 +1,5 @@ package fpspeedrun -import fpspeedrun.Iso.{Wrapper, WrapperCompanion} +import fpspeedrun.Iso.{ Wrapper, WrapperCompanion } import simulacrum.typeclass import syntax.semigroup._ @@ -18,32 +18,44 @@ object Monoid extends StdMonoidInstances[Monoid]{ } //TODO find the real type. Hint: look below - type FreeMonoid[A] = Nothing + type FreeMonoid[A] = List[A] implicit val freeMonoid: FreeConstruct[Monoid, FreeMonoid] = new FreeConstruct[Monoid, FreeMonoid] { - override def embed[T](x: T): FreeMonoid[T] = ??? - override def instance[T]: Monoid[FreeMonoid[T]] = ??? - override def mapInterpret[A, B](fa: FreeMonoid[A])(f: A => B)(implicit instance: Monoid[B]): B = ??? + override def embed[T](x: T): FreeMonoid[T] = x :: Nil + override def instance[T]: Monoid[FreeMonoid[T]] = implicitly[Monoid[List[T]]] + override def mapInterpret[A, B](fa: FreeMonoid[A])(f: A => B)(implicit instance: Monoid[B]): B = + fa.map(f).fold(instance.empty)(instance.combine) } } final case class Endo[A](run: A => A) extends AnyVal object Endo { - implicit def endoMonoid[A]: Monoid[Endo[A]] = ??? + implicit def endoMonoid[A]: Monoid[Endo[A]] = new Monoid[Endo[A]] { + override def empty: Endo[A] = Endo(identity) + override def combine(x: Endo[A], y: Endo[A]): Endo[A] = Endo(x.run andThen y.run) + } } final case class Sum[T](value: T) extends AnyVal with Wrapper[T] object Sum extends WrapperCompanion[Sum] { - implicit def sumMonoid[T: Num]: Monoid[Sum[T]] = ??? + import syntax.num._ + implicit def sumMonoid[T: Num]: Monoid[Sum[T]] = new Monoid[Sum[T]] { + override def empty: Sum[T] = Sum(zero) + override def combine(x: Sum[T], y: Sum[T]): Sum[T] = Sum(x.value + y.value) + } } final case class Prod[T](value: T) extends AnyVal with Wrapper[T] object Prod extends WrapperCompanion[Prod] { - implicit def prodMonoid[T: Num]: Monoid[Prod[T]] = ??? + import syntax.num._ + implicit def prodMonoid[T: Num]: Monoid[Prod[T]] = new Monoid[Prod[T]] { + override def empty: Prod[T] = Prod(one) + override def combine(x: Prod[T], y: Prod[T]): Prod[T] = Prod(x.value * y.value) + } } trait StdMonoidInstances[TC[x] >: Monoid[x]] { @@ -57,5 +69,8 @@ trait StdMonoidInstances[TC[x] >: Monoid[x]] { override def combine(x: List[A], y: List[A]): List[A] = x ::: y } - final implicit def vectorMonoid[A]: TC[Vector[A]] = ??? + final implicit def vectorMonoid[A]: TC[Vector[A]] = new Monoid[Vector[A]] { + override def empty: Vector[A] = Vector.empty + override def combine(x: Vector[A], y: Vector[A]): Vector[A] = x ++ y + } } diff --git a/src/main/scala/fpspeedrun/Ord.scala b/src/main/scala/fpspeedrun/Ord.scala index f00201e..ab1a664 100644 --- a/src/main/scala/fpspeedrun/Ord.scala +++ b/src/main/scala/fpspeedrun/Ord.scala @@ -70,6 +70,14 @@ object Ord extends StdOrdInstances[Ord] { } trait StdOrdInstances[TC[t] >: Ord[t]] extends StdNumInstances[TC]{ + import fpspeedrun.Ord.ops._ final implicit val stringOrd: TC[String] = byOrdering - final implicit def optionOrd[A: Ord]: TC[Option[A]] = ??? -} \ No newline at end of file + final implicit def optionOrd[A: Ord]: TC[Option[A]] = new Ord[Option[A]] { + override def compare(x: Option[A], y: Option[A]): Compare = (x, y) match { + case (None, None) => EQ + case (None, Some(_)) => LT + case (Some(_), None) => GT + case (Some(x1), Some(y1)) => x1 compare y1 + } + } +} diff --git a/src/main/scala/fpspeedrun/Semigroup.scala b/src/main/scala/fpspeedrun/Semigroup.scala index 4570bfb..bb4c81a 100644 --- a/src/main/scala/fpspeedrun/Semigroup.scala +++ b/src/main/scala/fpspeedrun/Semigroup.scala @@ -1,6 +1,7 @@ package fpspeedrun -import fpspeedrun.Iso.{Wrapper, WrapperCompanion} -import simulacrum.{op, typeclass} +import cats.data.NonEmptyList +import fpspeedrun.Iso.{ Wrapper, WrapperCompanion } +import simulacrum.{ op, typeclass } @typeclass trait Semigroup[T] extends Magma[T] { @@ -12,12 +13,13 @@ trait Semigroup[T] extends Magma[T] { object Semigroup extends StdSemigroupInstances[Semigroup]{ //TODO find the real type. Hint: search in cats something like FreeMonoid but little bit stricter - type FreeSemigrpoup[T] = Nothing + type FreeSemigrpoup[T] = NonEmptyList[T] implicit val freeConstruct: FreeConstruct[Semigroup, FreeSemigrpoup] = new FreeConstruct[Semigroup, FreeSemigrpoup] { - override def embed[T](x: T): FreeSemigrpoup[T] = ??? - override def instance[T]: Semigroup[FreeSemigrpoup[T]] = ??? - override def mapInterpret[A, B](fa: FreeSemigrpoup[A])(f: A => B)(implicit instance: Semigroup[B]): B = ??? + override def embed[T](x: T): FreeSemigrpoup[T] = NonEmptyList.of(x) + override def instance[T]: Semigroup[FreeSemigrpoup[T]] = _ ::: _ + override def mapInterpret[A, B](fa: FreeSemigrpoup[A])(f: A => B)(implicit instance: Semigroup[B]): B = + fa.map(f).reduceLeft(instance.combine) } } diff --git a/src/main/scala/fpspeedrun/ZipList.scala b/src/main/scala/fpspeedrun/ZipList.scala index 607767d..25f21ef 100644 --- a/src/main/scala/fpspeedrun/ZipList.scala +++ b/src/main/scala/fpspeedrun/ZipList.scala @@ -1,5 +1,13 @@ package fpspeedrun +import fpspeedrun.syntax.eq._ +import fpspeedrun.syntax.ord._ +import fpspeedrun.syntax.num._ +import fpspeedrun.syntax.integ._ +import fpspeedrun.syntax.frac._ +import fpspeedrun.syntax.semigroup._ +import fpspeedrun.Ord.Compare._ + trait ZipList[A] { def value: Either[A, List[A]] @@ -25,17 +33,55 @@ object ZipList { def apply[A](list: List[A]): ZipList[A] = Finite(list) def repeat[A](value: A): ZipList[A] = Repeat(value) - implicit def zipListSemigroup[A: Semigroup]: Semigroup[ZipList[A]] = ??? + private class ZipListSemigroup[A: Semigroup] extends Semigroup[ZipList[A]] { + override def combine(x: ZipList[A], y: ZipList[A]): ZipList[A] = x.zipWith(y)(_ combine _) + } - implicit def zipListMonoid[A: Monoid]: Monoid[ZipList[A]] = ??? + private class ZipListMonoid[A: Monoid] extends ZipListSemigroup[A] with Monoid[ZipList[A]] { + override def empty: ZipList[A] = repeat(Monoid[A].empty) + } - implicit def zipListEq[A: Eq]: Eq[ZipList[A]] = ??? + private class ZipListEq[A: Eq] extends Eq[ZipList[A]] { + override def equal(x: ZipList[A], y: ZipList[A]): Boolean = x.value === y.value + } - implicit def zipListOrd[A: Ord]: Ord[ZipList[A]] = ??? + private class ZipListOrd[A: Ord] extends ZipListEq[A] with Ord[ZipList[A]] { + override def compare(x: ZipList[A], y: ZipList[A]): Ord.Compare = (x.value, y.value) match { + case (Left(x1), Left(y1)) => x1 <=> y1 + case (Right(x1), Right(y1)) => x1 <=> y1 + case (Left(x1), Right(ys)) => ys.collectFirst { + case y1 if (x1 <=> y1) != EQ => x1 <=> y1 + }.getOrElse(EQ) + case (Right(xs), Left(y1)) => xs.collectFirst { + case x1 if (x1 <=> y1) != EQ => x1 <=> y1 + }.getOrElse(EQ) + } + } - implicit def zipListNum[A: Num]: Num[ZipList[A]] = ??? + private class ZipListNum[A: Num] extends ZipListOrd[A] with Num[ZipList[A]] { + override def fromInt(x: Int): ZipList[A] = Repeat(x.toNum) + override def plus(x: ZipList[A], y: ZipList[A]): ZipList[A] = x.zipWith(y)(_ + _) + override def times(x: ZipList[A], y: ZipList[A]): ZipList[A] = x.zipWith(y)(_ * _) + } - implicit def zipListInteg[A: Integ]: Integ[ZipList[A]] = ??? + private class ZipListInteg[A: Integ] extends ZipListNum[A] with Integ[ZipList[A]] { + override def quotRem(x: ZipList[A], y: ZipList[A]): (ZipList[A], ZipList[A]) = x.zipWith(y)(_ /% _).value match { + case Left((q, r)) => Repeat(q) -> Repeat(r) + case Right(qrs) => qrs.unzip match { + case (l, r) => ZipList(l) -> ZipList(r) + } + } + } + + private class ZipListFrac[A: Frac] extends ZipListNum[A] with Frac[ZipList[A]] { + override def div(x: ZipList[A], y: ZipList[A]): ZipList[A] = x.zipWith(y)(_ / _) + } - implicit def zipListFrac[A: Frac]: Frac[ZipList[A]] = ??? + implicit def zipListSemigroup[A: Semigroup]: Semigroup[ZipList[A]] = new ZipListSemigroup[A] + implicit def zipListMonoid[A: Monoid]: Monoid[ZipList[A]] = new ZipListMonoid[A] + implicit def zipListEq[A: Eq]: Eq[ZipList[A]] = new ZipListEq[A] + implicit def zipListOrd[A: Ord]: Ord[ZipList[A]] = new ZipListOrd[A] + implicit def zipListNum[A: Num]: Num[ZipList[A]] = new ZipListNum[A] + implicit def zipListInteg[A: Integ]: Integ[ZipList[A]] = new ZipListInteg[A] + implicit def zipListFrac[A: Frac]: Frac[ZipList[A]] = new ZipListFrac[A] } diff --git a/src/main/scala/fpspeedrun/syntax/package.scala b/src/main/scala/fpspeedrun/syntax/package.scala index 0361005..363a5f7 100644 --- a/src/main/scala/fpspeedrun/syntax/package.scala +++ b/src/main/scala/fpspeedrun/syntax/package.scala @@ -48,13 +48,14 @@ object semigroup extends Semigroup.ToSemigroupOps { } object monoid extends Monoid.ToMonoidOps{ - def empty[T: Monoid]: T = ??? + def empty[T: Monoid]: T = Monoid[T].empty implicit class ListOps[A](val xs: List[A]) extends AnyVal{ - def foldAll(implicit mon: Monoid[A]): A = ??? + def foldAll(implicit mon: Monoid[A]): A = xs.foldLeft(mon.empty)(mon.combine) - def foldMap[B: Monoid](f: A => B): B = ??? + def foldMap[B: Monoid](f: A => B): B = xs.map(f).foldAll - def foldVia[F[_]](implicit iso: Iso[A, F[A]], mon: Monoid[F[A]]): A = ??? + def foldVia[F[_]](implicit iso: Iso[A, F[A]], mon: Monoid[F[A]]): A = + xs.foldLeft(iso.unwrap(mon.empty))((x, y) => iso.unwrap(mon.combine(iso.wrap(x), iso.wrap(y)))) } }