Skip to content

Commit

Permalink
Merge pull request #1424 from edmundnoble/march-of-the-variance
Browse files Browse the repository at this point in the history
Make NonEmptyList covariant
  • Loading branch information
ceedubs authored Jan 6, 2017
2 parents 468e753 + a38a588 commit ec0af6e
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 35 deletions.
29 changes: 15 additions & 14 deletions core/src/main/scala/cats/data/NonEmptyList.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import scala.collection.mutable.ListBuffer
* 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]) {
final case class NonEmptyList[+A](head: A, tail: List[A]) {

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

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

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

def ::(a: A): NonEmptyList[A] = NonEmptyList(a, head :: tail)
def ::[AA >: A](a: AA): NonEmptyList[AA] =
NonEmptyList(a, head :: tail)

/**
* Remove elements not matching the predicate
Expand Down Expand Up @@ -68,7 +69,7 @@ final case class NonEmptyList[A](head: A, tail: List[A]) {
/**
* Append another NonEmptyList
*/
def concat(other: NonEmptyList[A]): NonEmptyList[A] =
def concat[AA >: A](other: NonEmptyList[AA]): NonEmptyList[AA] =
NonEmptyList(head, tail ::: other.toList)

/**
Expand Down Expand Up @@ -105,8 +106,8 @@ final case class NonEmptyList[A](head: A, tail: List[A]) {
/**
* Left-associative reduce using f.
*/
def reduceLeft(f: (A, A) => A): A =
tail.foldLeft(head)(f)
def reduceLeft[AA >: A](f: (AA, AA) => AA): AA =
tail.foldLeft[AA](head)(f)

def traverse[G[_], B](f: A => G[B])(implicit G: Applicative[G]): G[NonEmptyList[B]] =
G.map2Eval(f(head), Always(Traverse[List].traverse(tail)(f)))(NonEmptyList(_, _)).value
Expand All @@ -123,23 +124,23 @@ final case class NonEmptyList[A](head: A, tail: List[A]) {
NonEmptyList(f(this), consume(tail))
}

def ===(o: NonEmptyList[A])(implicit A: Eq[A]): Boolean =
(this.head === o.head) && this.tail === o.tail
def ===[AA >: A](o: NonEmptyList[AA])(implicit AA: Eq[AA]): Boolean =
((this.head: AA) === o.head) && (this.tail: List[AA]) === o.tail

def show(implicit A: Show[A]): String =
toList.iterator.map(A.show).mkString("NonEmptyList(", ", ", ")")
def show[AA >: A](implicit AA: Show[AA]): String =
toList.iterator.map(AA.show).mkString("NonEmptyList(", ", ", ")")

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

/**
* Remove duplicates. Duplicates are checked using `Order[_]` instance.
*/
def distinct(implicit O: Order[A]): NonEmptyList[A] = {
def distinct[AA >: A](implicit O: Order[AA]): NonEmptyList[AA] = {
implicit val ord = O.toOrdering

val buf = ListBuffer.empty[A]
tail.foldLeft(TreeSet(head)) { (elementsSoFar, a) =>
if (elementsSoFar(a)) elementsSoFar else { buf += a; elementsSoFar + a }
val buf = ListBuffer.empty[AA]
tail.foldLeft(TreeSet(head: AA)) { (elementsSoFar, b) =>
if (elementsSoFar(b)) elementsSoFar else { buf += b; elementsSoFar + b }
}

NonEmptyList(head, buf.toList)
Expand Down
41 changes: 21 additions & 20 deletions core/src/main/scala/cats/data/NonEmptyVector.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import cats.instances.vector._
* `NonEmptyVector`. However, due to https://issues.scala-lang.org/browse/SI-6601, on
* Scala 2.10, this may be bypassed due to a compiler bug.
*/
final class NonEmptyVector[A] private (val toVector: Vector[A]) extends AnyVal {
final class NonEmptyVector[+A] private (val toVector: Vector[A]) extends AnyVal {

/** Gets the element at the index, if it exists */
def get(i: Int): Option[A] =
Expand All @@ -22,15 +22,15 @@ final class NonEmptyVector[A] private (val toVector: Vector[A]) extends AnyVal {
def getUnsafe(i: Int): A = toVector(i)

/** Updates the element at the index, if it exists */
def updated(i: Int, a: A): Option[NonEmptyVector[A]] =
def updated[AA >: A](i: Int, a: AA): Option[NonEmptyVector[AA]] =
if (toVector.isDefinedAt(i)) Some(new NonEmptyVector(toVector.updated(i, a))) else None

/**
* Updates the element at the index, or throws an `IndexOutOfBoundsException`
* if none exists (if `i` does not satisfy `0 <= i < length`).
*/
def updatedUnsafe(i: Int, a: A):
NonEmptyVector[A] = new NonEmptyVector(toVector.updated(i, a))
def updatedUnsafe[AA >: A](i: Int, a: AA):
NonEmptyVector[AA] = new NonEmptyVector(toVector.updated(i, a))

def head: A = toVector.head

Expand Down Expand Up @@ -63,37 +63,37 @@ final class NonEmptyVector[A] private (val toVector: Vector[A]) extends AnyVal {
/**
* Alias for [[concat]]
*/
def ++(other: Vector[A]): NonEmptyVector[A] = concat(other)
def ++[AA >: A](other: Vector[AA]): NonEmptyVector[AA] = concat(other)

/**
* Append another `Vector` to this, producing a new `NonEmptyVector`.
*/
def concat(other: Vector[A]): NonEmptyVector[A] = new NonEmptyVector(toVector ++ other)
def concat[AA >: A](other: Vector[AA]): NonEmptyVector[AA] = new NonEmptyVector(toVector ++ other)

/**
* Append another `NonEmptyVector` to this, producing a new `NonEmptyVector`.
*/
def concatNev(other: NonEmptyVector[A]): NonEmptyVector[A] = new NonEmptyVector(toVector ++ other.toVector)
def concatNev[AA >: A](other: NonEmptyVector[AA]): NonEmptyVector[AA] = new NonEmptyVector(toVector ++ other.toVector)

/**
* Append an item to this, producing a new `NonEmptyVector`.
*/
def append(a: A): NonEmptyVector[A] = new NonEmptyVector(toVector :+ a)
def append[AA >: A](a: AA): NonEmptyVector[AA] = new NonEmptyVector(toVector :+ a)

/**
* Alias for [[append]]
*/
def :+(a: A): NonEmptyVector[A] = append(a)
def :+[AA >: A](a: AA): NonEmptyVector[AA] = append(a)

/**
* Prepend an item to this, producing a new `NonEmptyVector`.
*/
def prepend(a: A): NonEmptyVector[A] = new NonEmptyVector(a +: toVector)
def prepend[AA >: A](a: AA): NonEmptyVector[AA] = new NonEmptyVector(a +: toVector)

/**
* Alias for [[prepend]]
*/
def +:(a: A): NonEmptyVector[A] = prepend(a)
def +:[AA >: A](a: AA): NonEmptyVector[AA] = prepend(a)

/**
* Find the first element matching the predicate, if one exists
Expand Down Expand Up @@ -137,13 +137,13 @@ final class NonEmptyVector[A] private (val toVector: Vector[A]) extends AnyVal {
/**
* Left-associative reduce using f.
*/
def reduceLeft(f: (A, A) => A): A =
tail.foldLeft(head)(f)
def reduceLeft[AA >: A](f: (AA, AA) => AA): AA =
tail.foldLeft(head: AA)(f)

/**
* Reduce using the Semigroup of A
*/
def reduce(implicit S: Semigroup[A]): A =
def reduce[AA >: A](implicit S: Semigroup[AA]): AA =
S.combineAllOption(toVector).get

/**
Expand All @@ -154,7 +154,8 @@ final class NonEmptyVector[A] private (val toVector: Vector[A]) extends AnyVal {
* equality provided by Eq[_] instances, rather than using the
* universal equality provided by .equals.
*/
def ===(that: NonEmptyVector[A])(implicit A: Eq[A]): Boolean = Eq[Vector[A]].eqv(toVector, that.toVector)
def ===[AA >: A](that: NonEmptyVector[AA])(implicit A: Eq[AA]): Boolean =
Eq[Vector[AA]].eqv(toVector, that.toVector)

/**
* Typesafe stringification method.
Expand All @@ -163,8 +164,8 @@ final class NonEmptyVector[A] private (val toVector: Vector[A]) extends AnyVal {
* values according to Show[_] instances, rather than using the
* universal .toString method.
*/
def show(implicit A: Show[A]): String =
s"NonEmpty${Show[Vector[A]].show(toVector)}"
def show[AA >: A](implicit AA: Show[AA]): String =
s"NonEmpty${Show[Vector[AA]].show(toVector)}"

def length: Int = toVector.length

Expand All @@ -173,11 +174,11 @@ final class NonEmptyVector[A] private (val toVector: Vector[A]) extends AnyVal {
/**
* Remove duplicates. Duplicates are checked using `Order[_]` instance.
*/
def distinct(implicit O: Order[A]): NonEmptyVector[A] = {
def distinct[AA >: A](implicit O: Order[AA]): NonEmptyVector[AA] = {
implicit val ord = O.toOrdering

val buf = Vector.newBuilder[A]
tail.foldLeft(TreeSet(head)) { (elementsSoFar, a) =>
val buf = Vector.newBuilder[AA]
tail.foldLeft(TreeSet(head: AA)) { (elementsSoFar, a) =>
if (elementsSoFar(a)) elementsSoFar else { buf += a; elementsSoFar + a }
}

Expand Down
2 changes: 1 addition & 1 deletion core/src/main/scala/cats/data/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package cats

package object data {
type NonEmptyStream[A] = OneAnd[Stream, A]
type ValidatedNel[E, A] = Validated[NonEmptyList[E], A]
type ValidatedNel[+E, +A] = Validated[NonEmptyList[E], A]

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

0 comments on commit ec0af6e

Please sign in to comment.