121 lines
3.7 KiB
Scala
121 lines
3.7 KiB
Scala
package green.thisfieldwas.embracingnondeterminism.data
|
|
|
|
/** The `NonEmptyList` class encodes a dimension contextualized by the presence
|
|
* of an unknown number of instances of term `A`. The length of the
|
|
* `NonEmptyList` in practices is nondeterministic at runtime without specific
|
|
* knowledge of the inputs to the operation that produces it. In contrast with
|
|
* `List`, a `NonEmptyList` is guaranteed to always hold at least one instance
|
|
* of term `A` in its `head`.
|
|
*
|
|
* This `NonEmptyList` is modeled as a special case of a cons: a typed, present
|
|
* construction of a head and a tail of a potentially empty `List`.
|
|
*
|
|
* As at least one instance of the term `A` is guaranteed to be present, this
|
|
* context is always in the desired case.
|
|
*
|
|
* @param head
|
|
* The present instance of term `A`.
|
|
* @param tail
|
|
* A potentially empty remainder of the list.
|
|
* @tparam A
|
|
* The type contained within the `NonEmptyList`.
|
|
*/
|
|
case class NonEmptyList[+A](head: A, tail: List[A]) {
|
|
|
|
/** The length of the `NonEmptyList`. This will always be at least 1 and is a
|
|
* linear-time operation.
|
|
*
|
|
* @return
|
|
* The length of the `NonEmptyList`.
|
|
*/
|
|
def length: Int = 1 + tail.length
|
|
|
|
/** Conses a new head to this `NonEmptyList`. The resulting `NonEmptyList`
|
|
* should be treated as though its length is guaranteed to only be at least
|
|
* 1. This is a constant-time operation.
|
|
*
|
|
* @param newHead
|
|
* The new head.
|
|
* @tparam B
|
|
* The type of the head.
|
|
* @return
|
|
* The new `NonEmptyList`.
|
|
*/
|
|
def |:[B >: A](newHead: B): NonEmptyList[B] = NonEmptyList(newHead, head :: tail)
|
|
|
|
/** Converts the `NonEmptyList` to a `List`. The resulting `List` should be
|
|
* treated as though it may be empty. This is a constant-time operation.
|
|
*
|
|
* @return
|
|
* The new `List`.
|
|
*/
|
|
def toList: List[A] = head :: tail
|
|
|
|
/** Converts the `NonEmptyList` to a Scala `Seq`. This is a linear-time
|
|
* operation.
|
|
*
|
|
* @return
|
|
* The new `Seq`.
|
|
*/
|
|
def toSeq: Seq[A] = head +: tail.toSeq
|
|
}
|
|
|
|
object |: {
|
|
|
|
/** Allows for idiomatic pattern matching against a `NonEmptyList`:
|
|
*
|
|
* {{{
|
|
* nonEmptyList match {
|
|
* head |: tail => ???
|
|
* }
|
|
* }}}
|
|
*
|
|
* @param nel
|
|
* The `NonEmptyList` to unapply.
|
|
* @tparam A
|
|
* The type contained within the `NonEmptyList`.
|
|
* @return
|
|
* A present 2-tuple of the head and tail.
|
|
*/
|
|
def unapply[A](nel: NonEmptyList[A]): scala.Some[(A, List[A])] = scala.Some((nel.head, nel.tail))
|
|
}
|
|
|
|
object NonEmptyList {
|
|
|
|
/** Allows for the idiomatic construction of a `NonEmptyList` using this
|
|
* syntax:
|
|
*
|
|
* {{{
|
|
* NonEmptyList(1, 2, 3, 4, ...)
|
|
* }}}
|
|
*
|
|
* @param head
|
|
* @param tail
|
|
* @tparam A
|
|
* @return
|
|
*/
|
|
def apply[A](head: A, tail: A*): NonEmptyList[A] = NonEmptyList(head, List(tail: _*))
|
|
|
|
implicit val nonEmptyListFunctor: Functor[NonEmptyList] = new Functor[NonEmptyList] {
|
|
|
|
/** Given a structure `F[A]` and function `f: A => B`, if the structure is
|
|
* in the desired case, then apply the function such that `F[A] => F[B]`.
|
|
* If the structure `F[A]` is in the undesired case, then propagate the
|
|
* undesired case as `F[B]`.
|
|
*
|
|
* @param fa
|
|
* The instance of the structure to apply the function to.
|
|
* @param f
|
|
* The function to apply, if the structure is in the desired case.
|
|
* @tparam A
|
|
* The type of instances contained within the structure.
|
|
* @tparam B
|
|
* The type to map the instances into.
|
|
* @return
|
|
* The resulting structure.
|
|
*/
|
|
def map[A, B](fa: NonEmptyList[A])(f: A => B): NonEmptyList[B] =
|
|
f(fa.head) |: Functor[List].map(fa.tail)(f)
|
|
}
|
|
}
|