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

scaloi.EitherNeitherBoth.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2007 Learning Objects
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package scaloi

import scalaz.{Applicative, Bitraverse, \/}
import scalaz.syntax.either._

/** Either, neither or both type. Represents an `A`, a `B`, both an `A` and a `B`,
  * or neither. Contrast with [[scalaz.\&/]] which does not admit the possibility of
  * neither and [[scalaz.\/]] which further denies the possibility of both. This is
  * isomorphic with [[scala.Option]] of [[scalaz.\&/]] but less opaque.
  *
  * @tparam A the left possible type
  * @tparam B the right possible type
  */
sealed abstract class \|/[A, B] extends Product with Serializable {
  import \|/._

  /** Get this, if present. */
  def thisOption: Option[A] = PartialFunction.condOpt(this) {
    case This(a)    => a
    case Both(a, _) => a
  }

  /** Get that, if present. */
  def thatOption: Option[B] = PartialFunction.condOpt(this) {
    case That(b)    => b
    case Both(_, b) => b
  }

  /** Get either, if present, but not both. */
  def eitherOption: Option[A \/ B] = PartialFunction.condOpt(this) {
    case This(a) => a.left
    case That(b) => b.right
  }

  /** Map this and that. */
  def bimap[C, D](f: A => C, g: B => D): C \|/ D =
    \|/(thisOption map f, thatOption map g)

  /** Traverse this and that. */
  def bitraverse[F[_]: Applicative, C, D](f: A => F[C], g: B => F[D]): F[C \|/ D] = this match {
    case Neither()  => Applicative[F].point(Neither())
    case This(a)    => Applicative[F].apply(f(a))(This.apply)
    case That(b)    => Applicative[F].apply(g(b))(That.apply)
    case Both(a, b) => Applicative[F].apply2(f(a), g(b))(Both.apply)
  }

  /** Is this only one of the two values? */
  def isOnlyOne: Boolean = this match {
    case This(_) | That(_) => true
    case _                 => false
  }
}

/** Either, neither or both companion. */
object \|/ {

  /**
    * Construct an either, neither or both from a pair of options.
    * @param ao this value, if present
    * @param bo that value, if present
    * @tparam A this type
    * @tparam B that type
    * @return the either, neither or both
    */
  def apply[A, B](ao: Option[A], bo: Option[B]): A \|/ B = (ao, bo) match {
    case (None, None)       => Neither()
    case (Some(a), None)    => This(a)
    case (None, Some(b))    => That(b)
    case (Some(a), Some(b)) => Both(a, b)
  }

  /** When neither option is present. */
  sealed abstract case class Neither[A, B] private() extends (A \|/ B) {
    def coerce[C, D]: C \|/ D  = this.asInstanceOf[C \|/ D]
  }

  object Neither {
    private[this] val value = new Neither[Nothing, Nothing]{}
    def apply[A, B](): A \|/ B = value.coerce[A, B]
  }

  /** When only this value is present. */
  final case class This[A, B](a: A) extends (A \|/ B) {
    def coerceThat[C]: A \|/ C = this.asInstanceOf[A \|/ C]
  }

  /** When only that value is present. */
  final case class That[A, B](b: B) extends (A \|/ B) {
    def coerceThis[C]: C \|/ B = this.asInstanceOf[C \|/ B]
  }

  /** When both values are present. */
  final case class Both[A, B](a: A, b: B) extends (A \|/ B)

  /**
    * Bitraverse evidence for ENBs.
    */
  implicit val BitraverseENB: Bitraverse[\|/] = new Bitraverse[\|/] {
    override def bimap[A, B, C, D](ab: A \|/ B)(f: A => C, g: B => D): C \|/ D = ab.bimap(f, g)

    override def bitraverseImpl[F[_]: Applicative, A, B, C, D](ab: A \|/ B)(f: A => F[C], g: B => F[D]): F[C \|/ D] =
      ab.bitraverse(f, g)
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy