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

com.mchange.leftright.BiasedEither.scala Maven / Gradle / Ivy

The newest version!
package com.mchange.leftright;

import scala.language.implicitConversions;

import scala.util.{Either,Left,Right}

/**
  * For now, this is maintained in a scala language fork, proposed for
  * inclusion within Either. Modifications/improvements should be made there, 
  * and then pulled back here.
  * 
  * This library lacks detailed documentation, to keep the code concise and 
  * easy to review. For full documentation, please see the 
  * [[http://www.mchange.com/work/enrich-bias-either/enrich-bias-either-2015-09-19/index.html#scala.util.Either forked version of Either]].
  * The biasing in this library is identical to that documented for the scala 
  * fork, except that `Either.LeftBias` and Either.RightBias` map to 
  * `BiasedEither.LeftBias` and `BiasedEither.RightBias`.
  */ 
object BiasedEither {
  trait Bias[+E] {
    def empty : E;

    def isLeftBias  : Boolean;
    def isRightBias : Boolean = !isLeftBias;
    def conformsToBias[A,B]( target : Either[A,B] ) : Boolean = {
      target match {
        case Left( _ )  => isLeftBias;
        case Right( _ ) => isRightBias;
      }
    }
  }
  final object RightBias {

    private[BiasedEither] final val DefaultThrowingOps = withEmptyToken.Throwing( throw new NoSuchElementException( noSuchElementMessage( true ) ) );

    implicit class Ops[A,B]( val target : Either[A,B] ) extends AnyVal { // alas, we don't define a trait, but write all these ops twice, so we can avoid boxing here

      // monad ops
      def flatMap[AA >: A, Z]( f : B => Either[AA,Z] ) : Either[AA,Z] = DefaultThrowingOps.flatMap[A,AA,B,Z]( target )( f );
      def map[Z]( f : B => Z )                         : Either[A,Z]  = DefaultThrowingOps.map( target )( f );
      def withFilter( p : B => Boolean )               : Either[A,B]  = DefaultThrowingOps.withFilter( target )( p );

      // extra ops
      def exists( f : B => Boolean )                  : Boolean           = DefaultThrowingOps.exists( target )( f );
      def forall( f : B => Boolean )                  : Boolean           = DefaultThrowingOps.forall( target )( f );
      def foreach[U]( f : B => U )                    : Any               = DefaultThrowingOps.foreach( target )( f );
      def get                                         : B                 = DefaultThrowingOps.get( target );
      def getOrElse[ BB >: B ]( or : =>BB )           : BB                = DefaultThrowingOps.getOrElse[A,B,BB]( target )( or );
      def toOption                                    : Option[B]         = DefaultThrowingOps.toOption( target );
      def toSeq                                       : collection.Seq[B] = DefaultThrowingOps.toSeq( target );
      def xget                                        : A                 = DefaultThrowingOps.xget( target );
      def xgetOrElse[AA>:A]( or : =>AA )              : AA                = DefaultThrowingOps.xgetOrElse[A,AA,B]( target )( or );
      def xmap[Z]( f : A => Z )                       : Either[Z,B]       = DefaultThrowingOps.xmap( target )( f );
      def replaceIfEmpty[AA>:A]( replacement : =>AA ) : Either[AA,B]      = DefaultThrowingOps.replaceIfEmpty[A,AA,B]( target )( replacement );
      def isEmpty                                     : Boolean           = DefaultThrowingOps.isEmpty( target );
      def isLeftBiased                                : Boolean           = DefaultThrowingOps.isLeftBias;
      def isRightBiased                               : Boolean           = DefaultThrowingOps.isRightBias;
      def conformsToBias                              : Boolean           = DefaultThrowingOps.conformsToBias( target );
    }

    object withEmptyToken {
      abstract class AbstractOps[A,B]( target : Either[A,B] )( opsTypeClass : BiasedEither.RightBias.withEmptyToken.Generic[A] ) {

        // monad ops
        def flatMap[AA >: A, Z]( f : B => Either[AA,Z] ) : Either[AA,Z] = opsTypeClass.flatMap[A,AA,B,Z]( target )( f );
        def map[Z]( f : B => Z )                         : Either[A,Z]  = opsTypeClass.map( target )( f );
        def withFilter( p : B => Boolean )               : Either[A,B]  = opsTypeClass.withFilter( target )( p );

        // extra ops
        def exists( f : B => Boolean )                  : Boolean           = opsTypeClass.exists( target )( f );
        def forall( f : B => Boolean )                  : Boolean           = opsTypeClass.forall( target )( f );
        def foreach[U]( f : B => U )                    : Any               = opsTypeClass.foreach( target )( f );
        def get                                         : B                 = opsTypeClass.get( target );
        def getOrElse[BB>:B]( or : =>BB )               : BB                = opsTypeClass.getOrElse[A,B,BB]( target )( or );
        def toOption                                    : Option[B]         = opsTypeClass.toOption( target );
        def toSeq                                       : collection.Seq[B] = opsTypeClass.toSeq( target );
        def isEmpty                                     : Boolean           = opsTypeClass.isEmpty( target );
        def xget                                        : A                 = opsTypeClass.xget( target );
        def xgetOrElse[AA>:A]( or : =>AA )              : AA                = opsTypeClass.xgetOrElse[A,AA,B]( target )( or );
        def xmap[Z]( f : A => Z )                       : Either[Z,B]       = opsTypeClass.xmap( target )( f );
        def replaceIfEmpty[AA>:A]( replacement : =>AA ) : Either[AA,B]      = opsTypeClass.replaceIfEmpty[A,AA,B]( target )( replacement );
        def isLeftBiased                                : Boolean           = opsTypeClass.isLeftBias;
        def isRightBiased                               : Boolean           = opsTypeClass.isRightBias;
        def conformsToBias                              : Boolean           = opsTypeClass.conformsToBias( target );
      }

      implicit final class Ops[A,B]( target : Either[A,B] )( implicit opsTypeClass : BiasedEither.RightBias.withEmptyToken.Generic[A] ) extends AbstractOps( target )( opsTypeClass );

      trait Generic[+E] extends BiasedEither.Bias[E] {
        /*
         * In order to meet the contract of withFilter(...) [from which this method is called],
         * no object allocation should occur on each non-Exception-raising call of this method.
         * Raising an exception is "fine" (in that it represents a hackish violation of the contract
         * anyway), as is overriding this with a val. But no new Left should be created on each
         * invocation.
         */ 
        protected def leftEmpty : Left[E,Nothing];

        def isEmpty[A>:E,B]( target : Either[A,B] ) : Boolean;

        // monad ops
        def flatMap[A>:E,AA>:A,B,Z]( target : Either[A,B] )( f : B => Either[AA,Z] ) : Either[AA,Z] = {
          target match {
            case Left( _ )  => target.asInstanceOf[Left[A,Z]]
            case Right( b ) => f( b )
          }
        }
        def map[A>:E,B,Z]( target : Either[A,B] )( f : B => Z ) : Either[A,Z] = {
          target match {
            case Left( _ )  => target.asInstanceOf[Left[A,Z]]
            case Right( b ) => Right( f( b ) )
          }
        }
        def withFilter[A>:E,B]( target : Either[A,B] )( p : B => Boolean ) : Either[A,B] = {
          target match {
            case      Left( _ ) => target;
            case r @ Right( b ) => if ( p(b) ) r else leftEmpty;
          }
        }

        // extra ops
        def exists[A>:E,B]( target : Either[A,B] )( f : B => Boolean ) : Boolean = {
          target match {
            case Left( _ )  => false;
            case Right( b ) => f( b );
          }
        }
        def forall[A>:E,B]( target : Either[A,B] )( f : B => Boolean ) : Boolean = {
          target match {
            case Left( _ )  => true;
            case Right( b ) => f( b );
          }
        }
        def foreach[A>:E,B,U]( target : Either[A,B] )( f : B => U ) : Any = {
          target match {
            case Left( _ )  => ();
            case Right( b ) => f( b );
          }
        }
        def get[A>:E,B]( target : Either[A,B] ) : B = {
          target match {
            case Left( _ )  => throw new NoSuchElementException( NoSuchRightMessage );
            case Right( b ) => b;
          }
        }
        def getOrElse[A>:E,B,BB>:B]( target : Either[A,B] )( or : =>BB ) : BB = {
          target match {
            case Left( _ )  => or;
            case Right( b ) => b;
          }
        }
        def toOption[A>:E,B]( target : Either[A,B] ) : Option[B] = {
          target match {
            case Left( _ )  => None;
            case Right( b ) => Some( b );
          }
        }
        def toSeq[A>:E,B]( target : Either[A,B] ) : collection.Seq[B] = {
          target match {
            case Left( _ )  => collection.Seq.empty[B];
            case Right( b ) => collection.Seq( b );
          }
        }
        def xget[A>:E,B]( target : Either[A,B] ) : A = {
          target match {
            case Left( a )  => a;
            case Right( _ ) => throw new NoSuchElementException( NoSuchXLeftMessage );
          }
        }
        def xgetOrElse[A>:E,AA>:A,B]( target : Either[A,B] )( or : =>AA ) : AA = {
          target match {
            case Left( a )  => a;
            case Right( _ ) => or;
          }
        }
        def xmap[A>:E,B,Z]( target : Either[A,B] )( f : A => Z ) : Either[Z,B] = {
          target match {
            case Left( a )  => Left( f( a ) )
            case Right( _ ) => target.asInstanceOf[Right[Z,B]]
          }
        }
        def replaceIfEmpty[A>:E,AA>:A,B]( target : Either[A,B] )( replacement : =>AA ) : Either[AA,B] = {
          if (isEmpty( target )) Left( replacement ) else target;
        }
        def isLeftBias  : Boolean = false;

