Skip to content

Commit

Permalink
Merge branch 'main' into update/auxlib-0.4.1
Browse files Browse the repository at this point in the history
  • Loading branch information
djspiewak authored Nov 18, 2021
2 parents d343e60 + 26cdba3 commit fc39113
Show file tree
Hide file tree
Showing 6 changed files with 157 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ private[cats] trait ScalaVersionSpecificMonoidKInstances {
}

private[cats] trait ScalaVersionSpecificParallelInstances {
@deprecated("Use catsParallelForLazyList", "3.0.0")
@deprecated("Use catsStdParallelForZipLazyList", "3.0.0")
implicit def catsStdParallelForZipStream: Parallel.Aux[Stream, ZipStream] =
cats.instances.parallel.catsStdParallelForZipStream

Expand Down Expand Up @@ -69,7 +69,7 @@ private[cats] trait ScalaVersionSpecificTraverseFilterInstances {
}

private[cats] trait ScalaVersionSpecificAlignInstances {
@deprecated("Use catsTraverseFilterForLazyList", "3.0.0")
@deprecated("Use catsAlignForLazyList", "3.0.0")
implicit def catsAlignForStream: Align[Stream] =
cats.instances.stream.catsStdInstancesForStream

Expand Down
116 changes: 116 additions & 0 deletions core/src/main/scala/cats/syntax/vector.scala
Original file line number Diff line number Diff line change
@@ -1,11 +1,127 @@
package cats.syntax

import cats.data.NonEmptyVector
import cats.{Applicative, Functor, Order, Traverse}

import scala.collection.immutable.SortedMap

trait VectorSyntax {
implicit final def catsSyntaxVectors[A](va: Vector[A]): VectorOps[A] = new VectorOps(va)
}

final class VectorOps[A](private val va: Vector[A]) extends AnyVal {

/**
* Returns an Option of NonEmptyVector from a Vector
*
* Example:
* {{{
* scala> import cats.data.NonEmptyVector
* scala> import cats.implicits._
*
* scala> val result1: Vector[Int] = Vector(1, 2)
* scala> result1.toNev
* res0: Option[NonEmptyVector[Int]] = Some(NonEmptyVector(1, 2))
*
* scala> val result2: Vector[Int] = Vector.empty[Int]
* scala> result2.toNev
* res1: Option[NonEmptyVector[Int]] = None
* }}}
*/
def toNev: Option[NonEmptyVector[A]] = NonEmptyVector.fromVector(va)

/**
* Groups elements inside this `Vector` according to the `Order` of the keys
* produced by the given mapping function.
*
* {{{
* scala> import cats.data.NonEmptyVector
* scala> import scala.collection.immutable.SortedMap
* scala> import cats.implicits._
*
* scala> val vector = Vector(12, -2, 3, -5)
*
* scala> val expectedResult = SortedMap(false -> NonEmptyVector.of(-2, -5), true -> NonEmptyVector.of(12, 3))
*
* scala> vector.groupByNev(_ >= 0) === expectedResult
* res0: Boolean = true
* }}}
*/
def groupByNev[B](f: A => B)(implicit B: Order[B]): SortedMap[B, NonEmptyVector[A]] = {
implicit val ordering: Ordering[B] = B.toOrdering
toNev.fold(SortedMap.empty[B, NonEmptyVector[A]])(_.groupBy(f))
}

/**
* Groups elements inside this `Vector` according to the `Order` of the keys
* produced by the given mapping monadic function.
*
* {{{
* scala> import cats.data.NonEmptyVector
* scala> import scala.collection.immutable.SortedMap
* scala> import cats.implicits._
*
* scala> val vector = Vector(12, -2, 3, -5)
*
* scala> val expectedResult = Option(SortedMap(false -> NonEmptyVector.of(-2, -5), true -> NonEmptyVector.of(12, 3)))
*
* scala> vector.groupByNevA(num => Option(0).map(num >= _)) === expectedResult
* res0: Boolean = true
* }}}
*/
def groupByNevA[F[_], B](
f: A => F[B]
)(implicit F: Applicative[F], B: Order[B]): F[SortedMap[B, NonEmptyVector[A]]] = {
implicit val ordering: Ordering[B] = B.toOrdering
val mapFunctor = Functor[SortedMap[B, *]]
val nevTraverse = Traverse[NonEmptyVector]

toNev.fold(F.pure(SortedMap.empty[B, NonEmptyVector[A]])) { nev =>
F.map(nevTraverse.traverse(nev)(a => F.tupleLeft(f(a), a))) { vector =>
mapFunctor.map(vector.groupBy(_._2))(_.map(_._1))
}
}
}

/**
* Produces a `NonEmptyVector` containing cumulative results of applying the
* operator going left to right.
*
* Example:
* {{{
* scala> import cats.data.NonEmptyVector
* scala> import cats.implicits._
*
* scala> val result1: Vector[Int] = Vector(1, 2)
* scala> result1.scanLeftNev(100)(_ + _)
* res0: NonEmptyVector[Int] = NonEmptyVector(100, 101, 103)
*
* scala> val result2: Vector[Int] = Vector.empty[Int]
* scala> result2.scanLeftNev(1)(_ + _)
* res1: NonEmptyVector[Int] = NonEmptyVector(1)
* }}}
*/
def scanLeftNev[B](b: B)(f: (B, A) => B): NonEmptyVector[B] =
NonEmptyVector.fromVectorUnsafe(va.scanLeft(b)(f))

/**
* Produces a `NonEmptyVector` containing cumulative results of applying the
* operator going right to left.
*
* Example:
* {{{
* scala> import cats.data.NonEmptyVector
* scala> import cats.implicits._
*
* scala> val result1: Vector[Int] = Vector(1, 2)
* scala> result1.scanRightNev(100)(_ + _)
* res0: NonEmptyVector[Int] = NonEmptyVector(103, 102, 100)
*
* scala> val result2: Vector[Int] = Vector.empty[Int]
* scala> result2.scanRightNev(1)(_ + _)
* res1: NonEmptyVector[Int] = NonEmptyVector(1)
* }}}
*/
def scanRightNev[B](b: B)(f: (A, B) => B): NonEmptyVector[B] =
NonEmptyVector.fromVectorUnsafe(va.scanRight(b)(f))
}
2 changes: 1 addition & 1 deletion docs/src/main/mdoc/typeclasses/applicativemonaderror.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ when things go wrong.
```scala mdoc:silent
def getTemperatureFromByCoordinatesAlternate[F[_]](x: (Int, Int))(implicit me: MonadError[F, String]): F[Int] = {
if (x._1 < 0 || x._2 < 0) me.raiseError("Invalid Coordinates")
for { c <- getCityClosestToCoordinate[F](x)
else for { c <- getCityClosestToCoordinate[F](x)
t <- getTemperatureByCity[F](c) } yield t
}
```
4 changes: 2 additions & 2 deletions project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.31")
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.32")
addSbtPlugin("com.github.sbt" % "sbt-unidoc" % "0.5.0")
addSbtPlugin("com.github.sbt" %% "sbt-release" % "1.1.0")
addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.1.2")
Expand All @@ -11,7 +11,7 @@ addSbtPlugin("com.47deg" % "sbt-microsites" % "1.3.4")
addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.2.24")
addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.1.0")
addSbtPlugin("org.portable-scala" % "sbt-scala-native-crossproject" % "1.1.0")
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.7.1")
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.5.1")
addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.1")
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.3")
addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.10.0")
Expand Down
12 changes: 12 additions & 0 deletions tests/src/test/scala/cats/tests/ListSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,18 @@ class ListSuite extends CatsSuite {
}
)

test("scanLeftNel should be consistent with scanLeft")(
forAll { (fa: List[Int], b: Int, f: (Int, Int) => Int) =>
assert(fa.scanLeftNel(b)(f).toList === fa.scanLeft(b)(f))
}
)

test("scanRightNel should be consistent with scanRight")(
forAll { (fa: List[Int], b: Int, f: (Int, Int) => Int) =>
assert(fa.scanRightNel(b)(f).toList === fa.scanRight(b)(f))
}
)

test("show") {
assert(List(1, 2, 3).show === "List(1, 2, 3)")
assert((Nil: List[Int]).show === "List()")
Expand Down
24 changes: 24 additions & 0 deletions tests/src/test/scala/cats/tests/VectorSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,30 @@ class VectorSuite extends CatsSuite {
assert(Vector.empty[Int].toNev == None)
}

test("groupByNev should be consistent with groupBy")(
forAll { (fa: Vector[Int], f: Int => Int) =>
assert((fa.groupByNev(f).map { case (k, v) => (k, v.toVector) }: Map[Int, Vector[Int]]) === fa.groupBy(f))
}
)

test("groupByNevA should be consistent with groupByNev")(
forAll { (fa: Vector[Int], f: Int => Int) =>
assert(fa.groupByNevA(f.andThen(Option(_))) === Option(fa.groupByNev(f)))
}
)

test("scanLeftNev should be consistent with scanLeft")(
forAll { (fa: Vector[Int], b: Int, f: (Int, Int) => Int) =>
assert(fa.scanLeftNev(b)(f).toVector === fa.scanLeft(b)(f))
}
)

test("scanRightNev should be consistent with scanRight")(
forAll { (fa: Vector[Int], b: Int, f: (Int, Int) => Int) =>
assert(fa.scanRightNev(b)(f).toVector === fa.scanRight(b)(f))
}
)

test("traverse is stack-safe") {
val vec = (0 until 100000).toVector
val sumAll = Traverse[Vector]
Expand Down

0 comments on commit fc39113

Please sign in to comment.