All Downloads are FREE. Search and download functionalities are using the official Maven repository.

arrow.data.NonEmptyList.kt Maven / Gradle / Ivy

package arrow.data

import arrow.*
import arrow.core.*
import arrow.typeclasses.Applicative
import arrow.typeclasses.foldable

typealias Nel = NonEmptyList

/**
 * A List that can not be empty
 */
@higherkind
class NonEmptyList private constructor(
        val head: A,
        val tail: List,
        val all: List) : NonEmptyListKind {

    constructor(head: A, tail: List) : this(head, tail, listOf(head) + tail)
    private constructor(list: List) : this(list[0], list.drop(1), list)

    val size: Int = all.size

    fun contains(element: @UnsafeVariance A): Boolean = (head == element) || element in tail

    fun containsAll(elements: Collection<@UnsafeVariance A>): Boolean = elements.all(this::contains)

    fun isEmpty(): Boolean = false

    fun  map(f: (A) -> B): NonEmptyList = NonEmptyList(f(head), tail.map(f))

    fun  flatMap(f: (A) -> NonEmptyListKind): NonEmptyList = f(head).ev() + tail.flatMap { f(it).ev().all }

    fun  ap(ff: NonEmptyListKind<(A) -> B>): NonEmptyList = ff.ev().flatMap { f -> map(f) }.ev()

    operator fun plus(l: NonEmptyList<@UnsafeVariance A>): NonEmptyList = NonEmptyList(all + l.all)

    operator fun plus(l: List<@UnsafeVariance A>): NonEmptyList = NonEmptyList(all + l)

    operator fun plus(a: @UnsafeVariance A): NonEmptyList = NonEmptyList(all + a)

    fun  foldLeft(b: B, f: (B, A) -> B): B = this.ev().tail.fold(f(b, this.ev().head), f)

    fun  foldRight(lb: Eval, f: (A, Eval) -> Eval): Eval = foldable().foldRight(this.ev().all.k(), lb, f)

    fun  traverse(f: (A) -> HK, GA: Applicative): HK> =
            GA.map2Eval(f(this.ev().head), Eval.always {
                arrow.typeclasses.traverse().traverse(this.ev().tail.k(), f, GA)
            }, {
                NonEmptyList(it.a, it.b.ev().list)
            }).value()

    fun  coflatMap(f: (NonEmptyListKind) -> B): NonEmptyList {
        val buf = mutableListOf()
        tailrec fun consume(list: List): List =
                if (list.isEmpty()) {
                    buf
                } else {
                    val tail = list.subList(1, list.size)
                    buf += f(NonEmptyList(list[0], tail))
                    consume(tail)
                }
        return NonEmptyList(f(this), consume(this.ev().tail))
    }

    fun extract(): A = this.ev().head

    fun iterator(): Iterator = all.iterator()

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other?.javaClass != javaClass) return false

        other as NonEmptyList<*>

        if (all != other.all) return false

        return true
    }

    fun show(): String = all.joinToString()

    override fun hashCode(): Int = all.hashCode()

    override fun toString(): String = "NonEmptyList(all=$all)"

    companion object {
        fun  of(head: A, vararg t: A): NonEmptyList = NonEmptyList(head, t.asList())
        fun  fromList(l: List): Option> = if (l.isEmpty()) None else Some(NonEmptyList(l))
        fun  fromListUnsafe(l: List): NonEmptyList = NonEmptyList(l)

        fun  pure(a: A): NonEmptyList = a.nel()

        @Suppress("UNCHECKED_CAST")
        private tailrec fun  go(
                buf: ArrayList,
                f: (A) -> HK>,
                v: NonEmptyList>) {
            val head: Either = v.head
            when (head) {
                is Either.Right -> {
                    buf += head.b
                    val x = fromList(v.tail)
                    when (x) {
                        is Some>> -> go(buf, f, x.t)
                        is None -> Unit
                    }
                }
                is Either.Left -> go(buf, f, f(head.a).ev() + v.tail)
            }
        }

        fun  tailRecM(a: A, f: (A) -> HK>): NonEmptyList {
            val buf = ArrayList()
            go(buf, f, f(a).ev())
            return fromListUnsafe(buf)
        }

    }
}

fun  A.nel(): NonEmptyList = NonEmptyList.of(this)

fun  NonEmptyList.combineK(y: NonEmptyListKind): NonEmptyList = this.plus(y.ev())