Skip to content
This repository was archived by the owner on Jun 10, 2021. It is now read-only.

Commit 507d8ca

Browse files
authored
Merge pull request #6 from danslapman/cats
Migrate to cats
2 parents 12bb932 + 4306dc1 commit 507d8ca

22 files changed

+460
-186
lines changed

build.sbt

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,35 @@ name := "asyncstreams"
22

33
organization := "danslapman"
44

5-
version := "0.5"
5+
version := "0.6-SNAPSHOT"
66

77
scalaVersion := "2.12.4"
88

99
crossScalaVersions := Seq("2.11.12", "2.12.4")
1010

11+
scalacOptions += "-Ypartial-unification"
12+
1113
parallelExecution in ThisBuild := false
1214

13-
addCompilerPlugin("org.spire-math" %% "kind-projector" % "0.9.4")
15+
addCompilerPlugin("org.spire-math" %% "kind-projector" % "0.9.5")
1416

1517
val versions = Map(
16-
"monix" -> "2.3.2"
18+
"monix" -> "3.0.0-M2",
19+
"cats" -> "1.0.0-RC1"
1720
)
1821

1922
libraryDependencies ++= Seq(
20-
"org.scalaz" %% "scalaz-core" % "7.2.16",
23+
"org.typelevel" %% "cats-core" % versions("cats"),
24+
"org.typelevel" %% "alleycats-core" % versions("cats"),
25+
"org.typelevel" %% "cats-mtl-core" % "0.1.0",
2126
"io.monix" %% "monix-eval" % versions("monix") % Test,
22-
"io.monix" %% "monix-scalaz-72" % versions("monix") % Test,
23-
"com.twitter" %% "util-core" % "7.1.0" % Test,
24-
"io.catbird" %% "catbird-util" % "0.18.0" % Test,
25-
"me.jeffshaw.harmony" %% "harmony_cats1-0-0-mf_scalaz7-2" % "2.0" % Test,
27+
"com.twitter" %% "util-core" % "17.11.0" % Test,
28+
"io.catbird" %% "catbird-util" % "0.21.0" % Test,
2629
"org.scalatest" %% "scalatest" % "3.0.4" % Test
2730
)
2831

2932
licenses += ("WTFPL", url("http://www.wtfpl.net"))
3033

3134
bintrayOrganization := Some("danslapman")
3235

33-
bintrayReleaseOnPublish in ThisBuild := false
36+
bintrayReleaseOnPublish in ThisBuild := false

project/build.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
sbt.version = 0.13.16
1+
sbt.version = 1.0.4
Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,60 @@
11
package asyncstreams
22

3-
import asyncstreams.typeclass.ZeroK
3+
import alleycats.EmptyK
4+
import cats.MonadError
5+
import cats.syntax.applicative._
6+
import cats.syntax.applicativeError._
7+
import cats.syntax.flatMap._
8+
import cats.syntax.functor._
49

510
import scala.language.higherKinds
6-
import scalaz.MonadError
711

812
trait ASImpl[F[+_]] {
913
def empty[A]: AsyncStream[F, A]
1014
def collectLeft[A, B](s: AsyncStream[F, A])(init: B)(f: (B, A) => B): F[B]
1115
def fromIterable[T](it: Iterable[T]): AsyncStream[F, T]
1216
def takeWhile[T](s: AsyncStream[F, T])(p: T => Boolean): AsyncStream[F, T]
1317
def isEmpty[T](s: AsyncStream[F, T]): F[Boolean]
18+
def find[T](s: AsyncStream[F, T], p: T => Boolean): F[Option[T]]
19+
def findF[T](s: AsyncStream[F, T], p: T => F[Boolean]): F[Option[T]]
1420
}
1521

16-
class ASImplForMonadError[F[+_]](implicit fmp: MonadError[F, Throwable], ze: ZeroK[F]) extends ASImpl[F] {
17-
import scalaz.syntax.monadError._
18-
19-
override def empty[A]: AsyncStream[F, A] = AsyncStream(ze.zero)
22+
class ASImplForMonadError[F[+_]](implicit fme: MonadError[F, Throwable], zk: EmptyK[F]) extends ASImpl[F] {
23+
override def empty[A]: AsyncStream[F, A] = AsyncStream(zk.empty)
2024

2125
override def collectLeft[A, B](s: AsyncStream[F, A])(init: B)(f: (B, A) => B): F[B] = {
2226
def impl(d: F[Step[A, AsyncStream[F, A]]], acc: F[B]): F[B] =
23-
d.flatMap(step => impl(step.rest.data, acc.map(b => f(b, step.value)))).handleError(_ => acc)
27+
d.flatMap(step => impl(step.rest.data, acc.map(b => f(b, step.value)))).handleErrorWith(_ => acc)
2428

25-
impl(s.data, init.point[F])
29+
impl(s.data, init.pure[F])
2630
}
2731

2832
override def fromIterable[T](it: Iterable[T]): AsyncStream[F, T] = AsyncStream {
29-
if (it.nonEmpty) Step(it.head, fromIterable(it.tail)).point[F] else ze.zero
33+
if (it.nonEmpty) Step(it.head, fromIterable(it.tail)).pure[F] else zk.empty
3034
}
3135

3236
override def takeWhile[T](s: AsyncStream[F, T])(p: (T) => Boolean): AsyncStream[F, T] = AsyncStream {
3337
s.data.flatMap {
34-
case step if !p(step.value) => ze.zero
35-
case step => Step(step.value, takeWhile(step.rest)(p)).point[F]
38+
case step if !p(step.value) => zk.empty
39+
case step => Step(step.value, takeWhile(step.rest)(p)).pure[F]
3640
}
3741
}
3842

39-
override def isEmpty[T](s: AsyncStream[F, T]): F[Boolean] = s.data.map(_ => false).handleError(_ => true.point[F])
43+
override def isEmpty[T](s: AsyncStream[F, T]): F[Boolean] = s.data.map(_ => false).handleErrorWith(_ => true.pure[F])
44+
45+
override def find[T](s: AsyncStream[F, T], p: T => Boolean): F[Option[T]] = {
46+
s.data.flatMap { s =>
47+
if (p(s.value)) Some(s.value).pure[F]
48+
else find(s.rest, p)
49+
}.handleErrorWith(_ => None.pure[F])
50+
}
51+
52+
override def findF[T](s: AsyncStream[F, T], p: T => F[Boolean]): F[Option[T]] = {
53+
s.data.flatMap { s =>
54+
p(s.value).flatMap {
55+
case true => Some(s.value).pure[F]
56+
case false => findF(s.rest, p)
57+
}
58+
}.handleErrorWith(_ => None.pure[F])
59+
}
4060
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package asyncstreams
2+
3+
import alleycats.EmptyK
4+
import cats.{Alternative, Monad, MonadError, StackSafeMonad}
5+
import cats.syntax.applicative._
6+
import cats.syntax.applicativeError._
7+
import cats.syntax.flatMap._
8+
import cats.syntax.functor._
9+
10+
import scala.language.higherKinds
11+
12+
class ASInstanceForMonadError[F[+_]](implicit fme: MonadError[F, Throwable], zk: EmptyK[F]) extends Monad[AsyncStream[F, ?]] with Alternative[AsyncStream[F, ?]] with StackSafeMonad[AsyncStream[F, ?]] {
13+
override def empty[A] = AsyncStream(zk.empty)
14+
15+
override def combineK[A](x: AsyncStream[F, A], y: AsyncStream[F, A]): AsyncStream[F, A] = AsyncStream {
16+
x.data.map(step => Step(step.value, combineK(step.rest, y))).handleErrorWith(_ => y.data)
17+
}
18+
19+
override def pure[A](x: A) = AsyncStream(Step(x, empty[A]).pure[F])
20+
21+
override def flatMap[A, B](fa: AsyncStream[F, A])(f: A => AsyncStream[F, B]): AsyncStream[F, B] = AsyncStream {
22+
fa.data.flatMap(step => f(step.value).data.map(step2 => Step(step2.value, combineK(step2.rest, flatMap(step.rest)(f)))))
23+
}
24+
}

src/main/scala/asyncstreams/ASMonadPlusForMonadError.scala

Lines changed: 0 additions & 21 deletions
This file was deleted.
Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,40 @@
11
package asyncstreams
22

3+
import cats.{Alternative, Monad}
4+
import cats.data.StateT
5+
import cats.mtl.MonadState
6+
import cats.syntax.applicative._
7+
import cats.syntax.flatMap._
8+
import cats.syntax.functor._
9+
310
import scala.language.higherKinds
4-
import scalaz.syntax.monad._
5-
import scalaz.{IndexedStateT, Monad, MonadPlus, MonadState, StateT}
611

712
class ASStateTOps[F[+_]: Monad](implicit methods: ASImpl[F]) {
813
def foreach[A, S](stream: AsyncStream[F, A])(f: A => StateT[F, S, _]): StateT[F, S, Unit] = StateT { s =>
9-
methods.collectLeft(stream)(s.point[F])((fS, a) => fS.flatMap(s2 => f(a)(s2).map(_._1)))
14+
methods.collectLeft(stream)(s.pure[F])((fS, a) => fS.flatMap(s2 => f(a).run(s2).map(_._1)))
1015
.flatMap(identity).map((_, ()))
1116
}
1217

1318
def isEmpty[A, S](stream: AsyncStream[F, A]): StateT[F, S, Boolean] = StateT { s =>
1419
stream.isEmpty.map((s, _))
1520
}
1621

17-
def isEmpty[A, S](f: S => AsyncStream[F, A])(implicit ms: MonadState[IndexedStateT[F, S, S, ?], S]): StateT[F, S, Boolean] = {
22+
def isEmpty[A, S](f: S => AsyncStream[F, A])(implicit ms: MonadState[StateT[F, S, ?], S]): StateT[F, S, Boolean] = {
1823
ms.get >>= ((s: S) => isEmpty(f(s)))
1924
}
2025

2126
def notEmpty[A, S](stream: AsyncStream[F, A]): StateT[F, S, Boolean] = StateT { s =>
2227
stream.nonEmpty.map((s, _))
2328
}
2429

25-
def notEmpty[A, S](f: S => AsyncStream[F, A])(implicit ms: MonadState[IndexedStateT[F, S, S, ?], S]): StateT[F, S, Boolean] = {
30+
def notEmpty[A, S](f: S => AsyncStream[F, A])(implicit ms: MonadState[StateT[F, S, ?], S]): StateT[F, S, Boolean] = {
2631
ms.get >>= ((s: S) => notEmpty(f(s)))
2732
}
2833

2934
def get[A, S](stream: AsyncStream[F, A]): StateT[F, S, (AsyncStream[F, A], A)] = StateT { s =>
3035
stream.data.map(step => (s, (step.rest, step.value)))
3136
}
3237

33-
def genS[S, A](start: S)(gen: StateT[F, S, A])(implicit smp: MonadPlus[AsyncStream[F, ?]]): AsyncStream[F, A] =
38+
def genS[S, A](start: S)(gen: StateT[F, S, A])(implicit alt: Alternative[AsyncStream[F, ?]]): AsyncStream[F, A] =
3439
AsyncStream.generate(start)(gen.run)
3540
}
Lines changed: 75 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,110 @@
11
package asyncstreams
22

3+
import cats.kernel.Monoid
4+
import cats.{Alternative, Applicative, Monad}
5+
import cats.syntax.applicative._
6+
import cats.syntax.flatMap._
7+
import cats.syntax.functor._
8+
import cats.syntax.semigroupk._
9+
310
import scala.annotation.unchecked.{uncheckedVariance => uV}
411
import scala.collection.GenIterable
512
import scala.collection.generic.CanBuildFrom
613
import scala.language.higherKinds
7-
import scalaz.syntax.monadPlus._
8-
import scalaz.{Monad, MonadPlus}
9-
10-
class AsyncStream[F[+_]: Monad, A](val data: F[Step[A, AsyncStream[F, A]]]) {
11-
type SStep = Step[A, AsyncStream[F, A]]
1214

13-
def to[Col[_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]], methods: ASImpl[F]): F[Col[A]] =
14-
methods.collectLeft(this)(cbf())((col, el) => col += el).map(_.result())
15+
class AsyncStream[F[+_]: Monad, +A](private[asyncstreams] val data: F[Step[A, AsyncStream[F, A]]]) {
16+
def to[Col[+_]](implicit cbf: CanBuildFrom[Nothing, A, Col[A @uV]], impl: ASImpl[F]): F[Col[A]] =
17+
impl.collectLeft(this)(cbf())((col, el) => col += el).map(_.result())
1518

1619
def takeWhile(p: A => Boolean)(implicit impl: ASImpl[F]): AsyncStream[F, A] = impl.takeWhile(this)(p)
1720

18-
def take(n: Int)(implicit smp: MonadPlus[AsyncStream[F, ?]]): AsyncStream[F, A] =
19-
if (n <= 0) smp.empty
21+
def take(n: Int)(implicit alt: Alternative[AsyncStream[F, ?]]): AsyncStream[F, A] =
22+
if (n <= 0) alt.empty
2023
else AsyncStream {
2124
data.map(p => Step(p.value, p.rest.take(n - 1)))
2225
}
2326

24-
def foreach[U](f: (A) => U)(implicit methods: ASImpl[F]): F[Unit] =
25-
methods.collectLeft(this)(())((_: Unit, a: A) => {f(a); ()})
27+
def drop(n: Int): AsyncStream[F, A] =
28+
if (n <= 0) this
29+
else AsyncStream {
30+
data.flatMap(p => p.rest.drop(n - 1).data)
31+
}
32+
33+
def foreach[U](f: (A) => U)(implicit impl: ASImpl[F]): F[Unit] =
34+
impl.collectLeft(this)(())((_: Unit, a: A) => {f(a); ()})
2635

2736
def foreachF[U](f: (A) => F[U])(implicit impl: ASImpl[F]): F[Unit] =
28-
impl.collectLeft(this)(().point[F])((fu: F[Unit], a: A) => fu.flatMap(_ => f(a)).map(_ => ())).flatMap(identity)
37+
impl.collectLeft(this)(().pure[F])((fu: F[Unit], a: A) => fu.flatMap(_ => f(a)).map(_ => ())).flatMap(identity)
2938

30-
def flatten[B](implicit asIterable: A => GenIterable[B], smp: MonadPlus[AsyncStream[F, ?]], impl: ASImpl[F]): AsyncStream[F, B] = {
31-
def streamChunk(step: AsyncStream[F, A]#SStep): AsyncStream[F, B] =
39+
def flatten[B](implicit asIterable: A => GenIterable[B], alt: Alternative[AsyncStream[F, ?]], impl: ASImpl[F]): AsyncStream[F, B] = {
40+
def streamChunk(step: Step[A, AsyncStream[F, A]]): AsyncStream[F, B] =
3241
impl.fromIterable(asIterable(step.value).seq) <+> step.rest.flatten
3342

3443
AsyncStream(data.flatMap(step => streamChunk(step).data))
3544
}
3645

3746
def isEmpty(implicit impl: ASImpl[F]): F[Boolean] = impl.isEmpty(this)
3847
def nonEmpty(implicit impl: ASImpl[F]): F[Boolean] = impl.isEmpty(this).map(!_)
48+
49+
def map[B](f: A => B): AsyncStream[F, B] = AsyncStream {
50+
data.map(s => Step(f(s.value), s.rest.map(f)))
51+
}
52+
53+
def mapF[B](f: A => F[B]): AsyncStream[F, B] = AsyncStream {
54+
data.flatMap(s => f(s.value).map(nv => Step(nv, s.rest.mapF(f))))
55+
}
56+
57+
def flatMap[B](f: A => AsyncStream[F, B])(implicit alt: Alternative[AsyncStream[F, ?]]): AsyncStream[F, B] = AsyncStream {
58+
data.flatMap(s => (f(s.value) <+> s.rest.flatMap(f)).data)
59+
}
60+
61+
def filter(p: A => Boolean): AsyncStream[F, A] = AsyncStream {
62+
data.flatMap { s =>
63+
if (p(s.value)) Step(s.value, s.rest.filter(p)).pure[F]
64+
else s.rest.filter(p).data
65+
}
66+
}
67+
68+
def withFilter(p: A => Boolean): AsyncStream[F, A] = filter(p)
69+
70+
def find(p: A => Boolean)(implicit impl: ASImpl[F]): F[Option[A]] = impl.find(this, p)
71+
def findF(p: A => F[Boolean])(implicit impl: ASImpl[F]): F[Option[A]] = impl.findF(this, p)
72+
73+
def partition(p: A => Boolean): (AsyncStream[F, A], AsyncStream[F, A]) = (filter(p), filter(p.andThen(!_)))
74+
75+
def foldMap[B](f: A => B)(implicit impl: ASImpl[F], mb: Monoid[B]): F[B] = {
76+
impl.collectLeft(this)(mb.empty)((b, a) => mb.combine(b, f(a)))
77+
}
78+
79+
def zip[B](sb: AsyncStream[F, B]): AsyncStream[F, (A, B)] = AsyncStream {
80+
for {
81+
stepA <- data
82+
stepB <- sb.data
83+
} yield Step((stepA.value, stepB.value), stepA.rest zip stepB.rest)
84+
}
85+
86+
def zipWithIndex(implicit app: Applicative[AsyncStream[F, ?]]): AsyncStream[F, (A, Int)] =
87+
zip(AsyncStream.unfold(0)(_ + 1))
88+
89+
def foldLeft[B](init: B)(fold: (B, A) => B)(implicit impl: ASImpl[F]): F[B] =
90+
impl.collectLeft(this)(init)(fold)
3991
}
4092

4193
object AsyncStream {
42-
def apply[F[+_]: Monad, A](data: => F[Step[A, AsyncStream[F, A]]]): AsyncStream[F, A] = new AsyncStream(data)
94+
private[asyncstreams] def apply[F[+_]: Monad, A](data: => F[Step[A, AsyncStream[F, A]]]): AsyncStream[F, A] = new AsyncStream(data)
4395
def asyncNil[F[+_]: Monad, A](implicit impl: ASImpl[F]): AsyncStream[F, A] = impl.empty
4496

45-
private[asyncstreams] def generate[F[+_]: Monad, S, A](start: S)(gen: S => F[(S, A)])(implicit smp: MonadPlus[AsyncStream[F, ?]]): AsyncStream[F, A] = AsyncStream {
97+
private[asyncstreams] def generate[F[+_]: Monad, S, A](start: S)(gen: S => F[(S, A)])(implicit app: Applicative[AsyncStream[F, ?]]): AsyncStream[F, A] = AsyncStream {
4698
gen(start).map((stateEl: (S, A)) => Step(stateEl._2, generate(stateEl._1)(gen)))
4799
}
48100

49-
def unfold[F[+_]: Monad, T](start: T)(makeNext: T => T)(implicit smp: MonadPlus[AsyncStream[F, ?]]): AsyncStream[F, T] =
50-
generate(start)(s => (makeNext(s), s).point[F])
101+
def unfold[F[+_]: Monad, T](start: T)(makeNext: T => T)(implicit app: Applicative[AsyncStream[F, ?]]): AsyncStream[F, T] =
102+
generate(start)(s => (makeNext(s), s).pure[F])
103+
104+
def unfoldM[F[+_]: Monad, T](start: T)(makeNext: T => F[T])(implicit app: Applicative[AsyncStream[F, ?]]): AsyncStream[F, T] =
105+
generate(start)(s => makeNext(s).map(n => (n, s)))
51106

52-
implicit class AsyncStreamOps[F[+_]: Monad, A](stream: => AsyncStream[F, A]) {
53-
def ~::(el: A) = AsyncStream(Step(el, stream).point[F])
107+
def unfoldMM[F[+_]: Monad, T](start: F[T])(makeNext: T => F[T])(implicit app: Applicative[AsyncStream[F, ?]]): AsyncStream[F, T] = AsyncStream {
108+
start.flatMap(initial => generate(initial)(s => makeNext(s).map(n => (n, s))).data)
54109
}
55110
}

src/main/scala/asyncstreams/Implicits.scala

Lines changed: 0 additions & 18 deletions
This file was deleted.

src/main/scala/asyncstreams/MonadFilterForMonadError.scala

Lines changed: 0 additions & 20 deletions
This file was deleted.

0 commit comments

Comments
 (0)