Skip to content

Commit 3ab307b

Browse files
authored
Merge pull request #1231 from ceedubs/nel
Finish up work on NonEmptyList
2 parents 910a42a + cfdd085 commit 3ab307b

17 files changed

+570
-114
lines changed

core/src/main/scala/cats/Reducible.scala

+7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package cats
22

3+
import cats.data.NonEmptyList
4+
35
import simulacrum.typeclass
46

57
/**
@@ -113,6 +115,11 @@ import simulacrum.typeclass
113115
def sequence1_[G[_], A](fga: F[G[A]])(implicit G: Apply[G]): G[Unit] =
114116
G.map(reduceLeft(fga)((x, y) => G.map2(x, y)((_, b) => b)))(_ => ())
115117

118+
def toNonEmptyList[A](fa: F[A]): NonEmptyList[A] =
119+
reduceRightTo(fa)(a => NonEmptyList(a, Nil)) { (a, lnel) =>
120+
lnel.map { case NonEmptyList(h, t) => NonEmptyList(a, h :: t) }
121+
}.value
122+
116123
def compose[G[_]: Reducible]: Reducible[λ[α => F[G[α]]]] =
117124
new ComposedReducible[F, G] {
118125
val F = self

core/src/main/scala/cats/Traverse.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,9 @@ import simulacrum.typeclass
9696
* scala> import cats.implicits._
9797
* scala> val x: List[ValidatedNel[String, Int]] = List(Validated.valid(1), Validated.invalid("a"), Validated.invalid("b")).map(_.toValidatedNel)
9898
* scala> x.sequenceU
99-
* res0: cats.data.ValidatedNel[String,List[Int]] = Invalid(OneAnd(a,List(b)))
99+
* res0: cats.data.ValidatedNel[String,List[Int]] = Invalid(NonEmptyList(a, b))
100100
* scala> x.sequence[ValidatedNel[String, ?], Int]
101-
* res1: cats.data.ValidatedNel[String,List[Int]] = Invalid(OneAnd(a,List(b)))
101+
* res1: cats.data.ValidatedNel[String,List[Int]] = Invalid(NonEmptyList(a, b))
102102
* }}}
103103
*/
104104
def sequenceU[GA](fga: F[GA])(implicit U: Unapply[Applicative, GA]): U.M[F[U.A]] =
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
package cats
2+
package data
3+
4+
import cats.instances.list._
5+
import cats.syntax.order._
6+
7+
import scala.annotation.tailrec
8+
import scala.collection.mutable.ListBuffer
9+
10+
/**
11+
* A data type which represents a non empty list of A, with
12+
* single element (head) and optional structure (tail).
13+
*/
14+
final case class NonEmptyList[A](head: A, tail: List[A]) {
15+
16+
/**
17+
* Return the head and tail into a single list
18+
*/
19+
def toList: List[A] = head :: tail
20+
21+
/**
22+
* Applies f to all the elements of the structure
23+
*/
24+
def map[B](f: A => B): NonEmptyList[B] =
25+
NonEmptyList(f(head), tail.map(f))
26+
27+
def ++(l: List[A]): NonEmptyList[A] =
28+
NonEmptyList(head, tail ++ l)
29+
30+
def flatMap[B](f: A => NonEmptyList[B]): NonEmptyList[B] =
31+
f(head) ++ tail.flatMap(f andThen (_.toList))
32+
33+
def ::(a: A): NonEmptyList[A] = NonEmptyList(a, head :: tail)
34+
35+
/**
36+
* remove elements not matching the predicate
37+
*/
38+
def filter(p: A => Boolean): List[A] = {
39+
val ftail = tail.filter(p)
40+
if (p(head)) head :: ftail
41+
else ftail
42+
}
43+
44+
/**
45+
* Append another NonEmptyList
46+
*/
47+
def concat(other: NonEmptyList[A]): NonEmptyList[A] =
48+
NonEmptyList(head, tail ::: other.toList)
49+
50+
/**
51+
* Find the first element matching the predicate, if one exists
52+
*/
53+
def find(p: A => Boolean): Option[A] =
54+
if (p(head)) Some(head)
55+
else tail.find(p)
56+
57+
/**
58+
* Check whether at least one element satisfies the predicate
59+
*/
60+
def exists(p: A => Boolean): Boolean =
61+
p(head) || tail.exists(p)
62+
63+
/**
64+
* Check whether all elements satisfy the predicate
65+
*/
66+
def forall(p: A => Boolean): Boolean =
67+
p(head) && tail.forall(p)
68+
69+
/**
70+
* Left-associative fold on the structure using f.
71+
*/
72+
def foldLeft[B](b: B)(f: (B, A) => B): B =
73+
tail.foldLeft(f(b, head))(f)
74+
75+
/**
76+
* Right-associative fold on the structure using f.
77+
*/
78+
def foldRight[B](lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
79+
Foldable[List].foldRight(toList, lb)(f)
80+
81+
/**
82+
* Left-associative reduce using f.
83+
*/
84+
def reduceLeft(f: (A, A) => A): A =
85+
tail.foldLeft(head)(f)
86+
87+
def traverse[G[_], B](f: A => G[B])(implicit G: Applicative[G]): G[NonEmptyList[B]] =
88+
G.map2Eval(f(head), Always(Traverse[List].traverse(tail)(f)))(NonEmptyList(_, _)).value
89+
90+
def coflatMap[B](f: NonEmptyList[A] => B): NonEmptyList[B] = {
91+
val buf = ListBuffer.empty[B]
92+
@tailrec def consume(as: List[A]): List[B] =
93+
as match {
94+
case Nil => buf.toList
95+
case a :: as =>
96+
buf += f(NonEmptyList(a, as))
97+
consume(as)
98+
}
99+
NonEmptyList(f(this), consume(tail))
100+
}
101+
102+
def ===(o: NonEmptyList[A])(implicit A: Eq[A]): Boolean =
103+
(this.head === o.head) && this.tail === o.tail
104+
105+
def show(implicit A: Show[A]): String =
106+
toList.iterator.map(A.show).mkString("NonEmptyList(", ", ", ")")
107+
108+
override def toString: String = s"NonEmpty$toList"
109+
}
110+
111+
object NonEmptyList extends NonEmptyListInstances {
112+
def apply[A](head: A, tail: A*): NonEmptyList[A] = NonEmptyList(head, tail.toList)
113+
114+
/**
115+
* Create a `NonEmptyList` from a `List`.
116+
*
117+
* The result will be `None` if the input list is empty and `Some` wrapping a
118+
* `NonEmptyList` otherwise.
119+
*
120+
* @see [[fromListUnsafe]] for an unsafe version that throws an exception if
121+
* the input list is empty.
122+
*/
123+
def fromList[A](l: List[A]): Option[NonEmptyList[A]] =
124+
l match {
125+
case Nil => None
126+
case h :: t => Some(NonEmptyList(h, t))
127+
}
128+
129+
/**
130+
* Create a `NonEmptyList` from a `List`, or throw an
131+
* `IllegalArgumentException` if the input list is empty.
132+
*
133+
* @see [[fromList]] for a safe version that returns `None` if the input list
134+
* is empty.
135+
*/
136+
def fromListUnsafe[A](l: List[A]): NonEmptyList[A] =
137+
l match {
138+
case Nil => throw new IllegalArgumentException("Cannot create NonEmptyList from empty list")
139+
case h :: t => NonEmptyList(h, t)
140+
}
141+
142+
def fromReducible[F[_], A](fa: F[A])(implicit F: Reducible[F]): NonEmptyList[A] =
143+
F.toNonEmptyList(fa)
144+
}
145+
146+
private[data] sealed trait NonEmptyListInstances extends NonEmptyListInstances0 {
147+
148+
implicit val catsDataInstancesForNonEmptyList: SemigroupK[NonEmptyList] with Reducible[NonEmptyList]
149+
with Comonad[NonEmptyList] with Traverse[NonEmptyList] with MonadRec[NonEmptyList] =
150+
new NonEmptyReducible[NonEmptyList, List] with SemigroupK[NonEmptyList]
151+
with Comonad[NonEmptyList] with Traverse[NonEmptyList] with MonadRec[NonEmptyList] {
152+
153+
def combineK[A](a: NonEmptyList[A], b: NonEmptyList[A]): NonEmptyList[A] =
154+
a concat b
155+
156+
override def split[A](fa: NonEmptyList[A]): (A, List[A]) = (fa.head, fa.tail)
157+
158+
override def reduceLeft[A](fa: NonEmptyList[A])(f: (A, A) => A): A =
159+
fa.reduceLeft(f)
160+
161+
override def map[A, B](fa: NonEmptyList[A])(f: A => B): NonEmptyList[B] =
162+
fa map f
163+
164+
def pure[A](x: A): NonEmptyList[A] =
165+
NonEmptyList(x, List.empty)
166+
167+
def flatMap[A, B](fa: NonEmptyList[A])(f: A => NonEmptyList[B]): NonEmptyList[B] =
168+
fa flatMap f
169+
170+
def coflatMap[A, B](fa: NonEmptyList[A])(f: NonEmptyList[A] => B): NonEmptyList[B] =
171+
fa coflatMap f
172+
173+
def extract[A](fa: NonEmptyList[A]): A = fa.head
174+
175+
def traverse[G[_], A, B](fa: NonEmptyList[A])(f: A => G[B])(implicit G: Applicative[G]): G[NonEmptyList[B]] =
176+
fa traverse f
177+
178+
override def foldLeft[A, B](fa: NonEmptyList[A], b: B)(f: (B, A) => B): B =
179+
fa.foldLeft(b)(f)
180+
181+
override def foldRight[A, B](fa: NonEmptyList[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
182+
fa.foldRight(lb)(f)
183+
184+
def tailRecM[A, B](a: A)(f: A => NonEmptyList[A Xor B]): NonEmptyList[B] = {
185+
val buf = new ListBuffer[B]
186+
@tailrec def go(v: NonEmptyList[A Xor B]): Unit = v.head match {
187+
case Xor.Right(b) =>
188+
buf += b
189+
NonEmptyList.fromList(v.tail) match {
190+
case Some(t) => go(t)
191+
case None => ()
192+
}
193+
case Xor.Left(a) => go(f(a) ++ v.tail)
194+
}
195+
go(f(a))
196+
NonEmptyList.fromListUnsafe(buf.result())
197+
}
198+
199+
override def forall[A](fa: NonEmptyList[A])(p: A => Boolean): Boolean =
200+
fa forall p
201+
202+
override def exists[A](fa: NonEmptyList[A])(p: A => Boolean): Boolean =
203+
fa exists p
204+
205+
override def toList[A](fa: NonEmptyList[A]): List[A] = fa.toList
206+
207+
override def toNonEmptyList[A](fa: NonEmptyList[A]): NonEmptyList[A] = fa
208+
}
209+
210+
implicit def catsDataShowForNonEmptyList[A](implicit A: Show[A]): Show[NonEmptyList[A]] =
211+
Show.show[NonEmptyList[A]](_.show)
212+
213+
implicit def catsDataSemigroupForNonEmptyList[A]: Semigroup[NonEmptyList[A]] =
214+
SemigroupK[NonEmptyList].algebra[A]
215+
216+
implicit def catsDataOrderForNonEmptyList[A](implicit A: Order[A]): Order[NonEmptyList[A]] =
217+
new NonEmptyListOrder[A] {
218+
val A0 = A
219+
}
220+
}
221+
222+
private[data] sealed trait NonEmptyListInstances0 extends NonEmptyListInstances1 {
223+
implicit def catsDataPartialOrderForNonEmptyList[A](implicit A: PartialOrder[A]): PartialOrder[NonEmptyList[A]] =
224+
new NonEmptyListPartialOrder[A] {
225+
val A0 = A
226+
}
227+
}
228+
229+
private[data] sealed trait NonEmptyListInstances1 {
230+
231+
implicit def catsDataEqForNonEmptyList[A](implicit A: Eq[A]): Eq[NonEmptyList[A]] =
232+
new NonEmptyListEq[A] {
233+
val A0 = A
234+
}
235+
}
236+
237+
private[data] sealed trait NonEmptyListEq[A] extends Eq[NonEmptyList[A]] {
238+
implicit def A0: Eq[A]
239+
240+
override def eqv(x: NonEmptyList[A], y: NonEmptyList[A]): Boolean = x === y
241+
}
242+
243+
private[data] sealed trait NonEmptyListPartialOrder[A] extends PartialOrder[NonEmptyList[A]] with NonEmptyListEq[A] {
244+
override implicit def A0: PartialOrder[A]
245+
246+
override def partialCompare(x: NonEmptyList[A], y: NonEmptyList[A]): Double =
247+
x.toList partialCompare y.toList
248+
}
249+
250+
private[data] sealed abstract class NonEmptyListOrder[A] extends Order[NonEmptyList[A]] with NonEmptyListPartialOrder[A] {
251+
override implicit def A0: Order[A]
252+
253+
override def compare(x: NonEmptyList[A], y: NonEmptyList[A]): Int =
254+
x.toList compare y.toList
255+
}

core/src/main/scala/cats/data/OneAnd.scala

+13-12
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ package cats
22
package data
33

44
import scala.annotation.tailrec
5-
import scala.collection.mutable.ListBuffer
6-
import cats.instances.list._
5+
import scala.collection.mutable.Builder
6+
import cats.instances.stream._
77

88
/**
99
* A data type which represents a single element (head) and some other
@@ -140,21 +140,22 @@ private[data] sealed trait OneAndInstances extends OneAndLowPriority2 {
140140
}
141141

142142
private[data] trait OneAndLowPriority0 {
143-
implicit val catsDataComonadForOneAnd: Comonad[OneAnd[List, ?]] =
144-
new Comonad[OneAnd[List, ?]] {
145-
def coflatMap[A, B](fa: OneAnd[List, A])(f: OneAnd[List, A] => B): OneAnd[List, B] = {
146-
@tailrec def consume(as: List[A], buf: ListBuffer[B]): List[B] =
147-
as match {
148-
case Nil => buf.toList
149-
case a :: as => consume(as, buf += f(OneAnd(a, as)))
143+
implicit val catsDataComonadForNonEmptyStream: Comonad[OneAnd[Stream, ?]] =
144+
new Comonad[OneAnd[Stream, ?]] {
145+
def coflatMap[A, B](fa: OneAnd[Stream, A])(f: OneAnd[Stream, A] => B): OneAnd[Stream, B] = {
146+
@tailrec def consume(as: Stream[A], buf: Builder[B, Stream[B]]): Stream[B] =
147+
if (as.isEmpty) buf.result
148+
else {
149+
val tail = as.tail
150+
consume(tail, buf += f(OneAnd(as.head, tail)))
150151
}
151-
OneAnd(f(fa), consume(fa.tail, ListBuffer.empty))
152+
OneAnd(f(fa), consume(fa.tail, Stream.newBuilder))
152153
}
153154

154-
def extract[A](fa: OneAnd[List, A]): A =
155+
def extract[A](fa: OneAnd[Stream, A]): A =
155156
fa.head
156157

157-
def map[A, B](fa: OneAnd[List, A])(f: A => B): OneAnd[List, B] =
158+
def map[A, B](fa: OneAnd[Stream, A])(f: A => B): OneAnd[Stream, B] =
158159
fa map f
159160
}
160161
}

core/src/main/scala/cats/data/XorT.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ final case class XorT[F[_], A, B](value: F[A Xor B]) {
173173
* scala> val v2: Validated[NonEmptyList[Error], Int] = Validated.Invalid(NonEmptyList("error 2"))
174174
* scala> val xort: XorT[Option, Error, Int] = XorT(Some(Xor.left("error 3")))
175175
* scala> xort.withValidated { v3 => (v1 |@| v2 |@| v3.leftMap(NonEmptyList(_))).map{ case (i, j, k) => i + j + k } }
176-
* res0: XorT[Option, NonEmptyList[Error], Int] = XorT(Some(Left(OneAnd(error 1,List(error 2, error 3)))))
176+
* res0: XorT[Option, NonEmptyList[Error], Int] = XorT(Some(Left(NonEmptyList(error 1, error 2, error 3))))
177177
* }}}
178178
*/
179179
def withValidated[AA, BB](f: Validated[A, B] => Validated[AA, BB])(implicit F: Functor[F]): XorT[F, AA, BB] =

core/src/main/scala/cats/data/package.scala

-19
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,14 @@
11
package cats
22

33
package object data {
4-
type NonEmptyList[A] = OneAnd[List, A]
54
type NonEmptyStream[A] = OneAnd[Stream, A]
65
type ValidatedNel[E, A] = Validated[NonEmptyList[E], A]
76

8-
def NonEmptyList[A](head: A, tail: List[A] = Nil): NonEmptyList[A] =
9-
OneAnd(head, tail)
10-
def NonEmptyList[A](head: A, tail: A*): NonEmptyList[A] =
11-
OneAnd[List, A](head, tail.toList)
12-
137
def NonEmptyStream[A](head: A, tail: Stream[A] = Stream.empty): NonEmptyStream[A] =
148
OneAnd(head, tail)
159
def NonEmptyStream[A](head: A, tail: A*): NonEmptyStream[A] =
1610
OneAnd(head, tail.toStream)
1711

18-
object NonEmptyList {
19-
def fromReducible[F[_], A](fa: F[A])(implicit F: Reducible[F]): Eval[NonEmptyList[A]] =
20-
F.reduceRightTo(fa)(a => NonEmptyList(a, Nil)) { (a, lnel) =>
21-
lnel.map { case OneAnd(h, t) => OneAnd(a, h :: t) }
22-
}
23-
24-
def fromList[A](la: List[A]): Option[NonEmptyList[A]] =
25-
la match {
26-
case (h :: t) => Some(OneAnd(h, t))
27-
case Nil => None
28-
}
29-
}
30-
3112
type ReaderT[F[_], A, B] = Kleisli[F, A, B]
3213
val ReaderT = Kleisli
3314

core/src/main/scala/cats/syntax/option.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ final class OptionOps[A](val oa: Option[A]) extends AnyVal {
103103
*
104104
* scala> val error1: Option[String] = Some("error!")
105105
* scala> error1.toInvalidNel(3)
106-
* res0: ValidatedNel[String, Int] = Invalid(OneAnd(error!,List()))
106+
* res0: ValidatedNel[String, Int] = Invalid(NonEmptyList(error!))
107107
*
108108
* scala> val error2: Option[String] = None
109109
* scala> error2.toInvalidNel(3)
@@ -149,7 +149,7 @@ final class OptionOps[A](val oa: Option[A]) extends AnyVal {
149149
*
150150
* scala> val result2: Option[Int] = None
151151
* scala> result2.toValidNel("error!")
152-
* res1: ValidatedNel[String, Int] = Invalid(OneAnd(error!,List()))
152+
* res1: ValidatedNel[String, Int] = Invalid(NonEmptyList(error!))
153153
* }}}
154154
*/
155155
def toValidNel[B](b: => B): ValidatedNel[B, A] = oa.fold[ValidatedNel[B, A]](Validated.invalidNel(b))(Validated.Valid(_))

0 commit comments

Comments
 (0)