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

cats.data.OneAnd.scala Maven / Gradle / Ivy

The newest version!
package cats
package data

import scala.annotation.tailrec
import kernel.compat.scalaVersionSpecific._

/**
 * A data type which represents a single element (head) and some other
 * structure (tail). As we have done in package.scala, this can be
 * used to represent a Stream which is guaranteed to not be empty:
 *
 * {{{
 * type NonEmptyStream[A] = OneAnd[Stream, A]
 * }}}
 */
final case class OneAnd[F[_], A](head: A, tail: F[A]) {

  /**
   * Combine the head and tail into a single `F[A]` value.
   */
  def unwrap(implicit F: Alternative[F]): F[A] =
    F.combineK(F.pure(head), tail)

  /**
   * remove elements not matching the predicate
   */
  def filter(f: A => Boolean)(implicit FA: Alternative[F], FM: Monad[F]): F[A] = {
    val rest = FM.flatMap(tail)(a => if (f(a)) FM.pure(a) else FA.empty[A])
    if (f(head)) FA.combineK(FM.pure(head), rest) else rest
  }

  /**
   * Append another OneAnd to this
   */
  def combine(other: OneAnd[F, A])(implicit F: Alternative[F]): OneAnd[F, A] =
    OneAnd(head, F.combineK(tail, F.combineK(F.pure(other.head), other.tail)))

  /**
   * find the first element matching the predicate, if one exists
   */
  def find(f: A => Boolean)(implicit F: Foldable[F]): Option[A] =
    if (f(head)) Some(head) else F.find(tail)(f)

  /**
   * Check whether at least one element satisfies the predicate.
   */
  def exists(p: A => Boolean)(implicit F: Foldable[F]): Boolean =
    p(head) || F.exists(tail)(p)

  /**
   * Check whether all elements satisfy the predicate.
   */
  def forall(p: A => Boolean)(implicit F: Foldable[F]): Boolean =
    p(head) && F.forall(tail)(p)

  def reduceLeft(f: (A, A) => A)(implicit F: Foldable[F]): A =
    F.foldLeft(tail, head)(f)

  /**
   * Left-associative fold on the structure using f.
   */
  def foldLeft[B](b: B)(f: (B, A) => B)(implicit F: Foldable[F]): B =
    F.foldLeft(tail, f(b, head))(f)

  /**
   * Right-associative fold on the structure using f.
   */
  def foldRight[B](lb: Eval[B])(f: (A, Eval[B]) => Eval[B])(implicit F: Foldable[F]): Eval[B] =
    Eval.defer(f(head, F.foldRight(tail, lb)(f)))

  /**
   * Applies f to all the elements of the structure
   */
  def map[B](f: A => B)(implicit F: Functor[F]): OneAnd[F, B] =
    OneAnd(f(head), F.map(tail)(f))

  /**
   * Modify the context `F` using transformation `f`.
   */
  def mapK[G[_]](f: F ~> G): OneAnd[G, A] =
    OneAnd(head, f(tail))

  /**
   * Typesafe equality operator.
   *
   * This method is similar to == except that it only allows two
   * OneAnd[F, A] values to be compared to each other, and uses
   * equality provided by Eq[_] instances, rather than using the
   * universal equality provided by .equals.
   */
  def ===(that: OneAnd[F, A])(implicit A: Eq[A], FA: Eq[F[A]]): Boolean =
    A.eqv(head, that.head) && FA.eqv(tail, that.tail)

  /**
   * Typesafe stringification method.
   *
   * This method is similar to .toString except that it stringifies
   * values according to Show[_] instances, rather than using the
   * universal .toString method.
   */
  def show(implicit A: Show[A], FA: Show[F[A]]): String =
    s"OneAnd(${A.show(head)}, ${FA.show(tail)})"
}

