Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make NonEmptyList covariant #1424

Merged
merged 1 commit into from
Jan 6, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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