        implicit def toOps[A>:E,B]( target : Either[A,B] ) : RightBias.withEmptyToken.Ops[A,B] = new RightBias.withEmptyToken.Ops[A,B]( target )( this )
      }
      def apply[E]( token : E ) : withEmptyToken[E] = new withEmptyToken( token );

      object Throwing {
        def apply( throwableBuilder : =>java.lang.Throwable ) : Throwing = new Throwing( throwableBuilder );
      }
      final class Throwing private( throwableBuilder : =>java.lang.Throwable ) extends withEmptyToken.Generic[Nothing] {
        override protected def leftEmpty : Nothing = empty;

        override def empty : Nothing = throw throwableBuilder;

        override def isEmpty[A,B]( target : Either[A,B] ) : Boolean = false; // no state represents empty, empties cannot be formed as an Exception is thrown when it is tried
      }
    }

    final class withEmptyToken[+E] private( override val empty : E ) extends withEmptyToken.Generic[E] {
      override protected val leftEmpty : Left[E,Nothing] = Left(empty);

      override def isEmpty[A>:E,B]( target : Either[A,B] ) : Boolean = (target == leftEmpty);
    }
    abstract class Base[L]( emptyToken : L ) extends RightBias[L] {
      override val EmptyTokenDefinition = RightBias.withEmptyToken[L]( emptyToken )
    }
  }
  trait RightBias[L] {
    val EmptyTokenDefinition : BiasedEither.RightBias.withEmptyToken.Generic[L] = RightBias.DefaultThrowingOps;

    implicit def toRightBiasEtherOps[R]( target : Either[L,R] ) : RightBias.withEmptyToken.AbstractOps[L,R] = new RightBias.withEmptyToken.Ops[L,R]( target )( EmptyTokenDefinition );
  }

  final object LeftBias {

    private[BiasedEither] final val DefaultThrowingOps = withEmptyToken.Throwing( throw new NoSuchElementException( noSuchElementMessage( false ) ) );

    implicit class Ops[A,B]( val target : Either[A,B] ) extends AnyVal { // alas, we don't define a trait, but write all these ops twice, so we can avoid boxing here

      // monad ops
      def flatMap[BB >: B, Z]( f : A => Either[Z,BB] ) : Either[Z,BB] = DefaultThrowingOps.flatMap[A,B,BB,Z]( target )( f );
      def map[Z]( f : A => Z )                         : Either[Z,B]  = DefaultThrowingOps.map( target )( f );
      def withFilter( p : A => Boolean )               : Either[A,B]  = DefaultThrowingOps.withFilter( target )( p );

      // extra ops
      def exists( f : A => Boolean )                  : Boolean           = DefaultThrowingOps.exists( target )( f );
      def forall( f : A => Boolean )                  : Boolean           = DefaultThrowingOps.forall( target )( f );
      def foreach[U]( f : A => U )                    : Any               = DefaultThrowingOps.foreach( target )( f );
      def get                                         : A                 = DefaultThrowingOps.get( target );
      def getOrElse[AA >: A ]( or : =>AA )            : AA                = DefaultThrowingOps.getOrElse[A,AA,B]( target )( or );
      def toOption                                    : Option[A]         = DefaultThrowingOps.toOption( target );
      def toSeq                                       : collection.Seq[A] = DefaultThrowingOps.toSeq( target );
      def isEmpty                                     : Boolean           = DefaultThrowingOps.isEmpty( target );
      def xget                                        : B                 = DefaultThrowingOps.xget( target );
      def xgetOrElse[BB>:B]( or : =>BB )              : BB                = DefaultThrowingOps.xgetOrElse[A,B,BB]( target )( or );
      def xmap[Z]( f : B => Z )                       : Either[A,Z]       = DefaultThrowingOps.xmap( target )( f );
      def replaceIfEmpty[BB>:B]( replacement : =>BB ) : Either[A,BB]      = DefaultThrowingOps.replaceIfEmpty[A,B,BB]( target )( replacement )
      def isLeftBiased                                : Boolean           = DefaultThrowingOps.isLeftBias;
      def isRightBiased                               : Boolean           = DefaultThrowingOps.isRightBias;
      def conformsToBias                              : Boolean           = DefaultThrowingOps.conformsToBias( target );
    }