@suppressUnusedImportWarningForScalaVersionSpecific
sealed abstract private[data] class OneAndInstances extends OneAndLowPriority0 {

  implicit def catsDataParallelForOneAnd[A, M[_]: Alternative, F0[_]: Alternative](
    implicit P: Parallel.Aux[M, F0]
  ): Parallel.Aux[OneAnd[M, *], OneAnd[F0, *]] =
    new Parallel[OneAnd[M, *]] {
      type F[x] = OneAnd[F0, x]
      def monad: Monad[OneAnd[M, *]] = catsDataMonadForOneAnd(P.monad, Alternative[M])

      def applicative: Applicative[OneAnd[F0, *]] = catsDataApplicativeForOneAnd(Alternative[F0])

      def sequential: OneAnd[F0, *] ~> OneAnd[M, *] =
        λ[OneAnd[F0, *] ~> OneAnd[M, *]](ofa => OneAnd(ofa.head, P.sequential(ofa.tail)))

      def parallel: OneAnd[M, *] ~> OneAnd[F0, *] =
        λ[OneAnd[M, *] ~> OneAnd[F0, *]](ofa => OneAnd(ofa.head, P.parallel(ofa.tail)))

    }

  implicit def catsDataEqForOneAnd[A, F[_]](implicit A: Eq[A], FA: Eq[F[A]]): Eq[OneAnd[F, A]] =
    new Eq[OneAnd[F, A]] {
      def eqv(x: OneAnd[F, A], y: OneAnd[F, A]): Boolean = x === y
    }

  implicit def catsDataShowForOneAnd[A, F[_]](implicit A: Show[A], FA: Show[F[A]]): Show[OneAnd[F, A]] =
    Show.show[OneAnd[F, A]](_.show)

  implicit def catsDataSemigroupKForOneAnd[F[_]: Alternative]: SemigroupK[OneAnd[F, *]] =
    new SemigroupK[OneAnd[F, *]] {
      def combineK[A](a: OneAnd[F, A], b: OneAnd[F, A]): OneAnd[F, A] =
        a.combine(b)
    }

  implicit def catsDataSemigroupForOneAnd[F[_]: Alternative, A]: Semigroup[OneAnd[F, A]] =
    catsDataSemigroupKForOneAnd[F].algebra

  implicit def catsDataMonadForOneAnd[F[_]](implicit monad: Monad[F],
                                            alternative: Alternative[F]): Monad[OneAnd[F, *]] =
    new Monad[OneAnd[F, *]] {
      override def map[A, B](fa: OneAnd[F, A])(f: A => B): OneAnd[F, B] =
        fa.map(f)(monad)

      def pure[A](x: A): OneAnd[F, A] =
        OneAnd(x, alternative.empty)

      def flatMap[A, B](fa: OneAnd[F, A])(f: A => OneAnd[F, B]): OneAnd[F, B] = {
        val end = monad.flatMap(fa.tail) { a =>
          val fa = f(a)
          alternative.combineK(monad.pure(fa.head), fa.tail)
        }
        val fst = f(fa.head)
        OneAnd(fst.head, alternative.combineK(fst.tail, end))
      }

      def tailRecM[A, B](a: A)(fn: A => OneAnd[F, Either[A, B]]): OneAnd[F, B] = {
        def stepF(a: A): F[Either[A, B]] = {
          val oneAnd = fn(a)
          alternative.combineK(monad.pure(oneAnd.head), oneAnd.tail)
        }
        def toFB(in: Either[A, B]): F[B] = in match {
          case Right(b) => monad.pure(b)
          case Left(a)  => monad.tailRecM(a)(stepF)
        }

        // This could probably be in SemigroupK to perform well
        @tailrec
        def combineAll(items: List[F[B]]): F[B] = items match {
          case Nil              => alternative.empty
          case h :: Nil         => h
          case h1 :: h2 :: tail => combineAll(alternative.combineK(h1, h2) :: tail)
        }

        @tailrec
        def go(in: A, rest: List[F[B]]): OneAnd[F, B] =
          fn(in) match {
            case OneAnd(Right(b), tail) =>
              val fbs = monad.flatMap(tail)(toFB)
              OneAnd(b, combineAll(fbs :: rest))
            case OneAnd(Left(a), tail) =>
              val fbs = monad.flatMap(tail)(toFB)
              go(a, fbs :: rest)
          }

        go(a, Nil)
      }
    }
}

