|
| 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 | +} |
0 commit comments