Skip to content

Commit 3e41fc5

Browse files
committed
Make NonEmptyList covariant
1 parent ce0f24b commit 3e41fc5

File tree

2 files changed

+15
-14
lines changed

2 files changed

+15
-14
lines changed

core/src/main/scala/cats/data/NonEmptyList.scala

+14-13
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import scala.collection.mutable.ListBuffer
1212
* A data type which represents a non empty list of A, with
1313
* single element (head) and optional structure (tail).
1414
*/
15-
final case class NonEmptyList[A](head: A, tail: List[A]) {
15+
final case class NonEmptyList[+A](head: A, tail: List[A]) {
1616

1717
/**
1818
* Return the head and tail into a single list
@@ -25,13 +25,14 @@ final case class NonEmptyList[A](head: A, tail: List[A]) {
2525
def map[B](f: A => B): NonEmptyList[B] =
2626
NonEmptyList(f(head), tail.map(f))
2727

28-
def ++(l: List[A]): NonEmptyList[A] =
28+
def ++[AA >: A](l: List[AA]): NonEmptyList[AA] =
2929
NonEmptyList(head, tail ++ l)
3030

3131
def flatMap[B](f: A => NonEmptyList[B]): NonEmptyList[B] =
3232
f(head) ++ tail.flatMap(f andThen (_.toList))
3333

34-
def ::(a: A): NonEmptyList[A] = NonEmptyList(a, head :: tail)
34+
def ::[AA >: A](a: AA): NonEmptyList[AA] =
35+
NonEmptyList(a, head :: tail)
3536

3637
/**
3738
* remove elements not matching the predicate
@@ -45,7 +46,7 @@ final case class NonEmptyList[A](head: A, tail: List[A]) {
4546
/**
4647
* Append another NonEmptyList
4748
*/
48-
def concat(other: NonEmptyList[A]): NonEmptyList[A] =
49+
def concat[AA >: A](other: NonEmptyList[AA]): NonEmptyList[AA] =
4950
NonEmptyList(head, tail ::: other.toList)
5051

5152
/**
@@ -82,8 +83,8 @@ final case class NonEmptyList[A](head: A, tail: List[A]) {
8283
/**
8384
* Left-associative reduce using f.
8485
*/
85-
def reduceLeft(f: (A, A) => A): A =
86-
tail.foldLeft(head)(f)
86+
def reduceLeft[AA >: A](f: (AA, AA) => AA): AA =
87+
tail.foldLeft[AA](head)(f)
8788

8889
def traverse[G[_], B](f: A => G[B])(implicit G: Applicative[G]): G[NonEmptyList[B]] =
8990
G.map2Eval(f(head), Always(Traverse[List].traverse(tail)(f)))(NonEmptyList(_, _)).value
@@ -100,23 +101,23 @@ final case class NonEmptyList[A](head: A, tail: List[A]) {
100101
NonEmptyList(f(this), consume(tail))
101102
}
102103

103-
def ===(o: NonEmptyList[A])(implicit A: Eq[A]): Boolean =
104-
(this.head === o.head) && this.tail === o.tail
104+
def ===[AA >: A](o: NonEmptyList[AA])(implicit AA: Eq[AA]): Boolean =
105+
((this.head: AA) === o.head) && (this.tail: List[AA]) === o.tail
105106

106-
def show(implicit A: Show[A]): String =
107-
toList.iterator.map(A.show).mkString("NonEmptyList(", ", ", ")")
107+
def show[AA >: A](implicit AA: Show[AA]): String =
108+
toList.iterator.map(AA.show).mkString("NonEmptyList(", ", ", ")")
108109

109110
override def toString: String = s"NonEmpty$toList"
110111

111112
/**
112113
* Remove duplicates. Duplicates are checked using `Order[_]` instance.
113114
*/
114-
def distinct(implicit O: Order[A]): NonEmptyList[A] = {
115+
def distinct[AA >: A](implicit O: Order[AA]): NonEmptyList[A] = {
115116
implicit val ord = O.toOrdering
116117

117118
val buf = ListBuffer.empty[A]
118-
tail.foldLeft(TreeSet(head)) { (elementsSoFar, a) =>
119-
if (elementsSoFar(a)) elementsSoFar else { buf += a; elementsSoFar + a }
119+
tail.foldLeft(TreeSet[AA](head)) { (elementsSoFar, b) =>
120+
if (elementsSoFar(b)) elementsSoFar else { buf += b; elementsSoFar + b }
120121
}
121122

122123
NonEmptyList(head, buf.toList)

core/src/main/scala/cats/data/package.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package cats
22

33
package object data {
44
type NonEmptyStream[A] = OneAnd[Stream, A]
5-
type ValidatedNel[E, A] = Validated[NonEmptyList[E], A]
5+
type ValidatedNel[+E, +A] = Validated[NonEmptyList[E], A]
66

77
def NonEmptyStream[A](head: A, tail: Stream[A] = Stream.empty): NonEmptyStream[A] =
88
OneAnd(head, tail)

0 commit comments

Comments
 (0)