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

japgolly.microlibs.utils.SafeBool.scala Maven / Gradle / Ivy

The newest version!
package japgolly.microlibs.utils

import japgolly.microlibs.utils.SafeBool._
import japgolly.univeq.UnivEq
import scala.annotation.nowarn
import scala.collection.Factory

/** Boolean isomorphism.
  *
  * Mix into the base type and override [[this.companion]] there.
  *
  * Examples:
  *
  * {{{
  *     sealed trait Enabled extends SafeBool[Enabled] {
  *       override final def companion = Enabled
  *     }
  *
  *     case object Enabled extends Enabled with SafeBool.Object[Enabled] {
  *       override def positive = Enabled
  *       override def negative = Disabled
  *     }
  *
  *     case object Disabled extends Enabled
  * }}}
  *
  * {{{
  *     sealed abstract class Permission extends SafeBool.WithBoolOps[Permission] {
  *       override final def companion = Permission
  *     }
  *
  *     case object Allow extends Permission
  *     case object Deny extends Permission
  *
  *     object Permission extends SafeBool.Object[Permission] {
  *       override def positive = Allow
  *       override def negative = Deny
  *     }
  * }}}
  */
trait SafeBool[B <: SafeBool[B]] extends Product with Serializable {
  this: B =>

  def companion: Object[B]

  final def unary_! : B =
    if (this == companion.positive)
      companion.negative
    else
      companion.positive

  @inline final def is(b: B): Boolean =
    b == this

  @inline final def when(cond: Boolean): B =
    if (cond) this else !this

  final def fnToThisWhen[A](f: A => Boolean): A => B =
    a => when(f(a))

  final def whenAllAre(bs: B*): B =
    this when bs.forall(is)

  final def whenAnyAre(bs: B*): B =
    this when bs.exists(is)
}

object SafeBool {

  /**
    * Mix into the companion object for the type.
    */
  trait Object[B <: SafeBool[B]] {
    implicit final def equality: UnivEq[B] = UnivEq.force

    def positive: B with SafeBool[B]
    def negative: B with SafeBool[B]

    final def memo[A](f: B => A): B => A = {
      val p = f(positive)
      val n = f(negative)
      b => if (b is positive) p else n
    }

    final def memoLazy[A](f: B => A): B => A = {
      lazy val p = f(positive)
      lazy val n = f(negative)
      b => if (b is positive) p else n
    }

    final def fold[A](a: A)(f: (A, B) => A): A =
      f(f(a, positive), negative)

    final def mapReduce[X, Y](m: B => X)(r: (X, X) => Y): Y =
      r(m(positive), m(negative))

    final def forall(f: B => Boolean): Boolean =
      f(positive) && f(negative)

    final def exists(f: B => Boolean): Boolean =
      f(positive) || f(negative)

    final type Values[+A] = SafeBool.Values[B, A]

    object Values {
      def apply[A](f: B => A): Values[A] =
        SafeBool.Values(pos = f(positive), neg = f(negative))

      def both[A](a: A): Values[A] =
        SafeBool.Values(a, a)

      def partition[C[_], A](as: IterableOnce[A])(f: A => B)(implicit factory: Factory[A, C[A]]): Values[C[A]] = {
        type Bldr = scala.collection.mutable.Builder[A, C[A]]
        val b = new SafeBool.Values[B, Bldr](factory.newBuilder, factory.newBuilder)
        for (a <- as.iterator) b(f(a)) += a
        b.map(_.result())
      }
    }
  }

  /**
    * Adds boolean ops with `companion.positive` being the equivalent of `true`.
    */
  trait WithBoolOps[B <: SafeBool[B]] extends SafeBool[B] {
    this: B =>

    final def &(that: => B): B = {
      val pos = companion.positive
      pos when ((this is pos) && (that is pos))
    }

    final def &&(that: => Boolean): B = {
      val pos = companion.positive
      pos when ((this is pos) && that)
    }

    final def |(that: => B): B = {
      val pos = companion.positive
      pos when ((this is pos) || (that is pos))
    }

    final def ||(that: => Boolean): B = {
      val pos = companion.positive
      pos when ((this is pos) || that)
    }
  }

  // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  final case class Values[B <: SafeBool[B], +A](pos: A, neg: A) {
    def apply(b: B): A =
      if (b is b.companion.positive) pos else neg

    def set[AA >: A](b: B, a: AA): Values[B, AA] =
      if (b is b.companion.positive) copy(pos = a) else copy(neg = a)

    def mod[AA >: A](b: B, f: A => AA): Values[B, AA] =
      if (b is b.companion.positive) copy(pos = f(pos)) else copy(neg = f(neg))

    def map[C](f: A => C): Values[B, C] =
      Values(pos = f(pos), neg = f(neg))

    def ap[C, D](other: Values[B, C])(f: (A, C) => D): Values[B, D] =
      Values(pos = f(pos, other.pos), neg = f(neg, other.neg))

    def exists(f: A => Boolean): Boolean =
      f(pos) || f(neg)

    def forall(f: A => Boolean): Boolean =
      f(pos) && f(neg)
  }

  object Values {
    @nowarn("cat=unused")
    implicit def univEq[B <: SafeBool[B], A: UnivEq]: UnivEq[Values[B, A]] =
      UnivEq.derive
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy