-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Finish up work on NonEmptyList #1231
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
Changes from all commits
aa8fea7
70187da
1c2adb1
4e8ffe1
9eea8e2
4acdc86
e8ce4fd
8a809c8
d788e62
e33a0f0
b1b4213
c8fcaf0
cfdd085
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,255 @@ | ||
package cats | ||
package data | ||
|
||
import cats.instances.list._ | ||
import cats.syntax.order._ | ||
|
||
import scala.annotation.tailrec | ||
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]) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what if we did something similar to |
||
|
||
/** | ||
* Return the head and tail into a single list | ||
*/ | ||
def toList: List[A] = head :: tail | ||
|
||
/** | ||
* Applies f to all the elements of the structure | ||
*/ | ||
def map[B](f: A => B): NonEmptyList[B] = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if we did the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One thing that we could do would be to define it as a |
||
NonEmptyList(f(head), tail.map(f)) | ||
|
||
def ++(l: List[A]): NonEmptyList[A] = | ||
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) | ||
|
||
/** | ||
* remove elements not matching the predicate | ||
*/ | ||
def filter(p: A => Boolean): List[A] = { | ||
val ftail = tail.filter(p) | ||
if (p(head)) head :: ftail | ||
else ftail | ||
} | ||
|
||
/** | ||
* Append another NonEmptyList | ||
*/ | ||
def concat(other: NonEmptyList[A]): NonEmptyList[A] = | ||
NonEmptyList(head, tail ::: other.toList) | ||
|
||
/** | ||
* Find the first element matching the predicate, if one exists | ||
*/ | ||
def find(p: A => Boolean): Option[A] = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what about: if (p(head)) Some(head)
else tail.find(p) |
||
if (p(head)) Some(head) | ||
else tail.find(p) | ||
|
||
/** | ||
* Check whether at least one element satisfies the predicate | ||
*/ | ||
def exists(p: A => Boolean): Boolean = | ||
p(head) || tail.exists(p) | ||
|
||
/** | ||
* Check whether all elements satisfy the predicate | ||
*/ | ||
def forall(p: A => Boolean): Boolean = | ||
p(head) && tail.forall(p) | ||
|
||
/** | ||
* Left-associative fold on the structure using f. | ||
*/ | ||
def foldLeft[B](b: B)(f: (B, A) => B): B = | ||
tail.foldLeft(f(b, head))(f) | ||
|
||
/** | ||
* Right-associative fold on the structure using f. | ||
*/ | ||
def foldRight[B](lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = | ||
Foldable[List].foldRight(toList, lb)(f) | ||
|
||
/** | ||
* Left-associative reduce using f. | ||
*/ | ||
def reduceLeft(f: (A, A) => A): A = | ||
tail.foldLeft(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 | ||
|
||
def coflatMap[B](f: NonEmptyList[A] => B): NonEmptyList[B] = { | ||
val buf = ListBuffer.empty[B] | ||
@tailrec def consume(as: List[A]): List[B] = | ||
as match { | ||
case Nil => buf.toList | ||
case a :: as => | ||
buf += f(NonEmptyList(a, as)) | ||
consume(as) | ||
} | ||
NonEmptyList(f(this), consume(tail)) | ||
} | ||
|
||
def ===(o: NonEmptyList[A])(implicit A: Eq[A]): Boolean = | ||
(this.head === o.head) && this.tail === o.tail | ||
|
||
def show(implicit A: Show[A]): String = | ||
toList.iterator.map(A.show).mkString("NonEmptyList(", ", ", ")") | ||
|
||
override def toString: String = s"NonEmpty$toList" | ||
} | ||
|
||
object NonEmptyList extends NonEmptyListInstances { | ||
def apply[A](head: A, tail: A*): NonEmptyList[A] = NonEmptyList(head, tail.toList) | ||
|
||
/** | ||
* Create a `NonEmptyList` from a `List`. | ||
* | ||
* The result will be `None` if the input list is empty and `Some` wrapping a | ||
* `NonEmptyList` otherwise. | ||
* | ||
* @see [[fromListUnsafe]] for an unsafe version that throws an exception if | ||
* the input list is empty. | ||
*/ | ||
def fromList[A](l: List[A]): Option[NonEmptyList[A]] = | ||
l match { | ||
case Nil => None | ||
case h :: t => Some(NonEmptyList(h, t)) | ||
} | ||
|
||
/** | ||
* Create a `NonEmptyList` from a `List`, or throw an | ||
* `IllegalArgumentException` if the input list is empty. | ||
* | ||
* @see [[fromList]] for a safe version that returns `None` if the input list | ||
* is empty. | ||
*/ | ||
def fromListUnsafe[A](l: List[A]): NonEmptyList[A] = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what about l match {
case Nil => throw new IllegalArgumentException("Cannot create NonEmptyList from empty list")
case h :: t => NonEmptyList(h, t)
} |
||
l match { | ||
case Nil => throw new IllegalArgumentException("Cannot create NonEmptyList from empty list") | ||
case h :: t => NonEmptyList(h, t) | ||
} | ||
|
||
def fromReducible[F[_], A](fa: F[A])(implicit F: Reducible[F]): NonEmptyList[A] = | ||
F.toNonEmptyList(fa) | ||
} | ||
|
||
private[data] sealed trait NonEmptyListInstances extends NonEmptyListInstances0 { | ||
|
||
implicit val catsDataInstancesForNonEmptyList: SemigroupK[NonEmptyList] with Reducible[NonEmptyList] | ||
with Comonad[NonEmptyList] with Traverse[NonEmptyList] with MonadRec[NonEmptyList] = | ||
new NonEmptyReducible[NonEmptyList, List] with SemigroupK[NonEmptyList] | ||
with Comonad[NonEmptyList] with Traverse[NonEmptyList] with MonadRec[NonEmptyList] { | ||
|
||
def combineK[A](a: NonEmptyList[A], b: NonEmptyList[A]): NonEmptyList[A] = | ||
a concat b | ||
|
||
override def split[A](fa: NonEmptyList[A]): (A, List[A]) = (fa.head, fa.tail) | ||
|
||
override def reduceLeft[A](fa: NonEmptyList[A])(f: (A, A) => A): A = | ||
fa.reduceLeft(f) | ||
|
||
override def map[A, B](fa: NonEmptyList[A])(f: A => B): NonEmptyList[B] = | ||
fa map f | ||
|
||
def pure[A](x: A): NonEmptyList[A] = | ||
NonEmptyList(x, List.empty) | ||
|
||
def flatMap[A, B](fa: NonEmptyList[A])(f: A => NonEmptyList[B]): NonEmptyList[B] = | ||
fa flatMap f | ||
|
||
def coflatMap[A, B](fa: NonEmptyList[A])(f: NonEmptyList[A] => B): NonEmptyList[B] = | ||
fa coflatMap f | ||
|
||
def extract[A](fa: NonEmptyList[A]): A = fa.head | ||
|
||
def traverse[G[_], A, B](fa: NonEmptyList[A])(f: A => G[B])(implicit G: Applicative[G]): G[NonEmptyList[B]] = | ||
fa traverse f | ||
|
||
override def foldLeft[A, B](fa: NonEmptyList[A], b: B)(f: (B, A) => B): B = | ||
fa.foldLeft(b)(f) | ||
|
||
override def foldRight[A, B](fa: NonEmptyList[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = | ||
fa.foldRight(lb)(f) | ||
|
||
def tailRecM[A, B](a: A)(f: A => NonEmptyList[A Xor B]): NonEmptyList[B] = { | ||
val buf = new ListBuffer[B] | ||
@tailrec def go(v: NonEmptyList[A Xor B]): Unit = v.head match { | ||
case Xor.Right(b) => | ||
buf += b | ||
NonEmptyList.fromList(v.tail) match { | ||
case Some(t) => go(t) | ||
case None => () | ||
} | ||
case Xor.Left(a) => go(f(a) ++ v.tail) | ||
} | ||
go(f(a)) | ||
NonEmptyList.fromListUnsafe(buf.result()) | ||
} | ||
|
||
override def forall[A](fa: NonEmptyList[A])(p: A => Boolean): Boolean = | ||
fa forall p | ||
|
||
override def exists[A](fa: NonEmptyList[A])(p: A => Boolean): Boolean = | ||
fa exists p | ||
|
||
override def toList[A](fa: NonEmptyList[A]): List[A] = fa.toList | ||
|
||
override def toNonEmptyList[A](fa: NonEmptyList[A]): NonEmptyList[A] = fa | ||
} | ||
|
||
implicit def catsDataShowForNonEmptyList[A](implicit A: Show[A]): Show[NonEmptyList[A]] = | ||
Show.show[NonEmptyList[A]](_.show) | ||
|
||
implicit def catsDataSemigroupForNonEmptyList[A]: Semigroup[NonEmptyList[A]] = | ||
SemigroupK[NonEmptyList].algebra[A] | ||
|
||
implicit def catsDataOrderForNonEmptyList[A](implicit A: Order[A]): Order[NonEmptyList[A]] = | ||
new NonEmptyListOrder[A] { | ||
val A0 = A | ||
} | ||
} | ||
|
||
private[data] sealed trait NonEmptyListInstances0 extends NonEmptyListInstances1 { | ||
implicit def catsDataPartialOrderForNonEmptyList[A](implicit A: PartialOrder[A]): PartialOrder[NonEmptyList[A]] = | ||
new NonEmptyListPartialOrder[A] { | ||
val A0 = A | ||
} | ||
} | ||
|
||
private[data] sealed trait NonEmptyListInstances1 { | ||
|
||
implicit def catsDataEqForNonEmptyList[A](implicit A: Eq[A]): Eq[NonEmptyList[A]] = | ||
new NonEmptyListEq[A] { | ||
val A0 = A | ||
} | ||
} | ||
|
||
private[data] sealed trait NonEmptyListEq[A] extends Eq[NonEmptyList[A]] { | ||
implicit def A0: Eq[A] | ||
|
||
override def eqv(x: NonEmptyList[A], y: NonEmptyList[A]): Boolean = x === y | ||
} | ||
|
||
private[data] sealed trait NonEmptyListPartialOrder[A] extends PartialOrder[NonEmptyList[A]] with NonEmptyListEq[A] { | ||
override implicit def A0: PartialOrder[A] | ||
|
||
override def partialCompare(x: NonEmptyList[A], y: NonEmptyList[A]): Double = | ||
x.toList partialCompare y.toList | ||
} | ||
|
||
private[data] sealed abstract class NonEmptyListOrder[A] extends Order[NonEmptyList[A]] with NonEmptyListPartialOrder[A] { | ||
override implicit def A0: Order[A] | ||
|
||
override def compare(x: NonEmptyList[A], y: NonEmptyList[A]): Int = | ||
x.toList compare y.toList | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we add
a :: nel
to make a new NonEmptyList?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added.