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

Adding NonEmptyVector #1137

Merged
merged 11 commits into from
Jul 13, 2016
178 changes: 178 additions & 0 deletions core/src/main/scala/cats/data/NonEmptyVector.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
package cats
package data

import scala.annotation.tailrec
import scala.collection.immutable.VectorBuilder
import cats.std.vector._

/**
* A data type which represents a single element (head) and a Vector
* (tail). This can be used to represent a Vector which is guaranteed
* to not be empty.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This description should be changed, since we're just wrapping a vector.

*/
final case class NonEmptyVector[A] private (vector: Vector[A]) {

def unwrap: Vector[A] = vector
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since people can just call vector, I don't know if this alias is very helpful. We can probably remove it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could call the member .toVector so that nev.toVector would do the right thing.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to .toVector


def head: A = vector.head

def tail: Vector[A] = vector.tail

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like it's worth supporting apply and updated for this structure, since indexed access is one of the best reasons to want to be using a vector. I'd probably use the standard signatures but I could be persuaded to have them return Option[A] and Option[Unit] respectively.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or both? :-) But agree on wanting indexed access.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@non you mean Option[NonEmptyVector[A]] for updated right? :)

I'd be in favor of updated that returned an Option and updatedUnsafe that had the behavior of the the std lib (throwing an exception if the index doesn't exists). Similarly for accessing an index, but the name may be a little awkward there if we use apply. Maybe atIndex/atIndexUnsafe or at or elem or something?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes! Sorry, in my defense it was late! ;)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not have:

def get(i: Int): Option[A]
// This is unsafe, it may throw on out-of-bounds, see `get` for a safe variant.
def apply(i: Int): A

for updated I would like:

def updatedOption(i: Int, a: A): Option[NonEmptyVector[A]]
// this will throw is i is out of bounds.
def updated(i: Int, a: A): NonEmptyVector[A]