    object withEmptyToken {
      abstract class AbstractOps[A,B]( target : Either[A,B] )( opsTypeClass : BiasedEither.LeftBias.withEmptyToken.Generic[B] ) {

        // monad ops
        def flatMap[BB >: B, Z]( f : A => Either[Z,BB] ) : Either[Z,BB] = opsTypeClass.flatMap[A,B,BB,Z]( target )( f );
        def map[Z]( f : A => Z ) : Either[Z,B] = opsTypeClass.map( target )( f );
        def withFilter( p : A => Boolean ) : Either[A,B] = opsTypeClass.withFilter( target )( p );

        // extra ops
        def exists( f : A => Boolean ) : Boolean = opsTypeClass.exists( target )( f );
        def forall( f : A => Boolean ) : Boolean = opsTypeClass.forall( target )( f );
        def foreach[U]( f : A => U ) : Any = opsTypeClass.foreach( target )( f );
        def get : A = opsTypeClass.get( target );
        def getOrElse[AA >: A ]( or : =>AA ) : AA = opsTypeClass.getOrElse[A,AA,B]( target )( or );
        def toOption : Option[A] = opsTypeClass.toOption( target );
        def toSeq : collection.Seq[A] = opsTypeClass.toSeq( target );
        def isEmpty : Boolean = opsTypeClass.isEmpty( target );
        def xget : B = opsTypeClass.xget( target );
        def xgetOrElse[BB>:B]( or : =>BB ) : BB = opsTypeClass.xgetOrElse[A,B,BB]( target )( or );
        def xmap[Z]( f : B => Z ) : Either[A,Z] = opsTypeClass.xmap( target )( f );
        def replaceIfEmpty[BB>:B]( replacement : =>BB ) : Either[A,BB] = opsTypeClass.replaceIfEmpty[A,B,BB]( target )( replacement )
        def isLeftBiased : Boolean = opsTypeClass.isLeftBias;
        def isRightBiased : Boolean = opsTypeClass.isRightBias;
        def conformsToBias : Boolean = opsTypeClass.conformsToBias( target );
      }

      implicit final class Ops[A,B]( target : Either[A,B] )( implicit opsTypeClass : BiasedEither.LeftBias.withEmptyToken.Generic[B] ) extends AbstractOps( target )( opsTypeClass );

      trait Generic[+E] extends BiasedEither.Bias[E] {
        /*
         * In order to meet the contract of withFilter(...) [from which this method is called],
         * no object allocation should occur on each non-Exception-raising call of this method.
         * Raising an exception is "fine" (in that it represents a hackish violation of the contract
         * anyway), as is overriding this with a val. But no new Right should be created on each
         * invocation.
         */ 
        protected def rightEmpty : Right[Nothing,E]; 

        def isEmpty[A>:E,B]( target : Either[A,B] ) : Boolean;

        // monad ops
        def flatMap[A, B>:E, BB>:B ,Z]( target : Either[A,B] )( f : A => Either[Z,BB] ) : Either[Z,BB] = {
          target match {
            case Left( a )  => f( a )
            case Right( _ ) => target.asInstanceOf[Right[Z,B]]
          }
        }
        def map[A, B>:E, Z]( target : Either[A,B] )( f : A => Z ) : Either[Z,B] = {
          target match {
            case Left( a )  => Left( f( a ) )
            case Right( _ ) => target.asInstanceOf[Right[Z,B]]
          }
        }
        def withFilter[A,B>:E]( target : Either[A,B] )( p : A => Boolean ) : Either[A,B] = {
          target match {
            case l @  Left( a ) => if ( p(a) ) l else rightEmpty;
            case     Right( _ ) => target;
          }
        }

