You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('') and can be up to 35 characters long.
120 lines
3.7 KiB
120 lines
3.7 KiB
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


* lineartime 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 constanttime 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 constanttime operation.


*


* @return


* The new `List`.


*/


def toList: List[A] = head :: tail




/** Converts the `NonEmptyList` to a Scala `Seq`. This is a lineartime


* 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 2tuple 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)


}


}


