diff --git a/src/main/scala/fpspeedrun/foldables/Delay.scala b/src/main/scala/fpspeedrun/foldables/Delay.scala index f75680c..77a6834 100644 --- a/src/main/scala/fpspeedrun/foldables/Delay.scala +++ b/src/main/scala/fpspeedrun/foldables/Delay.scala @@ -9,11 +9,13 @@ trait Delay[A] { def delay(x: => A): A } -sealed trait EvalDelayInstance{ - implicit def evalDelay[A]: Delay[Eval[A]] = ??? +sealed trait EvalDelayInstance { + implicit def evalDelay[A]: Delay[Eval[A]] = new Delay[Eval[A]] { + override def delay(x: => Eval[A]): Eval[A] = Eval.defer(x) + } } object Delay extends EvalDelayInstance { - implicit def dual[A](implicit delay: Delay[A]): Delay[Dual[A]] = ??? + implicit def dual[A](implicit d: Delay[A]): Delay[Dual[A]] = new Delay[Dual[A]] { + override def delay(x: => Dual[A]): Dual[A] = Dual(d.delay(x.getDual)) + } } - - diff --git a/src/main/scala/fpspeedrun/foldables/FoldMapDelay.scala b/src/main/scala/fpspeedrun/foldables/FoldMapDelay.scala index 9435fcf..714ceac 100644 --- a/src/main/scala/fpspeedrun/foldables/FoldMapDelay.scala +++ b/src/main/scala/fpspeedrun/foldables/FoldMapDelay.scala @@ -1,19 +1,31 @@ package fpspeedrun package foldables -import cats.{Eval, Foldable, Monoid} -import newts.Dual +import cats.{ Eval, Foldable, Monoid } +import fpspeedrun.foldables.LzEndo._ import syntax.delay._ import cats.syntax.semigroup._ +import newts.Dual abstract class FoldMapDelay[F[_]] extends Foldable[F] { def foldMapDelay[A, B: Monoid: Delay](fa: F[A])(f: A => B): B - override def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) => B): B = ??? - override def foldRight[A, B](fa: F[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = ??? + override def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) => B): B = { + foldMapDelay[A, LzEndo[B]](fa)(a => LzEndo[B](ev => ev.map(internal => f(internal, a)))) + .run(Eval.now(b)).value + } + override def foldRight[A, B](fa: F[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = { + foldMapDelay[A, Dual[LzEndo[B]]](fa)(a => Dual(LzEndo[B](f.curried(a)))).getDual.run(lb) + } } object FoldMapDelay { - implicit val streamInstance: Foldable[Stream] = ??? + implicit val streamInstance: Foldable[Stream] = new FoldMapDelay[Stream] { + override def foldMapDelay[A, B: Monoid : Delay](fa: Stream[A])(f: A => B): B = { + fa.headOption.fold(Monoid[B].empty) { h => + f(h) |+| foldMapDelay(fa.tail)(f).delay + } + } + } } diff --git a/src/main/scala/fpspeedrun/foldables/Iter.scala b/src/main/scala/fpspeedrun/foldables/Iter.scala index ebaaeff..bad8396 100644 --- a/src/main/scala/fpspeedrun/foldables/Iter.scala +++ b/src/main/scala/fpspeedrun/foldables/Iter.scala @@ -1,14 +1,31 @@ package fpspeedrun.foldables import cats.Foldable +import cats.Eval +import cats.instances.stream._ +import cats.syntax.foldable._ final case class Iter[F[_]: Foldable, A](x: F[A]) extends Iterable[A]{ - override def iterator: Iterator[A] = ??? + override def iterator: Iterator[A] = { + Foldable[F].foldRight(x, Eval.now(Iterator[A]())) { (v, acc) => + Eval.now { + val as = Iterator(v) + as ++ acc.value + } + }.value + } } -final case class Fold[A](xs: Iterable[A]) -object Fold{ - implicit val foldable: Foldable[Fold] = ??? -} +final case class Fold[A](xs: Iterable[A]) +object Fold { + implicit val foldable: Foldable[Fold] = new Foldable[Fold] { + override def foldLeft[A, B](fa: Fold[A], b: B)(f: (B, A) => B): B = { + fa.xs.foldLeft(b)(f) + } + override def foldRight[A, B](fa: Fold[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = { + fa.xs.toStream.foldr(lb)(f) + } + } +} diff --git a/src/main/scala/fpspeedrun/foldables/LzEndo.scala b/src/main/scala/fpspeedrun/foldables/LzEndo.scala index c28504a..72d00a6 100644 --- a/src/main/scala/fpspeedrun/foldables/LzEndo.scala +++ b/src/main/scala/fpspeedrun/foldables/LzEndo.scala @@ -5,5 +5,14 @@ import cats.{Eval, Monoid} final case class LzEndo[A](run: Eval[A] => Eval[A]) extends AnyVal object LzEndo { - implicit def endoInstance[A]: Monoid[LzEndo[A]] with Delay[LzEndo[A]] = ??? + implicit def endoInstance[A]: Monoid[LzEndo[A]] with Delay[LzEndo[A]] = + new Monoid[LzEndo[A]] with Delay[LzEndo[A]] { + override def empty: LzEndo[A] = LzEndo(identity) + + override def delay(x: => LzEndo[A]): LzEndo[A] = LzEndo(y => Eval.defer(x.run(y))) + + override def combine(x: LzEndo[A], y: LzEndo[A]): LzEndo[A] = { + LzEndo(ev => Eval.later(()).flatMap(_ => Eval.defer(y.run(Eval.defer(x.run(ev)))))) + } + } } diff --git a/src/main/scala/fpspeedrun/foldables/StringWrap.scala b/src/main/scala/fpspeedrun/foldables/StringWrap.scala new file mode 100644 index 0000000..631bdee --- /dev/null +++ b/src/main/scala/fpspeedrun/foldables/StringWrap.scala @@ -0,0 +1,33 @@ +package fpspeedrun.foldables + +import cats.{Eval, Foldable, Monoid} +import fpspeedrun.foldables.LzEndo._ +import newts.Dual + +case class StringWrap[E](str: String)(implicit ev: Char =:= E) { + def foldMap[B: Monoid](f: E => B): B = { + str.map(ch => f(ev(ch))).foldLeft(Monoid[B].empty)(Monoid[B].combine) + } +} + +object StringWrap { + implicit def foldable = new Foldable[StringWrap[?]] { + + override def foldLeft[A, B](fa: StringWrap[A], b: B)(f: (B, A) => B): B = { + fa.foldMap[LzEndo[B]](a => LzEndo[B](_.map(f(_, a)))).run(Eval.now(b)).value + } + + override def foldRight[A, B](fa: StringWrap[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = { + fa.foldMap[Dual[LzEndo[B]]](a => Dual(LzEndo(ev => f(a, ev)))).getDual.run(lb) + } + } +} + + +//object Main3 extends App { +// import StringWrap._ +// import cats.syntax.foldable._ +// +// require(StringWrap("asdqwe").foldLeft("A") { (acc, v) => v + acc } == "ewqdsaA") +// require(StringWrap("asdqwe").foldRight(Eval.now("A")) { (v, acc) => acc.map(_ + v) }.value == "Aewqdsa") +//} diff --git a/src/main/scala/fpspeedrun/foldables/streams.scala b/src/main/scala/fpspeedrun/foldables/streams.scala index b5cd011..8aa93a4 100644 --- a/src/main/scala/fpspeedrun/foldables/streams.scala +++ b/src/main/scala/fpspeedrun/foldables/streams.scala @@ -1,26 +1,76 @@ package fpspeedrun.foldables -import cats.Foldable +import cats.{Eval, Foldable} +import cats.syntax.foldable._ final case class Filter[F[_], A](fa: F[A], p: A => Boolean) object Filter { - implicit def foldable[F[_]: Foldable]: Foldable[Filter[F, ?]] = ??? + implicit def foldable[F[_]: Foldable]: Foldable[Filter[F, ?]] = new Foldable[Filter[F, ?]] { + override def foldLeft[A, B](fa: Filter[F, A], b: B)(f: (B, A) => B): B = { + fa.fa.foldLeft(b)((acc, el) => if (fa.p(el)) f(acc, el) else acc) + } + override def foldRight[A, B](fa: Filter[F, A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = { + fa.fa.foldRight(lb)((el, acc) => if (fa.p(el)) f(el, acc) else acc) + } + } } final case class Map[F[_], A, B](fa: F[A], p: A => B) object Map { - implicit def foldable[F[_]: Foldable, A]: Foldable[Map[F, A, ?]] = ??? + implicit def foldable[F[_]: Foldable, X]: Foldable[Map[F, X, ?]] = new Foldable[Map[F, X, ?]] { + override def foldLeft[A, B](fa: Map[F, X, A], b: B)(f: (B, A) => B): B = { + fa.fa.foldLeft(b) { (acc, el) => f(acc, fa.p(el)) } + } + + override def foldRight[A, B](fa: Map[F, X, A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = { + fa.fa.foldRight(lb) { (el, acc) => f(fa.p(el), acc) } + } + } } final case class Collect[F[_], A, B](fa: F[A], pf: PartialFunction[A, B]) object Collect { - implicit def foldable[F[_]: Foldable, A]: Foldable[Collect[F, A, ?]] = ??? + implicit def foldable[F[_]: Foldable, X]: Foldable[Collect[F, X, ?]] = new Foldable[Collect[F, X, ?]] { + override def foldLeft[A, B](fa: Collect[F, X, A], b: B)(f: (B, A) => B): B = { + fa.fa.foldLeft(b) { (acc, el) => fa.pf.lift(el).fold(acc)(f(acc, _)) } + } + + override def foldRight[A, B](fa: Collect[F, X, A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = { + fa.fa.foldRight(lb){ (el, acc) => fa.pf.lift(el).fold(acc)(f(_, acc)) } + } + } } -final case class FlatMap[F[_], A, G[_], B](fa: F[A], f: A => G[B]) +final case class FlatMap[F[_], X, G[_], A](fa: F[X], f: X => G[A]) object FlatMap { - implicit def foldable[F[_]: Foldable, A, G[_]: Foldable]: Foldable[FlatMap[F, A, G, ?]] = ??? + implicit def foldable[F[_]: Foldable, X, G[_]: Foldable]: Foldable[FlatMap[F, X, G, ?]] = new Foldable[FlatMap[F, X, G, ?]] { + override def foldLeft[A, B](fa: FlatMap[F, X, G, A], b: B)(f: (B, A) => B): B = { + fa.fa.foldLeft(b)((acc, x) => fa.f(x).foldLeft(acc)(f)) + } + + override def foldRight[A, B](fa: FlatMap[F, X, G, A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = { + fa.fa.foldRight(lb)((x, acc) => fa.f(x).foldRight(acc)(f)) + } + } +} + +object Main extends App { + import Collect._ + import FlatMap._ + + import cats.instances.stream._ + import cats.instances.list._ + + val pf: PartialFunction[Int, Int] = { case x: Int if x > 3 => x } + val collect: Collect[List, Int, Int] = Collect(List(1,2,3,4,5), pf) + require(Iter(collect).iterator.toStream.foldl(0)(_ + _) == 9) + require(Iter(collect).iterator.toStream.foldr(Eval.now(0))((x, acc) => acc.map(_ + x)).value == 9) + + val f = (x: Int) => List[Int](x, x) + val flatMap = FlatMap[List, Int, List, Int](List(1,2,3,4,5), f) + require(Iter(flatMap).iterator.toStream.foldl(0)(_ + _) == 30) + require(Iter(flatMap).iterator.toStream.foldr(Eval.now(0))((x, acc) => acc.map(_ + x)).value == 30) } diff --git a/src/test/scala/fpspeedrun/foldables/FoldMapDelayApp.scala b/src/test/scala/fpspeedrun/foldables/FoldMapDelayApp.scala index 59531bf..2b15ea8 100644 --- a/src/test/scala/fpspeedrun/foldables/FoldMapDelayApp.scala +++ b/src/test/scala/fpspeedrun/foldables/FoldMapDelayApp.scala @@ -23,8 +23,9 @@ object FoldMapDelayApp { .value def main(args: Array[String]): Unit = { -// println(sum(Stream.range[BigInt](0, 100001))) -// println(sumN(5)(Stream.range(0, 11).map{x => println(x); x * 2 + 1})) -// println(sumN(10001)(Stream.from(0))) + println(sum(Stream.range[BigInt](0, 100001))) + println(sumN(5)(Stream.range(0, 11).map{x => println(x); x * 2 + 1})) + println(sumN(10001)(Stream.from(0))) + println(Stream.range(0, 11).foldl(0)((acc, x) => acc + x)) } }