        // extra ops
        def exists[A,B>:E]( target : Either[A,B] )( f : A => Boolean ) : Boolean = {
          target match {
            case Left( a )  => f(a);
            case Right( _ ) => false;
          }
        }
        def forall[A,B>:E]( target : Either[A,B] )( f : A => Boolean ) : Boolean = {
          target match {
            case Left( a )  => f(a)
            case Right( _ ) => true;
          }
        }
        def foreach[A,B>:E,U]( target : Either[A,B] )( f : A => U ) : Any = {
          target match {
            case Left( a )  => f(a);
            case Right( _ ) => ();
          }
        }
        def get[A,B>:E]( target : Either[A,B] ) : A = {
          target match {
            case Left( a )  => a;
            case Right( _ ) => throw new NoSuchElementException( NoSuchLeftMessage );
          }
        }
        def getOrElse[A, AA>:A, B>:E]( target : Either[A,B] )( or : =>AA ) : AA = {
          target match {
            case Left( a )  => a;
            case Right( _ ) => or;
          }
        }
        def toOption[A,B>:E]( target : Either[A,B] ) : Option[A] = {
          target match {
            case Left( a )  => Some( a );
            case Right( _ ) => None; 
          }
        }
        def toSeq[A,B>:E]( target : Either[A,B] ) : collection.Seq[A] = {
          target match {
            case Left( a )  => collection.Seq( a );
            case Right( _ ) => collection.Seq.empty[A];
          }
        }
        def xget[A,B>:E]( target : Either[A,B] ) : B = {
          target match {
            case Left( _ )  => throw new NoSuchElementException( NoSuchXRightMessage );
            case Right( b ) => b;
          }
        }
        def xgetOrElse[A,B>:E,BB>:B]( target : Either[A,B] )( or : =>BB ) : BB = {
          target match {
            case Left( _ )  => or;
            case Right( b ) => b;
          }
        }
        def xmap[A,B>:E,Z]( target : Either[A,B] )( f : B => Z ) : Either[A,Z] = {
          target match {
            case Left( _ )  => target.asInstanceOf[Left[A,Z]]
            case Right( b ) => Right( f(b) )
          }
        }
        def replaceIfEmpty[A,B>:E,BB>:B]( target : Either[A,B] )( replacement : =>BB ) : Either[A,BB] = {
          if (isEmpty( target )) Right( replacement ) else target;
        }
        def isLeftBias  : Boolean = true;

        implicit def toOps[A,B>:E]( target : Either[A,B] ) : LeftBias.withEmptyToken.Ops[A,B] = new LeftBias.withEmptyToken.Ops[A,B]( target )( this )
      }
      def apply[E]( token : E ) : withEmptyToken[E] = new withEmptyToken( token );

      object Throwing {
        def apply( throwableBuilder : =>java.lang.Throwable ) : Throwing = new Throwing( throwableBuilder );
      }
      final class Throwing private( throwableBuilder : =>java.lang.Throwable ) extends withEmptyToken.Generic[Nothing] {
        override protected def rightEmpty : Nothing = empty;

        override def empty : Nothing = throw throwableBuilder;

        override def isEmpty[A,B]( target : Either[A,B] ) : Boolean = false; // no state represents empty, empties cannot be formed as an Exception is thrown when it is tried
      }
    }
    final class withEmptyToken[+E] private( override val empty : E ) extends withEmptyToken.Generic[E] {
      override protected val rightEmpty : Right[Nothing,E] = Right(empty);

      override def isEmpty[A>:E,B]( target : Either[A,B] ) : Boolean = (target == rightEmpty);
    }
    abstract class Base[R]( emptyToken : R ) extends LeftBias[R] {
      override val EmptyTokenDefinition = LeftBias.withEmptyToken[R]( emptyToken )
    }
  }
  trait LeftBias[R] { // we use R rather than B here to emphasize to users that it is the right-side type that must be specified here
    val EmptyTokenDefinition : BiasedEither.LeftBias.withEmptyToken.Generic[R] = LeftBias.DefaultThrowingOps;

    implicit def toLeftBiasEtherOps[L]( target : Either[L,R] ) : LeftBias.withEmptyToken.AbstractOps[L,R] = new LeftBias.withEmptyToken.Ops[L,R]( target )( EmptyTokenDefinition );
  }

  private def noSuchElementMessage[A,B]( rightBias : Boolean, mbEither : Option[Either[A,B]] = None ) = {
    val bias = if ( rightBias ) "Right-biased" else "Left-biased";
    val withToken = if ( rightBias ) "RightBias.withEmptyToken" else "LeftBias.withEmptyToken";
    val eitherRep = mbEither.fold(" ")( either => s" '${either}' " );
    s"${bias} Either${eitherRep}filtered to empty or failed to match a pattern. Consider using ${withToken}"
  }

  private val NoSuchLeftMessage = "Can't get a value from a left-biased Either which is in fact a Right.";
  private val NoSuchRightMessage = "Can't get a value from a right-biased Either which is in fact a Left.";
  private val NoSuchXLeftMessage = "This right-biased either is in fact a Right. xget requires a value against its bias."
  private val NoSuchXRightMessage = "This left-biased either is in fact a Left. xget requires a value against its bias."
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy