From aa8fea732510218ed5e7255391e722924e66a088 Mon Sep 17 00:00:00 2001 From: Deepu Puthrote Date: Sat, 11 Jun 2016 16:16:30 +0100 Subject: [PATCH 1/6] NonEmptyList work in progress --- .../main/scala/cats/data/NonEmptyList.scala | 68 ++++++++ core/src/main/scala/cats/data/package.scala | 13 +- .../scala/cats/tests/NonEmptyListTests.scala | 165 ++++++++++++++++++ 3 files changed, 240 insertions(+), 6 deletions(-) create mode 100644 core/src/main/scala/cats/data/NonEmptyList.scala create mode 100644 tests/src/test/scala/cats/tests/NonEmptyListTests.scala diff --git a/core/src/main/scala/cats/data/NonEmptyList.scala b/core/src/main/scala/cats/data/NonEmptyList.scala new file mode 100644 index 0000000000..c2fb91f422 --- /dev/null +++ b/core/src/main/scala/cats/data/NonEmptyList.scala @@ -0,0 +1,68 @@ +package cats +package data + +import cats.std.list._ +//it needs a Functor +//it needs semigroup - combine, filter + +/** + * A data type which represents a single element (head) and some other + * structure (tail). + */ +final case class NonEmptyList[A](head: A, tail: List[A]) { + + /** + * Return the head and tail into a single list + */ + def unwrap: List[A] = head :: tail + + /** + * remove elements not matching the predicate + */ + def filter(p: A => Boolean): List[A] = { + val rest = tail.filter(p) + if(p(head)) head::rest else rest + } + + /** + * Append another NonEmptyList + */ + def combine(other: NonEmptyList[A]):NonEmptyList[A] = + NonEmptyList(head, MonadCombine[List].combineK(tail, other.head::other.tail)) + + /** + * Find the first element matching the predicate, if one exists + */ + def find(p:A=>Boolean): Option[A] = + if(p(head)) Some(head) else tail.find(p) + + /** + * Check whether at least one element satisfies the predicate + */ + def exists(p: A => Boolean): Boolean = + p(head) || tail.exists(p) + + /** + * Check whether all elements satisfy the predicate + */ + def forall(p: A => Boolean): Boolean = + p(head) && tail.exists(p) + + /** + * Left-associative fold on the structure using f. + */ + def foldLeft[B](b: B)(f: (B, A) => B): B = + (head::tail).foldLeft(b)(f) + + /** + * Right-associative fold on the structure using f. + */ + def foldRight[B](lb: Eval[B])(f: (A, Eval[B]) => Eval[B]):Eval[B] = + (head::tail).foldRight(lb)(f) + + /** + * Applies f to all the elements of the structure + */ + def map[B](f: A => B):NonEmptyList[B] = + NonEmptyList(f(head), tail.map(f)) +} diff --git a/core/src/main/scala/cats/data/package.scala b/core/src/main/scala/cats/data/package.scala index de3c882952..f4b92362d9 100644 --- a/core/src/main/scala/cats/data/package.scala +++ b/core/src/main/scala/cats/data/package.scala @@ -1,15 +1,15 @@ package cats package object data { - type NonEmptyList[A] = OneAnd[List, A] +// type NonEmptyList[A] = OneAnd[List, A] type NonEmptyVector[A] = OneAnd[Vector, A] type NonEmptyStream[A] = OneAnd[Stream, A] type ValidatedNel[E, A] = Validated[NonEmptyList[E], A] - def NonEmptyList[A](head: A, tail: List[A] = Nil): NonEmptyList[A] = - OneAnd(head, tail) - def NonEmptyList[A](head: A, tail: A*): NonEmptyList[A] = - OneAnd[List, A](head, tail.toList) +// def NonEmptyList[A](head: A, tail: List[A] = Nil): NonEmptyList[A] = +// OneAnd(head, tail) +// def NonEmptyList[A](head: A, tail: A*): NonEmptyList[A] = +// OneAnd[List, A](head, tail.toList) def NonEmptyVector[A](head: A, tail: Vector[A] = Vector.empty): NonEmptyVector[A] = OneAnd(head, tail) @@ -21,6 +21,7 @@ package object data { def NonEmptyStream[A](head: A, tail: A*): NonEmptyStream[A] = OneAnd(head, tail.toStream) + /* object NonEmptyList { def fromReducible[F[_], A](fa: F[A])(implicit F: Reducible[F]): Eval[NonEmptyList[A]] = F.reduceRightTo(fa)(a => NonEmptyList(a, Nil)) { (a, lnel) => @@ -32,7 +33,7 @@ package object data { case (h :: t) => Some(OneAnd(h, t)) case Nil => None } - } + }*/ type ReaderT[F[_], A, B] = Kleisli[F, A, B] val ReaderT = Kleisli diff --git a/tests/src/test/scala/cats/tests/NonEmptyListTests.scala b/tests/src/test/scala/cats/tests/NonEmptyListTests.scala new file mode 100644 index 0000000000..df2d272d5a --- /dev/null +++ b/tests/src/test/scala/cats/tests/NonEmptyListTests.scala @@ -0,0 +1,165 @@ +package cats +package tests + +import cats.kernel.laws.{GroupLaws, OrderLaws} + +import cats.data.{NonEmptyList, NonEmptyList} +import cats.laws.discipline.{ComonadTests, FunctorTests, SemigroupKTests, FoldableTests, MonadTests, SerializableTests, CartesianTests, TraverseTests, ReducibleTests} +import cats.laws.discipline.arbitrary._ + +class NonEmptyListTests extends CatsSuite { + // Lots of collections here.. telling ScalaCheck to calm down a bit + implicit override val generatorDrivenConfig: PropertyCheckConfiguration = + PropertyCheckConfig(maxSize = 5, minSuccessful = 20) + + checkAll("NonEmptyList[List, Int]", OrderLaws[NonEmptyList[List, Int]].eqv) + + checkAll("NonEmptyList[List, Int] with Option", TraverseTests[NonEmptyList[List, ?]].traverse[Int, Int, Int, Int, Option, Option]) + checkAll("Traverse[NonEmptyList[List, A]]", SerializableTests.serializable(Traverse[NonEmptyList[List, ?]])) + + checkAll("NonEmptyList[List, Int]", ReducibleTests[NonEmptyList[List, ?]].reducible[Option, Int, Int]) + checkAll("Reducible[NonEmptyList[List, ?]]", SerializableTests.serializable(Reducible[NonEmptyList[List, ?]])) + + implicit val iso = CartesianTests.Isomorphisms.invariant[NonEmptyList[ListWrapper, ?]](NonEmptyList.catsDataFunctorForNonEmptyList(ListWrapper.functor)) + + // Test instances that have more general constraints + { + implicit val monadCombine = ListWrapper.monadCombine + checkAll("NonEmptyList[ListWrapper, Int]", CartesianTests[NonEmptyList[ListWrapper, ?]].cartesian[Int, Int, Int]) + checkAll("Cartesian[NonEmptyList[ListWrapper, A]]", SerializableTests.serializable(Cartesian[NonEmptyList[ListWrapper, ?]])) + } + + { + implicit val functor = ListWrapper.functor + checkAll("NonEmptyList[ListWrapper, Int]", FunctorTests[NonEmptyList[ListWrapper, ?]].functor[Int, Int, Int]) + checkAll("Functor[NonEmptyList[ListWrapper, A]]", SerializableTests.serializable(Functor[NonEmptyList[ListWrapper, ?]])) + } + + { + implicit val monadCombine = ListWrapper.monadCombine + checkAll("NonEmptyList[ListWrapper, Int]", SemigroupKTests[NonEmptyList[ListWrapper, ?]].semigroupK[Int]) + checkAll("NonEmptyList[List, Int]", GroupLaws[NonEmptyList[List, Int]].semigroup) + checkAll("SemigroupK[NonEmptyList[ListWrapper, A]]", SerializableTests.serializable(SemigroupK[NonEmptyList[ListWrapper, ?]])) + checkAll("Semigroup[NonEmptyList[Int]]", SerializableTests.serializable(Semigroup[NonEmptyList[List, Int]])) + } + + { + implicit val foldable = ListWrapper.foldable + checkAll("NonEmptyList[ListWrapper, Int]", FoldableTests[NonEmptyList[ListWrapper, ?]].foldable[Int, Int]) + checkAll("Foldable[NonEmptyList[ListWrapper, A]]", SerializableTests.serializable(Foldable[NonEmptyList[ListWrapper, ?]])) + } + + { + // Test functor and subclasses don't have implicit conflicts + implicitly[Functor[NonEmptyList]] + implicitly[Monad[NonEmptyList]] + implicitly[Comonad[NonEmptyList]] + } + + implicit val iso2 = CartesianTests.Isomorphisms.invariant[NonEmptyList[List, ?]] + + checkAll("NonEmptyList[Int]", MonadTests[NonEmptyList].monad[Int, Int, Int]) + checkAll("Monad[NonEmptyList[A]]", SerializableTests.serializable(Monad[NonEmptyList])) + + checkAll("NonEmptyList[Int]", ComonadTests[NonEmptyList].comonad[Int, Int, Int]) + checkAll("Comonad[NonEmptyList[A]]", SerializableTests.serializable(Comonad[NonEmptyList])) + + test("Show is not empty and is formatted as expected") { + forAll { (nel: NonEmptyList[Int]) => + nel.show.nonEmpty should === (true) + nel.show.startsWith("NonEmptyList(") should === (true) + nel.show should === (implicitly[Show[NonEmptyList[Int]]].show(nel)) + nel.show.contains(nel.head.show) should === (true) + } + } + + test("Show is formatted correctly") { + val nonEmptyList = NonEmptyList("Test", Nil) + nonEmptyList.show should === ("NonEmptyList(Test, List())") + } + + test("Creating NonEmptyList + unwrap is identity") { + forAll { (i: Int, tail: List[Int]) => + val list = i :: tail + val nonEmptyList = NonEmptyList(i, tail: _*) + list should === (nonEmptyList.unwrap) + } + } + + test("NonEmptyList#filter is consistent with List#filter") { + forAll { (nel: NonEmptyList[Int], p: Int => Boolean) => + val list = nel.unwrap + nel.filter(p) should === (list.filter(p)) + } + } + + test("NonEmptyList#find is consistent with List#find") { + forAll { (nel: NonEmptyList[Int], p: Int => Boolean) => + val list = nel.unwrap + nel.find(p) should === (list.find(p)) + } + } + + test("NonEmptyList#exists is consistent with List#exists") { + forAll { (nel: NonEmptyList[Int], p: Int => Boolean) => + val list = nel.unwrap + nel.exists(p) should === (list.exists(p)) + } + } + + test("NonEmptyList#forall is consistent with List#forall") { + forAll { (nel: NonEmptyList[Int], p: Int => Boolean) => + val list = nel.unwrap + nel.forall(p) should === (list.forall(p)) + } + } + + test("NonEmptyList#map is consistent with List#map") { + forAll { (nel: NonEmptyList[Int], p: Int => String) => + val list = nel.unwrap + nel.map(p).unwrap should === (list.map(p)) + } + } + + test("reduceLeft consistent with foldLeft") { + forAll { (nel: NonEmptyList[Int], f: (Int, Int) => Int) => + nel.reduceLeft(f) should === (nel.tail.foldLeft(nel.head)(f)) + } + } + + test("reduceRight consistent with foldRight") { + forAll { (nel: NonEmptyList[Int], f: (Int, Eval[Int]) => Eval[Int]) => + nel.reduceRight(f).value should === (nel.tail.foldRight(nel.head)((a, b) => f(a, Now(b)).value)) + } + } + + test("reduce consistent with fold") { + forAll { (nel: NonEmptyList[Int]) => + nel.reduce should === (nel.fold) + } + } + + test("reduce consistent with reduceK") { + forAll { (nel: NonEmptyList[Option[Int]]) => + nel.reduce(SemigroupK[Option].algebra[Int]) should === (nel.reduceK) + } + } + + test("reduceLeftToOption consistent with foldLeft + Option") { + forAll { (nel: NonEmptyList[Int], f: Int => String, g: (String, Int) => String) => + val expected = nel.tail.foldLeft(Option(f(nel.head))) { (opt, i) => + opt.map(s => g(s, i)) + } + nel.reduceLeftToOption(f)(g) should === (expected) + } + } + + test("reduceRightToOption consistent with foldRight + Option") { + forAll { (nel: NonEmptyList[Int], f: Int => String, g: (Int, Eval[String]) => Eval[String]) => + val expected = nel.tail.foldRight(Option(f(nel.head))) { (i, opt) => + opt.map(s => g(i, Now(s)).value) + } + nel.reduceRightToOption(f)(g).value should === (expected) + } + } +} From 70187da4fa57d68c92353696208a4b828a31bfc0 Mon Sep 17 00:00:00 2001 From: Deepu Puthrote Date: Mon, 13 Jun 2016 14:39:20 +0100 Subject: [PATCH 2/6] Added instances for NonEmptyList #1087 work in progress --- .../main/scala/cats/data/NonEmptyList.scala | 153 ++++++++++++++---- 1 file changed, 122 insertions(+), 31 deletions(-) diff --git a/core/src/main/scala/cats/data/NonEmptyList.scala b/core/src/main/scala/cats/data/NonEmptyList.scala index c2fb91f422..53f59d6ef6 100644 --- a/core/src/main/scala/cats/data/NonEmptyList.scala +++ b/core/src/main/scala/cats/data/NonEmptyList.scala @@ -6,63 +6,154 @@ import cats.std.list._ //it needs semigroup - combine, filter /** - * A data type which represents a single element (head) and some other - * structure (tail). - */ -final case class NonEmptyList[A](head: A, tail: List[A]) { + * A data type which represents a single element (head) and some other + * structure (tail). + */ +final case class NonEmptyList[A](head: A, tail: List[A] = List[A]()) { /** - * Return the head and tail into a single list - */ + * Return the head and tail into a single list + */ def unwrap: List[A] = head :: tail /** - * remove elements not matching the predicate - */ + * remove elements not matching the predicate + */ def filter(p: A => Boolean): List[A] = { val rest = tail.filter(p) - if(p(head)) head::rest else rest + if (p(head)) head :: rest else rest } /** - * Append another NonEmptyList - */ - def combine(other: NonEmptyList[A]):NonEmptyList[A] = - NonEmptyList(head, MonadCombine[List].combineK(tail, other.head::other.tail)) + * Append another NonEmptyList + */ + def combine(other: NonEmptyList[A]): NonEmptyList[A] = + NonEmptyList(head, MonadCombine[List].combineK(tail, other.head :: other.tail)) /** - * Find the first element matching the predicate, if one exists - */ - def find(p:A=>Boolean): Option[A] = - if(p(head)) Some(head) else tail.find(p) + * Find the first element matching the predicate, if one exists + */ + def find(p: A => Boolean): Option[A] = + if (p(head)) Some(head) else tail.find(p) /** - * Check whether at least one element satisfies the predicate - */ + * Check whether at least one element satisfies the predicate + */ def exists(p: A => Boolean): Boolean = p(head) || tail.exists(p) /** - * Check whether all elements satisfy the predicate - */ + * Check whether all elements satisfy the predicate + */ def forall(p: A => Boolean): Boolean = p(head) && tail.exists(p) /** - * Left-associative fold on the structure using f. - */ + * Left-associative fold on the structure using f. + */ def foldLeft[B](b: B)(f: (B, A) => B): B = - (head::tail).foldLeft(b)(f) + (head :: tail).foldLeft(b)(f) /** - * Right-associative fold on the structure using f. - */ - def foldRight[B](lb: Eval[B])(f: (A, Eval[B]) => Eval[B]):Eval[B] = - (head::tail).foldRight(lb)(f) + * Right-associative fold on the structure using f. + */ + def foldRight[B](lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = + (head :: tail).foldRight(lb)(f) /** - * Applies f to all the elements of the structure - */ - def map[B](f: A => B):NonEmptyList[B] = + * Applies f to all the elements of the structure + */ + def map[B](f: A => B): NonEmptyList[B] = NonEmptyList(f(head), tail.map(f)) } + +private[data] sealed trait NonEmptyListInstances extends NonEmptyListLowPriority2 { + + implicit def catsDataEqForNonEmptyList[A](implicit A: Eq[A]): Eq[NonEmptyList[A]] = + new Eq[NonEmptyList[A]]{ + def eqv(x: NonEmptyList[A], y: NonEmptyList[A]): Boolean = x === y + } + + implicit def catsDataShowForNonEmptyList[A](implicit A: Show[A]): Show[NonEmptyList[A]] = + Show.show[NonEmptyList[A]](_.show) + + implicit def catsDataSemigroupKForNonEmptyList[A]: SemigroupK[NonEmptyList[?]] = + new SemigroupK[NonEmptyList[?]] { + def combineK[A](a: NonEmptyList[A], b: NonEmptyList[A]): NonEmptyList[A] = + a combine b + } + + implicit def catsDataSemigroupForNonEmptyList[A]: Semigroup[NonEmptyList[A]] = + catsDataSemigroupKForNonEmptyList[F].algebra + + implicit def catsDataReducibleForNonEmptyList[A]: Reducible[NonEmptyList[?]] = + new NonEmptyReducible[NonEmptyList[?]] { + override def split[A](fa: NonEmptyList[A]): (A, F[A]) = (fa.head, fa.tail) + } + + implicit def catsDataMonadForNonEmptyList[A]: Monad[NonEmptyList[?]] = + new Monad[NonEmptyList[?]] { + override def map[A, B](fa: NonEmptyList[F, A])(f: A => B): NonEmptyList[F, B] = + fa map f + + def pure[A](x: A): NonEmptyList[F, A] = + NonEmptyList(x, monad.empty) + + def flatMap[A, B](fa: NonEmptyList[F, A])(f: A => NonEmptyList[F, B]): NonEmptyList[F, B] = { + val end = monad.flatMap(fa.tail) { a => + val fa = f(a) + monad.combineK(monad.pure(fa.head), fa.tail) + } + val fst = f(fa.head) + NonEmptyList(fst.head, monad.combineK(fst.tail, end)) + } + } +} + +trait NonEmptyListLowPriority0 { + implicit val nelComonad: Comonad[NonEmptyList[List, ?]] = + new Comonad[NonEmptyList[List, ?]] { + def coflatMap[A, B](fa: NonEmptyList[List, A])(f: NonEmptyList[List, A] => B): NonEmptyList[List, B] = { + @tailrec def consume(as: List[A], buf: ListBuffer[B]): List[B] = + as match { + case Nil => buf.toList + case a :: as => consume(as, buf += f(NonEmptyList(a, as))) + } + NonEmptyList(f(fa), consume(fa.tail, ListBuffer.empty)) + } + + def extract[A](fa: NonEmptyList[List, A]): A = + fa.head + + def map[A, B](fa: NonEmptyList[List, A])(f: A => B): NonEmptyList[List, B] = + fa map f + } +} + +trait NonEmptyListLowPriority1 extends NonEmptyListLowPriority0 { + implicit def catsDataFunctorForNonEmptyList[F[_]](implicit F: Functor[F]): Functor[NonEmptyList[F, ?]] = + new Functor[NonEmptyList[F, ?]] { + def map[A, B](fa: NonEmptyList[F, A])(f: A => B): NonEmptyList[F, B] = + fa map f + } + +} + +trait NonEmptyListLowPriority2 extends NonEmptyListLowPriority1 { + implicit def catsDataTraverseForNonEmptyList[F[_]](implicit F: Traverse[F]): Traverse[NonEmptyList[F, ?]] = + new Traverse[NonEmptyList[F, ?]] { + def traverse[G[_], A, B](fa: NonEmptyList[F, A])(f: (A) => G[B])(implicit G: Applicative[G]): G[NonEmptyList[F, B]] = { + G.map2Eval(f(fa.head), Always(F.traverse(fa.tail)(f)))(NonEmptyList(_, _)).value + } + + def foldLeft[A, B](fa: NonEmptyList[F, A], b: B)(f: (B, A) => B): B = { + fa.foldLeft(b)(f) + } + + def foldRight[A, B](fa: NonEmptyList[F, A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = { + fa.foldRight(lb)(f) + } + } +} + +object NonEmptyList extends NonEmptyListInstances From 1c2adb17dc5dc23b973654c68db964a54daf0460 Mon Sep 17 00:00:00 2001 From: Deepu Puthrote Date: Mon, 13 Jun 2016 14:40:49 +0100 Subject: [PATCH 3/6] Review comment - rename unwrap to toList --- core/src/main/scala/cats/data/NonEmptyList.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/scala/cats/data/NonEmptyList.scala b/core/src/main/scala/cats/data/NonEmptyList.scala index 53f59d6ef6..08920bec43 100644 --- a/core/src/main/scala/cats/data/NonEmptyList.scala +++ b/core/src/main/scala/cats/data/NonEmptyList.scala @@ -14,7 +14,7 @@ final case class NonEmptyList[A](head: A, tail: List[A] = List[A]()) { /** * Return the head and tail into a single list */ - def unwrap: List[A] = head :: tail + def toList: List[A] = head :: tail /** * remove elements not matching the predicate From 4e8ffe1986f825fe4633f2733d96e5a3dfb8fccf Mon Sep 17 00:00:00 2001 From: Deepu Puthrote Date: Tue, 14 Jun 2016 00:02:40 +0100 Subject: [PATCH 4/6] Code review changes for #1087 - Use toList instead of head::tail - Use tail ::: other.toList, instead of MonadCombine - Define apply method instead of using default paramter for tail - Use Nil instead of monad.empty - Simplify find and filter --- .../main/scala/cats/data/NonEmptyList.scala | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/core/src/main/scala/cats/data/NonEmptyList.scala b/core/src/main/scala/cats/data/NonEmptyList.scala index 08920bec43..16119d10c8 100644 --- a/core/src/main/scala/cats/data/NonEmptyList.scala +++ b/core/src/main/scala/cats/data/NonEmptyList.scala @@ -1,15 +1,11 @@ package cats package data -import cats.std.list._ -//it needs a Functor -//it needs semigroup - combine, filter - /** - * A data type which represents a single element (head) and some other - * structure (tail). + * A data type which represents a non empty list of A, with + * single element (head) and optional structure (tail). */ -final case class NonEmptyList[A](head: A, tail: List[A] = List[A]()) { +final case class NonEmptyList[A](head: A, tail: List[A]) { /** * Return the head and tail into a single list @@ -19,22 +15,20 @@ final case class NonEmptyList[A](head: A, tail: List[A] = List[A]()) { /** * remove elements not matching the predicate */ - def filter(p: A => Boolean): List[A] = { - val rest = tail.filter(p) - if (p(head)) head :: rest else rest - } + def filter(p: A => Boolean): List[A] = + toList.filter(p) /** * Append another NonEmptyList */ def combine(other: NonEmptyList[A]): NonEmptyList[A] = - NonEmptyList(head, MonadCombine[List].combineK(tail, other.head :: other.tail)) + NonEmptyList(head, tail ::: other.toList) /** * Find the first element matching the predicate, if one exists */ def find(p: A => Boolean): Option[A] = - if (p(head)) Some(head) else tail.find(p) + toList.find(p) /** * Check whether at least one element satisfies the predicate @@ -52,13 +46,13 @@ final case class NonEmptyList[A](head: A, tail: List[A] = List[A]()) { * Left-associative fold on the structure using f. */ def foldLeft[B](b: B)(f: (B, A) => B): B = - (head :: tail).foldLeft(b)(f) + tail.foldLeft(f(b, head))(f) /** * Right-associative fold on the structure using f. */ def foldRight[B](lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = - (head :: tail).foldRight(lb)(f) + toList.foldRight(lb)(f) /** * Applies f to all the elements of the structure @@ -69,8 +63,10 @@ final case class NonEmptyList[A](head: A, tail: List[A] = List[A]()) { private[data] sealed trait NonEmptyListInstances extends NonEmptyListLowPriority2 { + def apply[A](head: A, tail: A*): NonEmptyList[A] = NonEmptyList(head, tail.toList) + implicit def catsDataEqForNonEmptyList[A](implicit A: Eq[A]): Eq[NonEmptyList[A]] = - new Eq[NonEmptyList[A]]{ + new Eq[NonEmptyList[A]] { def eqv(x: NonEmptyList[A], y: NonEmptyList[A]): Boolean = x === y } @@ -97,7 +93,7 @@ private[data] sealed trait NonEmptyListInstances extends NonEmptyListLowPriority fa map f def pure[A](x: A): NonEmptyList[F, A] = - NonEmptyList(x, monad.empty) + NonEmptyList(x, Nil) def flatMap[A, B](fa: NonEmptyList[F, A])(f: A => NonEmptyList[F, B]): NonEmptyList[F, B] = { val end = monad.flatMap(fa.tail) { a => From 9eea8e21234f6ba3e6e076fcf6e8ca55bfcd1217 Mon Sep 17 00:00:00 2001 From: Deepu Puthrote Date: Tue, 14 Jun 2016 00:20:43 +0100 Subject: [PATCH 5/6] Added TODOs based on review comments #1087 --- core/src/main/scala/cats/data/NonEmptyList.scala | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/src/main/scala/cats/data/NonEmptyList.scala b/core/src/main/scala/cats/data/NonEmptyList.scala index 16119d10c8..6a0f8c5774 100644 --- a/core/src/main/scala/cats/data/NonEmptyList.scala +++ b/core/src/main/scala/cats/data/NonEmptyList.scala @@ -56,6 +56,8 @@ final case class NonEmptyList[A](head: A, tail: List[A]) { /** * Applies f to all the elements of the structure + * TODO It would be nice to have variance on this particular method def map[AA <: A, B](f: AA => B): NonEmptyList[B] so that you can pass a function for a supertype of A into map. @yilinwei + * https://github.com/typelevel/cats/pull/1120#discussion-diff-66881573 */ def map[B](f: A => B): NonEmptyList[B] = NonEmptyList(f(head), tail.map(f)) @@ -95,6 +97,7 @@ private[data] sealed trait NonEmptyListInstances extends NonEmptyListLowPriority def pure[A](x: A): NonEmptyList[F, A] = NonEmptyList(x, Nil) + // TODO Could we move this method (and other type class methods) to NonEmptyList and then reference them in the instances? I think that will make the scaladoc for NonEmptyList a bit nicer. @non def flatMap[A, B](fa: NonEmptyList[F, A])(f: A => NonEmptyList[F, B]): NonEmptyList[F, B] = { val end = monad.flatMap(fa.tail) { a => val fa = f(a) @@ -102,6 +105,9 @@ private[data] sealed trait NonEmptyListInstances extends NonEmptyListLowPriority } val fst = f(fa.head) NonEmptyList(fst.head, monad.combineK(fst.tail, end)) + // TODO @yilinwei https://github.com/typelevel/cats/pull/1120#discussion_r66882139 + // val xs = f(head) ++ tail.flatMap(f.andThen(_.toList)) + // NonEmptyList(xs.head, xs.tail) } } } From 4acdc86925b404a6816f0faf1a894844a3ef4fdb Mon Sep 17 00:00:00 2001 From: Deepu Puthrote Date: Tue, 14 Jun 2016 00:23:19 +0100 Subject: [PATCH 6/6] Removed commented out code from package.scala #1087 --- core/src/main/scala/cats/data/package.scala | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/core/src/main/scala/cats/data/package.scala b/core/src/main/scala/cats/data/package.scala index f4b92362d9..e7cc91eada 100644 --- a/core/src/main/scala/cats/data/package.scala +++ b/core/src/main/scala/cats/data/package.scala @@ -1,16 +1,10 @@ package cats package object data { -// type NonEmptyList[A] = OneAnd[List, A] type NonEmptyVector[A] = OneAnd[Vector, A] type NonEmptyStream[A] = OneAnd[Stream, A] type ValidatedNel[E, A] = Validated[NonEmptyList[E], A] -// def NonEmptyList[A](head: A, tail: List[A] = Nil): NonEmptyList[A] = -// OneAnd(head, tail) -// def NonEmptyList[A](head: A, tail: A*): NonEmptyList[A] = -// OneAnd[List, A](head, tail.toList) - def NonEmptyVector[A](head: A, tail: Vector[A] = Vector.empty): NonEmptyVector[A] = OneAnd(head, tail) def NonEmptyVector[A](head: A, tail: A*): NonEmptyVector[A] = @@ -21,20 +15,6 @@ package object data { def NonEmptyStream[A](head: A, tail: A*): NonEmptyStream[A] = OneAnd(head, tail.toStream) - /* - object NonEmptyList { - def fromReducible[F[_], A](fa: F[A])(implicit F: Reducible[F]): Eval[NonEmptyList[A]] = - F.reduceRightTo(fa)(a => NonEmptyList(a, Nil)) { (a, lnel) => - lnel.map { case OneAnd(h, t) => OneAnd(a, h :: t) } - } - - def fromList[A](la: List[A]): Option[NonEmptyList[A]] = - la match { - case (h :: t) => Some(OneAnd(h, t)) - case Nil => None - } - }*/ - type ReaderT[F[_], A, B] = Kleisli[F, A, B] val ReaderT = Kleisli