sealed abstract private[data] class OneAndLowPriority3 extends OneAndLowPriority4 {

  implicit def catsDataFunctorForOneAnd[F[_]](implicit F: Functor[F]): Functor[OneAnd[F, *]] =
    new Functor[OneAnd[F, *]] {
      def map[A, B](fa: OneAnd[F, A])(f: A => B): OneAnd[F, B] =
        fa.map(f)
    }

}

sealed abstract private[data] class OneAndLowPriority2 extends OneAndLowPriority3 {

  implicit def catsDataApplicativeForOneAnd[F[_]](implicit F: Alternative[F]): Applicative[OneAnd[F, *]] =
    new Applicative[OneAnd[F, *]] {
      override def map[A, B](fa: OneAnd[F, A])(f: A => B): OneAnd[F, B] =
        fa.map(f)

      def pure[A](x: A): OneAnd[F, A] =
        OneAnd(x, F.empty)

      override def ap[A, B](ff: OneAnd[F, A => B])(fa: OneAnd[F, A]): OneAnd[F, B] = {
        val (f, tf) = (ff.head, ff.tail)
        val (a, ta) = (fa.head, fa.tail)
        val fb = F.ap(tf)(F.combineK(F.pure(a), ta))
        OneAnd(f(a), F.combineK(F.map(ta)(f), fb))
      }
    }

}

sealed abstract private[data] class OneAndLowPriority1 extends OneAndLowPriority2 {

  implicit def catsDataTraverseForOneAnd[F[_]](implicit F: Traverse[F]): Traverse[OneAnd[F, *]] =
    new Traverse[OneAnd[F, *]] {
      def traverse[G[_], A, B](fa: OneAnd[F, A])(f: (A) => G[B])(implicit G: Applicative[G]): G[OneAnd[F, B]] =
        G.map2Eval(f(fa.head), Always(F.traverse(fa.tail)(f)))(OneAnd(_, _)).value

      def foldLeft[A, B](fa: OneAnd[F, A], b: B)(f: (B, A) => B): B =
        fa.foldLeft(b)(f)

      def foldRight[A, B](fa: OneAnd[F, A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
        fa.foldRight(lb)(f)
    }
}

sealed abstract private[data] class OneAndLowPriority0_5 extends OneAndLowPriority1 {
  implicit def catsDataReducibleForOneAnd[F[_]](implicit F: Foldable[F]): Reducible[OneAnd[F, *]] =
    new NonEmptyReducible[OneAnd[F, *], F] {
      override def split[A](fa: OneAnd[F, A]): (A, F[A]) = (fa.head, fa.tail)

      override def get[A](fa: OneAnd[F, A])(idx: Long): Option[A] =
        if (idx == 0L) Some(fa.head) else F.get(fa.tail)(idx - 1L)

      override def size[A](fa: OneAnd[F, A]): Long = 1 + F.size(fa.tail)
    }
}

sealed abstract private[data] class OneAndLowPriority0 extends OneAndLowPriority0_5 {
  implicit def catsDataNonEmptyTraverseForOneAnd[F[_]](implicit F: Traverse[F],
                                                       F2: Alternative[F]): NonEmptyTraverse[OneAnd[F, *]] =
    new NonEmptyReducible[OneAnd[F, *], F] with NonEmptyTraverse[OneAnd[F, *]] {
      def nonEmptyTraverse[G[_], A, B](fa: OneAnd[F, A])(f: (A) => G[B])(implicit G: Apply[G]): G[OneAnd[F, B]] = {
        import cats.syntax.apply._

        fa.map(a => Apply[G].map(f(a))(OneAnd(_, F2.empty[B])))(F)
          .reduceLeft(((acc, a) => (acc, a).mapN((x: OneAnd[F, B], y: OneAnd[F, B]) => x.combine(y))))
      }

      override def traverse[G[_], A, B](fa: OneAnd[F, A])(f: (A) => G[B])(implicit G: Applicative[G]): G[OneAnd[F, B]] =
        G.map2Eval(f(fa.head), Always(F.traverse(fa.tail)(f)))(OneAnd(_, _)).value

      def split[A](fa: OneAnd[F, A]): (A, F[A]) = (fa.head, fa.tail)
    }
}

object OneAnd extends OneAndInstances




© 2015 - 2024 Weber Informatics LLC | Privacy Policy