I feel like adding Option as a suffix signals (like headOption, reduceOption is pretty standard), and I think the unsafe methods should keep the same name (lest we train people that this method is safe in some contexts but not others. Reading the code you have to know exactly if it is NonEmptyVector or Vector to see if apply or updated is safe, I'd rather always make them unsafe so we are consistent).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alternatively, since toVector is free, we could have only get and require ne.toVector(12) for unsafe apply.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is tough. I think that updated and apply have names that seem too benign for throwing exceptions in common cases. It's nice to be consistent with the standard library, but I also have seen lots of scala devs mess up with head and reduce when I don't think they would have if these had been the safe options and separate headUnsafe and reduceUnsafe options had existed. I'm hesitant to pull in gotchas just to have consistent naming with the standard library. Hmmm.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see your point on safety, but there is also a cost (especially to reviewers of future code) of using names that are unsafe on Vector to be safe here (if they change the type signature).

My bias is using cats in a large codebase with many novices that mostly work with the standard library. I'd rather avoid confusion in naming (not at the cost of safety though). So, I would say updateOption and maybe updateUnsafe would be the best solution.

Maybe I would say: when we can safely follow standard naming conventions, we should. Otherwise, if we want to add methods that can throw, use Unsafe as a suffix (or prefix).

head has the same type signature, so I am not worried about it. Changing updated to be optional seems like it will create reading difficulties, so I prefer a new name for a safe variant.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@johnynek everything that you said sounds good to me. So it sounds like it'll be updateOption and updateUnsafe for the update methods. And maybe get and getUnsafe for index access? Or it could be getOption to be symmetric if you'd prefer. Either one is fine with me.

/**
* remove elements not matching the predicate
*/
def filter(f: A => Boolean): Vector[A] = vector.filter(f)

/**
* Append another NonEmptyVector to this
*/
def combine(other: NonEmptyVector[A]): NonEmptyVector[A] = NonEmptyVector(vector ++ other.vector)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably also want a variant of this that works with Vector[A], since appending a vector to a non-empty vector (or vice-versa) always produces a non-empty vector.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And even though this will be called combine in the Semigroup instance, I'd be in favor of calling it concat (maybe with a ++ alias) since it's a little more descriptive in this context.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for ++ we should match Vector when possible.


/**
* find the first element matching the predicate, if one exists
*/
def find(f: A => Boolean): Option[A] = vector.find(f)

/**
* Check whether at least one element satisfies the predicate.
*/
def exists(f: A => Boolean): Boolean = vector.exists(f)

/**
* Check whether all elements satisfy the predicate.
*/
def forall(f: A => Boolean): Boolean = vector.forall(f)

/**
* Left-associative fold using f.
*/
def foldLeft[B](b: B)(f: (B, A) => B): B =
vector.foldLeft(b)(f)

/**
* Right-associative fold using f.
*/
def foldRight[B](lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
Eval.defer(vector.foldRight(lb)(f))

/**
* Applies f to all the elements
*/
def map[B](f: A => B): NonEmptyVector[B] =
NonEmptyVector(vector.map(f))

/**
* Applies f to all elements and combines the result
*/
def flatMap[B](f: A => NonEmptyVector[B]): NonEmptyVector[B] =
NonEmptyVector(vector.flatMap(a => f(a).vector))

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about reduce? Since, we know that this is non-empty the reduce(fn: (A, A) => A): A is a safe signature. That or we could have: def combineAll(implicit: sg: Semigroup[T]): T (or we could have both).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reduce and several other methods are available via the Reducible instance, but I agree that it would probably be nice to have some of these helpers on the class itself.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think they should be called reduceLeft(f: (A, A) => A): A and reduce(implicit S: Semigroup[A]): A to be consistent with cats.Reducible. AFAK, the standard library has similar names.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could add def zip[B](that: NonEmptyVector[B]): NonEmpty[(A, B)] which could be nice.

/**
* Typesafe equality operator.
*
* This method is similar to == except that it only allows two
* NonEmptyVector[A] values to be compared to each other, and uses
* 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(vector, that.vector)

/**
* Typesafe stringification method.
*
* This method is similar to .toString except that it stringifies
* values according to Show[_] instances, rather than using the
* universal .toString method.
*/
def show(implicit A: Show[A]): String =
s"NonEmptyVector(${A.show(head)}, ${Show[Vector[A]].show(tail)})"
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this show correct?
Or should I have s"NonEmptyVector(${Show[Vector[A]].show(vector)})" instead?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better than the cons like show.


private[data] sealed trait NonEmptyVectorInstances extends NonEmptyVectorLowPriority2 {

implicit def catsDataEqForNonEmptyVector[A](implicit A: Eq[A]): Eq[NonEmptyVector[A]] =
new Eq[NonEmptyVector[A]]{
def eqv(x: NonEmptyVector[A], y: NonEmptyVector[A]): Boolean = x === y
}

implicit def catsDataShowForNonEmptyVector[A](implicit A: Show[A]): Show[NonEmptyVector[A]] =
Show.show[NonEmptyVector[A]](_.show)

implicit def catsDataSemigroupKForNonEmptyVector: SemigroupK[NonEmptyVector] =
new SemigroupK[NonEmptyVector] {
def combineK[A](a: NonEmptyVector[A], b: NonEmptyVector[A]): NonEmptyVector[A] =
a combine b
}

implicit def catsDataSemigroupForNonEmptyVector[A]: Semigroup[NonEmptyVector[A]] =
catsDataSemigroupKForNonEmptyVector.algebra

implicit def catsDataReducibleForNonEmptyVector: Reducible[NonEmptyVector] =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should be able to combine a bunch of these instances into 1 like we do with the instances for List here. This avoids the need to mess with the priority hierarchy (or at least for the most part).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also when you do this you should be able to change it from a def to a val.

new NonEmptyReducible[NonEmptyVector, Vector] {
override def split[A](fa: NonEmptyVector[A]): (A, Vector[A]) = (fa.head, fa.tail)

override def size[A](fa: NonEmptyVector[A]): Long = 1 + fa.tail.size.toLong
}

implicit def catsDataMonadForNonEmptyVector: Monad[NonEmptyVector] =
new Monad[NonEmptyVector] {
override def map[A, B](fa: NonEmptyVector[A])(f: A => B): NonEmptyVector[B] =
fa map f

def pure[A](x: A): NonEmptyVector[A] =
NonEmptyVector(x, Vector.empty)

def flatMap[A, B](fa: NonEmptyVector[A])(f: A => NonEmptyVector[B]): NonEmptyVector[B] =
fa flatMap f
}
}

trait NonEmptyVectorLowPriority0 {

implicit def catsDataComonadForNonEmptyVector: Comonad[NonEmptyVector] = new Comonad[NonEmptyVector] {

def coflatMap[A, B](fa: NonEmptyVector[A])(f: NonEmptyVector[A] => B): NonEmptyVector[B] = {
@tailrec def consume(as: Vector[A], buf: VectorBuilder[B]): Vector[B] =
as match {
case a +: as => consume(as, buf += f(NonEmptyVector(a, as)))
case _ => buf.result()
}
NonEmptyVector(f(fa), consume(fa.tail, new VectorBuilder[B]))
}


def extract[A](fa: NonEmptyVector[A]): A = fa.head

def map[A, B](fa: NonEmptyVector[A])(f: A => B): NonEmptyVector[B] = fa map f
}
}

trait NonEmptyVectorLowPriority1 extends NonEmptyVectorLowPriority0 {
implicit def catsDataFunctorForNonEmptyVector: Functor[NonEmptyVector] =
new Functor[NonEmptyVector] {
def map[A, B](fa: NonEmptyVector[A])(f: A => B): NonEmptyVector[B] =
fa map f
}

}

trait NonEmptyVectorLowPriority2 extends NonEmptyVectorLowPriority1 {
implicit def catsDataTraverseForNonEmptyVector: Traverse[NonEmptyVector] =
new Traverse[NonEmptyVector] {
def traverse[G[_], A, B](fa: NonEmptyVector[A])(f: (A) => G[B])(implicit G: Applicative[G]): G[NonEmptyVector[B]] =
G.map2Eval(f(fa.head), Always(Traverse[Vector].traverse(fa.tail)(f)))(NonEmptyVector(_, _)).value


def foldLeft[A, B](fa: NonEmptyVector[A], b: B)(f: (B, A) => B): B =
fa.foldLeft(b)(f)

def foldRight[A, B](fa: NonEmptyVector[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
fa.foldRight(lb)(f)
}
}

object NonEmptyVector extends NonEmptyVectorInstances {

def apply[A](head: A, tail: Vector[A]): NonEmptyVector[A] = NonEmptyVector(head +: tail)
Copy link
Contributor

@non non Jun 18, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to support this as:

def apply[A](body: Vector[A], last: A): NonEmptyVector[A] = NonEmptyVector(body :+ last)

In general I don't like treating non-empty vector as a head/tail pair because it's so inefficient -- prepending to a vector is O(n) and reallocates the entire vector. Appending is a lot more efficient, so in this case I think it makes more sense.

Honestly I'd probably prefer an interface like:

def apply[A](xs: Vector[A]): Option[NonEmptyVector[A]] = ...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I probably missed something. I have been holding the belief that according to
http://docs.scala-lang.org/overviews/collections/performance-characteristics.html
Prepending to a vector is as efficient as appending? I thought it makes sense based on my (not very thorough) understanding of the implementation of vector. Is there some caveat in this claim?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might propose the following instantiation methods:

// to be consistent with var-arg `apply` methods for other collections
def apply[A](head: A, tail: A*): NonEmptyVector[A]

def fromVector[A](vector: Vector[A]): Option[NonEmptyVector[A]]

// this will throw an exception if the vector is empty
def fromVectorUnsafe[A](vector: Vector[A]): NonEmptyVector[A]

def prepend[A](head: A, tail: Vector[A]): NonEmptyVector[A]

def append[A](vector: Vector[A], last: A): NonEmptyVector[A]

I think that would cover all of the use-cases. WDYT?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right @kailuowang. I forgot that vectors maintain a start index to allow this kind of thing to be effectively constant time. I was wrong.

I think maybe I got confused because for awhile Vector(x) ++ xs was very slow. But this was just a bug, not related to x +: xs.


}
6 changes: 0 additions & 6 deletions core/src/main/scala/cats/data/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ 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]

Expand All @@ -11,11 +10,6 @@ package object data {
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] =
OneAnd(head, tail.toVector)

def NonEmptyStream[A](head: A, tail: Stream[A] = Stream.empty): NonEmptyStream[A] =
OneAnd(head, tail)
def NonEmptyStream[A](head: A, tail: A*): NonEmptyStream[A] =
Expand Down
8 changes: 1 addition & 7 deletions docs/src/main/tut/oneand.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,4 @@ type NonEmptyList[A] = OneAnd[List, A]

which is the actual implementation of non-empty lists in cats. By
having the higher kinded type parameter `F[_]`, `OneAnd` is also able
to represent other "non-empty" data structures, e.g.

```tut:silent
import cats.data.OneAnd

type NonEmptyVector[A] = OneAnd[Vector, A]
```
to represent other "non-empty" data structures.
Copy link
Contributor

@kailuowang kailuowang Jun 17, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't feel strongly about this but shall we replace this example with NonEmptyStream, instead of just removing it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that after this PR (or possibly as a part of this PR) we can probably rip out OneAnd as discussed here. Someone can follow up with a NonEmptyStream if it's something they really want -- I suspect it's not used as commonly as NonEmptyList and NonEmptyVector.

3 changes: 3 additions & 0 deletions laws/src/main/scala/cats/laws/discipline/Arbitrary.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ object arbitrary extends ArbitraryInstances0 {
implicit def catsLawsArbitraryForOneAnd[F[_], A](implicit A: Arbitrary[A], F: Arbitrary[F[A]]): Arbitrary[OneAnd[F, A]] =
Arbitrary(F.arbitrary.flatMap(fa => A.arbitrary.map(a => OneAnd(a, fa))))

implicit def catsLawsArbitraryForNonEmptyVector[A](implicit A: Arbitrary[A]): Arbitrary[NonEmptyVector[A]] =
Arbitrary(implicitly[Arbitrary[Vector[A]]].arbitrary.flatMap(fa => A.arbitrary.map(a => NonEmptyVector(a, fa))))

implicit def catsLawsArbitraryForXor[A, B](implicit A: Arbitrary[A], B: Arbitrary[B]): Arbitrary[A Xor B] =
Arbitrary(Gen.oneOf(A.arbitrary.map(Xor.left), B.arbitrary.map(Xor.right)))

Expand Down
171 changes: 171 additions & 0 deletions tests/src/test/scala/cats/tests/NonEmptyVectorTests.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package cats
package tests

import cats.kernel.laws.{GroupLaws, OrderLaws}

import cats.data.NonEmptyVector
import cats.laws.discipline.{ComonadTests, FunctorTests, SemigroupKTests, FoldableTests, MonadTests, SerializableTests, CartesianTests, TraverseTests, ReducibleTests}
import cats.laws.discipline.arbitrary._

class NonEmptyVectorTests extends CatsSuite {
// Lots of collections here.. telling ScalaCheck to calm down a bit
implicit override val generatorDrivenConfig: PropertyCheckConfiguration =
PropertyCheckConfig(maxSize = 5, minSuccessful = 20)

checkAll("NonEmptyVector[Int]", OrderLaws[NonEmptyVector[Int]].eqv)

checkAll("NonEmptyVector[Int] with Option", TraverseTests[NonEmptyVector].traverse[Int, Int, Int, Int, Option, Option])
checkAll("Traverse[NonEmptyVector[A]]", SerializableTests.serializable(Traverse[NonEmptyVector]))

checkAll("NonEmptyVector[Int]", ReducibleTests[NonEmptyVector].reducible[Option, Int, Int])
checkAll("Reducible[NonEmptyVector]", SerializableTests.serializable(Reducible[NonEmptyVector]))

implicit val iso = CartesianTests.Isomorphisms.invariant[NonEmptyVector](NonEmptyVector.catsDataFunctorForNonEmptyVector)

// Test instances that have more general constraints
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need to add braces for separate scopes for the following tests. That's just done for tests where there's an abstract F[_] and we need to test cases where there are different instances available for F[_].

checkAll("NonEmptyVector[Int]", CartesianTests[NonEmptyVector].cartesian[Int, Int, Int])
checkAll("Cartesian[NonEmptyVector]", SerializableTests.serializable(Cartesian[NonEmptyVector]))
}

{
checkAll("NonEmptyVector[Int]", FunctorTests[NonEmptyVector].functor[Int, Int, Int])
checkAll("Functor[NonEmptyVector]", SerializableTests.serializable(Functor[NonEmptyVector]))
}

{
checkAll("NonEmptyVector[Int]", SemigroupKTests[NonEmptyVector].semigroupK[Int])
checkAll("NonEmptyVector[Int]", GroupLaws[NonEmptyVector[Int]].semigroup)
checkAll("SemigroupK[NonEmptyVector]", SerializableTests.serializable(SemigroupK[NonEmptyVector]))
checkAll("Semigroup[NonEmptyVector[Int]]", SerializableTests.serializable(Semigroup[NonEmptyVector[Int]]))
}

{
checkAll("NonEmptyVector[Int]", FoldableTests[NonEmptyVector].foldable[Int, Int])
checkAll("Foldable[NonEmptyVector]", SerializableTests.serializable(Foldable[NonEmptyVector]))
}

{
// Test functor and subclasses don't have implicit conflicts
implicitly[Functor[NonEmptyVector]]
implicitly[Monad[NonEmptyVector]]
implicitly[Comonad[NonEmptyVector]]
}

{
checkAll("NonEmptyVector[Int]", MonadTests[NonEmptyVector].monad[Int, Int, Int])
checkAll("Monad[NonEmptyVector]", SerializableTests.serializable(Monad[NonEmptyVector]))
}

{
checkAll("NonEmptyVector[Int]", ComonadTests[NonEmptyVector].comonad[Int, Int, Int])
checkAll("Comonad[NonEmptyVector]", SerializableTests.serializable(Comonad[NonEmptyVector]))
}


test("size is consistent with toList.size") {
forAll { (nonEmptyVector: NonEmptyVector[Int]) =>
nonEmptyVector.size should === (nonEmptyVector.toList.size.toLong)
}
}


test("Show is not empty and is formatted as expected") {
forAll { (nonEmptyVector: NonEmptyVector[Int]) =>
nonEmptyVector.show.nonEmpty should === (true)
nonEmptyVector.show.startsWith("NonEmptyVector(") should === (true)
nonEmptyVector.show should === (implicitly[Show[NonEmptyVector[Int]]].show(nonEmptyVector))
nonEmptyVector.show.contains(nonEmptyVector.head.show) should === (true)
}
}

test("Show is formatted correctly") {
val nonEmptyVector = NonEmptyVector("Test", Vector.empty)
nonEmptyVector.show should === ("NonEmptyVector(Test, Vector())")
}

test("Creating NonEmptyVector + unwrap is identity") {
forAll { (i: Int, tail: Vector[Int]) =>
val vector = i +: tail
val nonEmptyVector = NonEmptyVector(i, tail)
vector should === (nonEmptyVector.unwrap)
}
}

test("NonEmptyVector#filter is consistent with NonEmptyVector#filter") {
forAll { (nonEmptyVector: NonEmptyVector[Int], p: Int => Boolean) =>
val vector = nonEmptyVector.unwrap
nonEmptyVector.filter(p) should === (vector.filter(p))
}
}

test("NonEmptyVector#find is consistent with NonEmptyVector#find") {
forAll { (nonEmptyVector: NonEmptyVector[Int], p: Int => Boolean) =>
val vector = nonEmptyVector.unwrap
nonEmptyVector.find(p) should === (vector.find(p))
}
}

test("NonEmptyVector#exists is consistent with NonEmptyVector#exists") {
forAll { (nonEmptyVector: NonEmptyVector[Int], p: Int => Boolean) =>
val vector = nonEmptyVector.unwrap
nonEmptyVector.exists(p) should === (vector.exists(p))
}
}

test("NonEmptyVector#forall is consistent with NonEmptyVector#forall") {
forAll { (nonEmptyVector: NonEmptyVector[Int], p: Int => Boolean) =>
val vector = nonEmptyVector.unwrap
nonEmptyVector.forall(p) should === (vector.forall(p))
}
}

test("NonEmptyVector#map is consistent with NonEmptyVector#map") {
forAll { (nonEmptyVector: NonEmptyVector[Int], p: Int => String) =>
val vector = nonEmptyVector.unwrap
nonEmptyVector.map(p).unwrap should === (vector.map(p))
}
}

test("reduceLeft consistent with foldLeft") {
forAll { (nonEmptyVector: NonEmptyVector[Int], f: (Int, Int) => Int) =>
nonEmptyVector.reduceLeft(f) should === (nonEmptyVector.tail.foldLeft(nonEmptyVector.head)(f))
}
}

test("reduceRight consistent with foldRight") {
forAll { (nonEmptyVector: NonEmptyVector[Int], f: (Int, Eval[Int]) => Eval[Int]) =>
nonEmptyVector.reduceRight(f).value should === (nonEmptyVector.tail.foldRight(nonEmptyVector.head)((a, b) => f(a, Now(b)).value))
}
}

test("reduce consistent with fold") {
forAll { (nonEmptyVector: NonEmptyVector[Int]) =>
nonEmptyVector.reduce should === (nonEmptyVector.fold)
}
}

test("reduce consistent with reduceK") {
forAll { (nonEmptyVector: NonEmptyVector[Option[Int]]) =>
nonEmptyVector.reduce(SemigroupK[Option].algebra[Int]) should === (nonEmptyVector.reduceK)
}
}

test("reduceLeftToOption consistent with foldLeft + Option") {
forAll { (nonEmptyVector: NonEmptyVector[Int], f: Int => String, g: (String, Int) => String) =>
val expected = nonEmptyVector.tail.foldLeft(Option(f(nonEmptyVector.head))) { (opt, i) =>
opt.map(s => g(s, i))
}
nonEmptyVector.reduceLeftToOption(f)(g) should === (expected)
}
}

test("reduceRightToOption consistent with foldRight + Option") {
forAll { (nonEmptyVector: NonEmptyVector[Int], f: Int => String, g: (Int, Eval[String]) => Eval[String]) =>
val expected = nonEmptyVector.tail.foldRight(Option(f(nonEmptyVector.head))) { (i, opt) =>
opt.map(s => g(i, Now(s)).value)
}
nonEmptyVector.reduceRightToOption(f)(g).value should === (expected)
}
}
}