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

Clean up some NonEmpty stuff #3307

Merged
merged 5 commits into from
Feb 20, 2020
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
128 changes: 124 additions & 4 deletions core/src/main/scala-2.13+/cats/data/NonEmptyLazyList.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import NonEmptyLazyList.create
import kernel.PartialOrder
import instances.lazyList._

import scala.collection.immutable.TreeSet
import scala.collection.immutable.{SortedMap, TreeMap, TreeSet}
import scala.collection.mutable

object NonEmptyLazyList extends NonEmptyLazyListInstances {

Expand Down Expand Up @@ -53,7 +54,9 @@ object NonEmptyLazyList extends NonEmptyLazyListInstances {
new NonEmptyLazyListOps(value)
}

class NonEmptyLazyListOps[A](private val value: NonEmptyLazyList[A]) extends AnyVal {
class NonEmptyLazyListOps[A](private val value: NonEmptyLazyList[A])
extends AnyVal
with NonEmptyCollection[A, LazyList, NonEmptyLazyList] {

/**
* Converts this NonEmptyLazyList to a `LazyList`
Expand Down Expand Up @@ -218,7 +221,14 @@ class NonEmptyLazyListOps[A](private val value: NonEmptyLazyList[A]) extends Any
* Finds the first element of this `NonEmptyLazyList` for which the given partial
* function is defined, and applies the partial function to it.
*/
final def collectLazyList[B](pf: PartialFunction[A, B]): Option[B] = toLazyList.collectFirst(pf)
@deprecated("Use collectFirst", "2.2.0-M1")
final def collectLazyList[B](pf: PartialFunction[A, B]): Option[B] = collectFirst(pf)

/**
* Finds the first element of this `NonEmptyLazyList` for which the given partial
* function is defined, and applies the partial function to it.
*/
final def collectFirst[B](pf: PartialFunction[A, B]): Option[B] = toLazyList.collectFirst(pf)

/**
* Filters all elements of this NonEmptyLazyList that do not satisfy the given predicate.
Expand Down Expand Up @@ -326,6 +336,116 @@ class NonEmptyLazyListOps[A](private val value: NonEmptyLazyList[A]) extends Any

create(buf.result())
}

/**
* Sorts this `NonEmptyLazyList` according to an `Order` on transformed `B` from `A`
*
* {{{
* scala> import cats.data.NonEmptyLazyList
* scala> import cats.instances.int._
* scala> val nel = NonEmptyLazyList(('a', 4), ('z', 1), ('e', 22))
* scala> nel.sortBy(_._2).toLazyList.toList
* res0: List[(Char, Int)] = List((z,1), (a,4), (e,22))
* }}}
*/
final def sortBy[B](f: A => B)(implicit B: Order[B]): NonEmptyLazyList[A] =
// safe: sorting a NonEmptyList cannot produce an empty List
create(toLazyList.sortBy(f)(B.toOrdering))

/**
* Sorts this `NonEmptyList` according to an `Order`
*
* {{{
* scala> import cats.data.NonEmptyLazyList
* scala> import cats.instances.int._
* scala> val nel = NonEmptyLazyList(12, 4, 3, 9)
* scala> nel.sorted.toLazyList.toList
* res0: List[Int] = List(3, 4, 9, 12)
* }}}
*/
final def sorted[AA >: A](implicit AA: Order[AA]): NonEmptyLazyList[AA] =
create(toLazyList.sorted(AA.toOrdering))

/**
* Groups elements inside this `NonEmptyLazyList` according to the `Order`
* of the keys produced by the given mapping function.
*
* {{{
* scala> import scala.collection.immutable.SortedMap
* scala> import cats.data.NonEmptyLazyList
* scala> import cats.implicits._
* scala> val nel = NonEmptyLazyList(12, -2, 3, -5)
* scala> val expectedResult = SortedMap(false -> NonEmptyLazyList(-2, -5), true -> NonEmptyLazyList(12, 3))
* scala> val result = nel.groupBy(_ >= 0)
* scala> result === expectedResult
* res0: Boolean = true
* }}}
*/
final def groupBy[B](f: A => B)(implicit B: Order[B]): SortedMap[B, NonEmptyLazyList[A]] = {
implicit val ordering: Ordering[B] = B.toOrdering
var m = TreeMap.empty[B, mutable.Builder[A, LazyList[A]]]

for { elem <- toLazyList } {
val k = f(elem)

m.get(k) match {
case None => m += ((k, LazyList.newBuilder[A] += elem))
case Some(builder) => builder += elem
}
}

m.map {
case (k, v) => (k, create(v.result))
}: TreeMap[B, NonEmptyLazyList[A]]
}

/**
* Groups elements inside this `NonEmptyLazyList` according to the `Order`
* of the keys produced by the given mapping function.
*
* {{{
* scala> import cats.data.{NonEmptyLazyList, NonEmptyMap}
* scala> import cats.implicits._
* scala> val nel = NonEmptyLazyList(12, -2, 3, -5)
* scala> val expectedResult = NonEmptyMap.of(false -> NonEmptyLazyList(-2, -5), true -> NonEmptyLazyList(12, 3))
* scala> val result = nel.groupByNem(_ >= 0)
* scala> result === expectedResult
* res0: Boolean = true
* }}}
*/
final def groupByNem[B](f: A => B)(implicit B: Order[B]): NonEmptyMap[B, NonEmptyLazyList[A]] =
NonEmptyMap.fromMapUnsafe(groupBy(f))

/**
* Creates new `NonEmptyMap`, similarly to List#toMap from scala standard library.
*{{{
* scala> import cats.data.{NonEmptyLazyList, NonEmptyMap}
* scala> import cats.implicits._
* scala> val nel = NonEmptyLazyList.fromLazyListPrepend((0, "a"), LazyList((1, "b"),(0, "c"), (2, "d")))
* scala> val expectedResult = NonEmptyMap.of(0 -> "c", 1 -> "b", 2 -> "d")
* scala> val result = nel.toNem
* scala> result === expectedResult
* res0: Boolean = true
*}}}
*
*/
final def toNem[T, U](implicit ev: A <:< (T, U), order: Order[T]): NonEmptyMap[T, U] =
NonEmptyMap.fromMapUnsafe(SortedMap(toLazyList.map(ev): _*)(order.toOrdering))

/**
* Creates new `NonEmptySet`, similarly to List#toSet from scala standard library.
*{{{
* scala> import cats.data._
* scala> import cats.instances.int._
* scala> val nel = NonEmptyLazyList.fromLazyListPrepend(1, LazyList(2,2,3,4))
* scala> nel.toNes
* res0: cats.data.NonEmptySet[Int] = TreeSet(1, 2, 3, 4)
*}}}
*/
final def toNes[B >: A](implicit order: Order[B]): NonEmptySet[B] =
NonEmptySet.of(head, tail: _*)

final def show[AA >: A](implicit AA: Show[AA]): String = s"NonEmpty${Show[LazyList[AA]].show(toLazyList)}"
}

sealed abstract private[data] class NonEmptyLazyListInstances extends NonEmptyLazyListInstances1 {
Expand Down Expand Up @@ -375,7 +495,7 @@ sealed abstract private[data] class NonEmptyLazyListInstances extends NonEmptyLa
Semigroup[LazyList[A]].asInstanceOf[Semigroup[NonEmptyLazyList[A]]]

implicit def catsDataShowForNonEmptyLazyList[A](implicit A: Show[A]): Show[NonEmptyLazyList[A]] =
Show.show[NonEmptyLazyList[A]](nell => s"NonEmpty${Show[LazyList[A]].show(nell.toLazyList)}")
Show.show[NonEmptyLazyList[A]](_.show)

implicit def catsDataParallelForNonEmptyLazyList: Parallel.Aux[NonEmptyLazyList, OneAnd[ZipLazyList, *]] =
new Parallel[NonEmptyLazyList] {
Expand Down
29 changes: 27 additions & 2 deletions core/src/main/scala/cats/data/Chain.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import Chain._
import cats.kernel.instances.StaticMethods

import scala.annotation.tailrec
import scala.collection.immutable.SortedMap
import scala.collection.immutable.TreeSet
import scala.collection.immutable.{SortedMap, TreeSet}
import scala.collection.mutable.ListBuffer

/**
Expand Down Expand Up @@ -324,6 +323,18 @@ sealed abstract class Chain[+A] {
result
}

/**
* Zips each element of this `Chain` with its index.
*/
final def zipWithIndex: Chain[(A, Int)] = this match {
case Empty => Empty
case Singleton(a) => Singleton((a, 0))
case Append(left, right) =>
val leftSize = left.length.toInt
Append(left.zipWithIndex, right.zipWithIndex.map { case (a, i) => (a, leftSize + i) })
case Wrap(seq) => Wrap(seq.zipWithIndex)
}

/**
* Groups elements inside this `Chain` according to the `Order`
* of the keys produced by the given mapping function.
Expand Down Expand Up @@ -529,6 +540,20 @@ sealed abstract class Chain[+A] {
}
result
}

final def sortBy[B](f: A => B)(implicit B: Order[B]): Chain[A] = this match {
case Empty => this
case Singleton(_) => this
case Append(_, _) => Wrap(toVector.sortBy(f)(B.toOrdering))
case Wrap(seq) => Wrap(seq.sortBy(f)(B.toOrdering))
}

final def sorted[AA >: A](implicit AA: Order[AA]): Chain[AA] = this match {
case Empty => this
case Singleton(_) => this
case Append(_, _) => Wrap(toVector.sorted(AA.toOrdering))
case Wrap(seq) => Wrap(seq.sorted(AA.toOrdering))
}
}

object Chain extends ChainInstances {
Expand Down
17 changes: 15 additions & 2 deletions core/src/main/scala/cats/data/NonEmptyChain.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package data
import NonEmptyChainImpl.create
import cats.{Order, Semigroup}
import cats.kernel._
import scala.collection.immutable.SortedMap

private[data] object NonEmptyChainImpl extends NonEmptyChainInstances with ScalaVersionSpecificNonEmptyChainImpl {
// The following 3 types are components of a technique to
Expand Down Expand Up @@ -52,7 +53,9 @@ private[data] object NonEmptyChainImpl extends NonEmptyChainInstances with Scala
new NonEmptyChainOps(value)
}

class NonEmptyChainOps[A](private val value: NonEmptyChain[A]) extends AnyVal {
class NonEmptyChainOps[A](private val value: NonEmptyChain[A])
extends AnyVal
with NonEmptyCollection[A, Chain, NonEmptyChain] {

/**
* Converts this chain to a `Chain`
Expand Down Expand Up @@ -382,6 +385,8 @@ class NonEmptyChainOps[A](private val value: NonEmptyChain[A]) extends AnyVal {
final def groupBy[B](f: A => B)(implicit B: Order[B]): NonEmptyMap[B, NonEmptyChain[A]] =
toChain.groupBy(f).asInstanceOf[NonEmptyMap[B, NonEmptyChain[A]]]

final def groupByNem[B](f: A => B)(implicit B: Order[B]): NonEmptyMap[B, NonEmptyChain[A]] = groupBy(f)

final def iterator: Iterator[A] = toChain.iterator

final def reverseIterator: Iterator[A] = toChain.reverseIterator
Expand All @@ -396,6 +401,14 @@ class NonEmptyChainOps[A](private val value: NonEmptyChain[A]) extends AnyVal {
final def distinct[AA >: A](implicit O: Order[AA]): NonEmptyChain[AA] =
create(toChain.distinct[AA])

final def sortBy[B](f: A => B)(implicit B: Order[B]): NonEmptyChain[A] = create(toChain.sortBy(f))
final def sorted[AA >: A](implicit AA: Order[AA]): NonEmptyChain[AA] = create(toChain.sorted[AA])
final def toNem[T, V](implicit ev: A <:< (T, V), order: Order[T]): NonEmptyMap[T, V] =
NonEmptyMap.fromMapUnsafe(SortedMap(toChain.toVector.map(ev): _*)(order.toOrdering))
final def toNes[B >: A](implicit order: Order[B]): NonEmptySet[B] = NonEmptySet.of(head, tail.toVector: _*)
final def zipWithIndex: NonEmptyChain[(A, Int)] = create(toChain.zipWithIndex)

final def show[AA >: A](implicit AA: Show[AA]): String = s"NonEmpty${Show[Chain[AA]].show(toChain)}"
}

sealed abstract private[data] class NonEmptyChainInstances extends NonEmptyChainInstances1 {
Expand Down Expand Up @@ -454,7 +467,7 @@ sealed abstract private[data] class NonEmptyChainInstances extends NonEmptyChain
Semigroup[Chain[A]].asInstanceOf[Semigroup[NonEmptyChain[A]]]

implicit def catsDataShowForNonEmptyChain[A](implicit A: Show[A]): Show[NonEmptyChain[A]] =
Show.show[NonEmptyChain[A]](nec => s"NonEmpty${Show[Chain[A]].show(nec.toChain)}")
Show.show[NonEmptyChain[A]](_.show)

}

Expand Down
40 changes: 40 additions & 0 deletions core/src/main/scala/cats/data/NonEmptyCollection.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package cats.data

import cats.Show
import cats.kernel.{Order, Semigroup}

private[cats] trait NonEmptyCollection[+A, U[+_], NE[+_]] extends Any {
def head: A
def tail: U[A]
def last: A
def init: U[A]

def iterator: Iterator[A]

def map[B](f: A => B): NE[B]
def reverse: NE[A]
def prepend[AA >: A](a: AA): NE[AA]
def append[AA >: A](a: AA): NE[AA]

def filter(f: A => Boolean): U[A]
def filterNot(p: A => Boolean): U[A]
def collect[B](pf: PartialFunction[A, B]): U[B]
def find(p: A => Boolean): Option[A]
def exists(p: A => Boolean): Boolean
def forall(p: A => Boolean): Boolean

def foldLeft[B](b: B)(f: (B, A) => B): B
def reduce[AA >: A](implicit S: Semigroup[AA]): AA

def zipWith[B, C](b: NE[B])(f: (A, B) => C): NE[C]
def zipWithIndex: NE[(A, Int)]

def distinct[AA >: A](implicit O: Order[AA]): NE[AA]
def sortBy[B](f: A => B)(implicit B: Order[B]): NE[A]
def sorted[AA >: A](implicit AA: Order[AA]): NE[AA]
def groupByNem[B](f: A => B)(implicit B: Order[B]): NonEmptyMap[B, NE[A]]
def toNem[T, V](implicit ev: A <:< (T, V), order: Order[T]): NonEmptyMap[T, V]
def toNes[B >: A](implicit order: Order[B]): NonEmptySet[B]

def show[AA >: A](implicit AA: Show[AA]): String
}
4 changes: 3 additions & 1 deletion core/src/main/scala/cats/data/NonEmptyList.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,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]) extends NonEmptyCollection[A, List, NonEmptyList] {

/**
* Return the head and tail into a single list
Expand Down Expand Up @@ -55,6 +55,8 @@ final case class NonEmptyList[+A](head: A, tail: List[A]) {
case t => head :: t.init
}

final def iterator: Iterator[A] = toList.iterator

/**
* The size of this NonEmptyList
*
Expand Down
Loading