Skip to content

Commit bbd6664

Browse files
committed
Cleanup comments, add LazyListCompat, add numeric step functions
1 parent 9debe1c commit bbd6664

File tree

5 files changed

+102
-42
lines changed

5 files changed

+102
-42
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package cats
2+
package kernel
3+
4+
private[kernel] object ScalaVersionSpecificLazyListCompat extends LazyListCompatBase {
5+
override final type T[A] = scala.collection.immutable.Stream[A]
6+
7+
override final def apply[A](a: A*): T[A] =
8+
scala.collection.immutable.Stream.apply[A](a: _*)
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package cats
2+
package kernel
3+
4+
private[kernel] object ScalaVersionSpecificLazyListCompat extends LazyListCompatBase {
5+
override final type T[A] = scala.collection.immutable.LazyList[A]
6+
7+
override final def apply[A](a: A*): T[A] =
8+
scala.collection.immutable.LazyList.apply[A](a: _*)
9+
}

kernel/src/main/scala/cats/kernel/Enumerable.scala

Lines changed: 72 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,15 @@
2222
package cats
2323
package kernel
2424

25-
import scala.collection.immutable.LazyList
2625
import scala.annotation.tailrec
2726
import scala.{specialized => sp}
27+
import cats.kernel.{ScalaVersionSpecificLazyListCompat => LazyListLike}
2828

2929
/** A typeclass for types which are countable. Formally this means that values
3030
* can be mapped on to the natural numbers.
3131
*
32-
* Because Countable types may be mapped to the natural numbers, being an
33-
* instance of `Countable` implies having a total ordering, e.g. an `Order`
32+
* Because Enumerable types may be mapped to the natural numbers, being an
33+
* instance of `Enumerable` implies having a total ordering, e.g. an `Order`
3434
* instance. It also implies having a `PartialNext` and `PartialPrevious` as
3535
* all representations of the countable numbers, or a subset there of, have
3636
* `PartialNext` and `PartialPrevious`.
@@ -42,52 +42,65 @@ import scala.{specialized => sp}
4242
* @see [[https://hackage.haskell.org/package/base-4.15.0.0/docs/GHC-Enum.html]]
4343
* @see [[https://en.wikipedia.org/wiki/Countable_set]]
4444
*/
45-
trait Countable[@sp A] extends PartialNext[A] with PartialPrevious[A]{
45+
trait Enumerable[@sp A] extends PartialNext[A] with PartialPrevious[A]{
4646
def order: Order[A]
4747
def fromEnum(a: A): BigInt
4848
def toEnum(i: BigInt): Option[A]
4949

50-
/** The fundamental function in the `Countable` class. Given a start position,
51-
* an offset, and an optional last position, enumerate the values between
52-
* `first` and `last` (or `MaxValue` or infinity), using a step of `by -
53-
* first`.
50+
/** The fundamental function in the `Enumerable` class. Given a `first`
51+
* element, a second element, and an optional `last` element, enumerate the
52+
* values between `first` and `last` (or `MaxValue` or infinity), the step
53+
* between the first and second element as the step between all elements.
5454
*
5555
* {{{
56-
* scala> Countable[Int].enumFromThenToOpt(1, 3, Some(11)).toList
56+
* scala> Enumerable[Int].enumFromThenToOpt(1, 3, Some(11)).toList
5757
* val res0: List[Int] = List(1, 3, 5, 7, 9, 11)
5858
* }}}
5959
*
60+
* @note If the last element is defined, and the second element is less
61+
* than the last element, then the last element will not be part of
62+
* the result.
63+
*
64+
* @note The last element will only be included in the enumerated result if
65+
* it aligns with the step. For example, `enumFromThenToOpt(1, 3,
66+
* 6).toList`, would be `List(1, 3, 5)`.
67+
*
68+
* {{{
69+
* scala> Enumerable[Int].enumFromThenToOpt(1, 2, Some(1)).toList
70+
* val res0: List[Int] = List(1)
71+
* }}}
72+
*
6073
* All other enum like functions can be expressed in terms of this
6174
* function.
6275
*/
63-
def enumFromThenToOpt(first: A, by: A, last: Option[A]): LazyList[A] = {
76+
def enumFromThenToOpt(first: A, second: A, last: Option[A]): LazyListLike.T[A] = {
6477
val Zero: BigInt = BigInt(0)
65-
val increment: BigInt = fromEnum(by) - fromEnum(first)
78+
val increment: BigInt = fromEnum(second) - fromEnum(first)
6679

67-
def loop(i: A): LazyList[A] =
80+
def loop(i: A): LazyListLike.T[A] =
6881
if (increment > Zero) {
6982
// forwards
7083
partialNextByN(i, increment) match {
7184
case Some(next) =>
7285
if (last.fold(false)(order.gt(next, _))) {
73-
LazyList.empty[A]
86+
LazyListLike.empty[A]
7487
} else {
7588
next #:: loop(next)
7689
}
7790
case _ =>
78-
LazyList.empty[A]
91+
LazyListLike.empty[A]
7992
}
8093
} else {
8194
// backwards or zero
8295
partialPreviousByN(i, increment.abs) match {
8396
case Some(next) =>
8497
if (last.fold(false)(order.lt(next, _))) {
85-
LazyList.empty
98+
LazyListLike.empty
8699
} else {
87100
next #:: loop(next)
88101
}
89102
case _ =>
90-
LazyList.empty
103+
LazyListLike.empty
91104
}
92105
}
93106

@@ -96,13 +109,13 @@ trait Countable[@sp A] extends PartialNext[A] with PartialPrevious[A]{
96109
order.compare(first, last) match {
97110
case result if result < Zero =>
98111
if (increment < Zero) {
99-
LazyList.empty[A]
112+
LazyListLike.empty[A]
100113
} else {
101114
first #:: loop(first)
102115
}
103116
case result if result > Zero =>
104117
if (increment > Zero) {
105-
LazyList.empty[A]
118+
LazyListLike.empty[A]
106119
} else {
107120
first #:: loop(first)
108121
}
@@ -114,69 +127,88 @@ trait Countable[@sp A] extends PartialNext[A] with PartialPrevious[A]{
114127
}
115128
}
116129

117-
/** Given a start position, an offset, and a last position, enumerate the
118-
* values between `first` and `last`, using a step of `by - first`.
130+
def enumFromByToOpt(first: A, step: BigInt, last: Option[A]): LazyListLike.T[A] =
131+
toEnum(step).fold(
132+
LazyListLike.empty[A]
133+
)(second =>
134+
enumFromThenToOpt(first, second, last)
135+
)
136+
137+
/** Given a `first` element, a second element, and a last, enumerate the
138+
* values between `first` and `last`, using a step between the `first` and
139+
* the `second` element as step between all elements.
119140
*
120141
* {{{
121-
* scala> Countable[Int].enumFromThenTo(1, 3, 11).toList
142+
* scala> Enumerable[Int].enumFromThenTo(1, 3, 11).toList
122143
* val res0: List[Int] = List(1, 3, 5, 7, 9, 11)
123144
* }}}
145+
*
146+
* @see [[#enumFromThenToOpt]]
124147
*/
125-
def enumFromThenTo(first: A, by: A, last: A): LazyList[A] =
126-
enumFromThenToOpt(first, by, Some(last))
148+
def enumFromThenTo(first: A, second: A, last: A): LazyListLike.T[A] =
149+
enumFromThenToOpt(first, second, Some(last))
127150

128-
/** Given a start position and a last position, enumerate the
151+
def enumFromByTo(first: A, step: BigInt, last: A): LazyListLike.T[A] =
152+
enumFromByToOpt(first, step, Some(last))
153+
154+
/** Given a first element and a last element, enumerate the
129155
* values between `first` and `last`, using a step of 1.
130156
*
131157
* {{{
132-
* scala> Countable[Int].enumFromTo(1, 5).toList
158+
* scala> Enumerable[Int].enumFromTo(1, 5).toList
133159
* val res0: List[Int] = List(1, 2, 3, 4, 5)
134160
* }}}
135161
*/
136-
def enumFromTo(first: A, last: A): LazyList[A] =
162+
def enumFromTo(first: A, last: A): LazyListLike.T[A] =
137163
partialNext(first) match {
138164
case Some(by) =>
139165
enumFromThenTo(first, by, last)
140166
case _ =>
141167
if (order.lteqv(first, last)) {
142-
LazyList(first)
168+
LazyListLike(first)
143169
} else {
144-
LazyList.empty
170+
LazyListLike.empty
145171
}
146172
}
147173

148-
/** Given a start position and a increment, enumerate the values starting at
149-
* `first` until `MaxValue` or infinity if the type is unbounded.
174+
/** Given a first element and second element, enumerate all values in the
175+
* domain starting at first using the step between first and second as the
176+
* step between all elements. If the domain is infinite, e.g. natural
177+
* numbers or `BigInt`, then this will be an infinite result.
150178
*
151179
* {{{
152-
* scala> Countable[Int].enumFromThen(Int.MaxValue - 5, Int.MaxValue - 4).toList
180+
* scala> Enumerable[Int].enumFromThen(Int.MaxValue - 5, Int.MaxValue - 4).toList
153181
* val res0: List[Int] = List(2147483642, 2147483643, 2147483644, 2147483645, 2147483646, 2147483647)
154182
* }}}
155183
*/
156-
def enumFromThen(first: A, by: A): LazyList[A] =
157-
enumFromThenToOpt(first, by, None)
184+
def enumFromThen(first: A, second: A): LazyListLike.T[A] =
185+
enumFromThenToOpt(first, second, None)
186+
187+
def enumFromBy(first: A, by: BigInt): LazyListLike.T[A] =
188+
enumFromByToOpt(first, by, None)
158189

159-
/** Given a start position, enumerate the values starting at `first` by 1,
160-
* until `MaxValue` or infinity if the type is unbounded.
190+
/** Given a first element, enumerate all values in the domain starting at
191+
* first using a step of 1 between all elements. If the domain is infinite,
192+
* e.g. natural numbers or `BigInt`, then this will be an infinite result.
161193
*
162194
* {{{
163-
* scala> Countable[Int].enumFrom(Int.MaxValue - 5).toList
195+
* scala> Enumerable[Int].enumFrom(Int.MaxValue - 5).toList
164196
* val res0: List[Int] = List(2147483642, 2147483643, 2147483644, 2147483645, 2147483646, 2147483647)
165197
* }}}
166198
*/
167-
def enumFrom(first: A): LazyList[A] =
199+
def enumFrom(first: A): LazyListLike.T[A] =
168200
partialNext(first) match {
169201
case Some(by) =>
170202
enumFromThen(first, by)
171203
case _ =>
172-
LazyList(first)
204+
LazyListLike(first)
173205
}
174206

175207
override final def partialOrder: PartialOrder[A] = order
176208
}
177209

178-
object Countable {
179-
def apply[A](implicit A: Countable[A]): Countable[A] = A
210+
object Enumerable {
211+
def apply[A](implicit A: Enumerable[A]): Enumerable[A] = A
180212
}
181213

182214
/**
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package cats
2+
package kernel
3+
4+
private[kernel] trait LazyListCompatBase{
5+
type T[A]
6+
7+
def apply[A](a: A*): T[A]
8+
9+
final def empty[A]: T[A] = apply[A]()
10+
}

kernel/src/main/scala/cats/kernel/instances/IntInstances.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ package cats.kernel
2323
package instances
2424

2525
trait IntInstances {
26-
implicit val catsKernelStdOrderForInt: Order[Int] with Hash[Int] with BoundedEnumerable[Int] with Countable[Int] =
26+
implicit val catsKernelStdOrderForInt: Order[Int] with Hash[Int] with BoundedEnumerable[Int] with Enumerable[Int] =
2727
new IntOrder
2828
implicit val catsKernelStdGroupForInt: CommutativeGroup[Int] = new IntGroup
2929
}
@@ -47,7 +47,7 @@ trait IntBounded extends LowerBounded[Int] with UpperBounded[Int] {
4747
override def maxBound: Int = Int.MaxValue
4848
}
4949

50-
class IntOrder extends Order[Int] with Hash[Int] with IntBounded with IntEnumerable with Countable[Int] { self =>
50+
class IntOrder extends Order[Int] with Hash[Int] with IntBounded with IntEnumerable with Enumerable[Int] { self =>
5151
def hash(x: Int): Int = x.hashCode()
5252
def compare(x: Int, y: Int): Int =
5353
if (x < y) -1 else if (x > y) 1 else 0

0 commit comments

Comments
 (0)