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

teststate.core.CheckOps.scala Maven / Gradle / Ivy

The newest version!
package teststate.core

import japgolly.microlibs.name_fn._
import teststate.core.Types._
import teststate.data._
import teststate.typeclass._

trait CheckOps[C[_, _, _]] {

  def mapE[O, S, E, F](c: C[O, S, E])(f: E => F): C[O, S, F]

  def map_OS[O, S, E, X, Y](c: C[O, S, E])(f: OS[X, Y] => OS[O, S]): C[X, Y, E]

  def pmapO[O, S, E, X](c: C[O, S, E])(f: X => E Or O): C[X, S, E]
}

object CheckOps {

  final class Ops[C[_, _, _], O, S, E](c: C[O, S, E])(implicit tc: CheckOps[C]) {

    def mapE[F](f: E => F): C[O, S, F] =
      tc.mapE(c)(f)

    @inline def map_OS[X, Y](f: OS[X, Y] => OS[O, S]): C[X, Y, E] =
      tc.map_OS(c)(f)

    def mapOS[X, Y](o: X => O, s: Y => S): C[X, Y, E] =
      map_OS(_.mapOS(o, s))

    def mapO[X](f: X => O): C[X, S, E] =
      map_OS(_ mapO f)

    def mapS[X](f: X => S): C[O, X, E] =
      map_OS(_ mapS f)

    def pmapO[X](f: X => E Or O): C[X, S, E] =
      tc.pmapO(c)(f)
  }

  @inline implicit def NameFnOsExt[O, S](n: NameFn[OS[O, S]]): NameFnOsExt[O, S] = new NameFnOsExt(n.fn)
  class NameFnOsExt[O, S](private val _n: Option[OS[O, S]] => Name) extends AnyVal {
    def pmapO[X](f: X => Any Or O): NameFn[OS[X, S]] =
      NameFn(_n).comap(_.emapO(f).toOption)
  }

  @inline implicit class EitherOsExt[E, O, S](private val e: E Or OS[O, S]) extends AnyVal {
    def pmapO[X](f: O => E Or X): E Or OS[X, S] =
      e flatMap(_ emapO f)
  }

  @inline implicit class OsToTriFnExt[O, S, E, A](private val fn: OS[O, S] => Tri[E, A]) extends AnyVal {
    def pmapO[X](f: X => E Or O): OS[X, S] => Tri[E, A] =
      _.emapO(f).toTriFlatMap(fn)
  }

  abstract class SingleCheck[D[-_, _]](implicit D: Profunctor[D]) extends CheckOps[CheckShape1[D]#T] {
    final type C[O, S, E] = CheckShape1[D]#T[O, S, E]

    override def mapE[O, S, E, F](c: C[O, S, E])(f: E => F): C[O, S, F] =
      D.rmap(c)(f)

    override def map_OS[O, S, E, X, Y](c: C[O, S, E])(f: OS[X, Y] => OS[O, S]): C[X, Y, E] =
      D.lmap(c)(f)
  }

  trait Implicits {

    implicit val checkOpsInstanceForPoint: CheckOps[CheckShape1[Point]#T] =
      new SingleCheck[Point] {
        override def pmapO[O, S, E, X](c: C[O, S, E])(f: X => E Or O): C[X, S, E] =
          Point(c.name pmapO f, c.test pmapO f)
      }

    implicit val checkOpsInstanceForDeltaA: CheckOps[CheckShape1[Around.DeltaA]#T] =
      new SingleCheck[Around.DeltaA] {
        override def pmapO[O, S, E, X](d: C[O, S, E])(f: X => E Or O): C[X, S, E] = {
          val t = d.test
          Around.DeltaA(
            d.name.comap(_.emap(_ emapO f).toOption),
            d.before pmapO f,
            (xs, a: d.A) => xs.emapO(f).test(t(_, a)).leftOption)
        }
      }


    implicit val checkOpsInstanceForAround: CheckOps[CheckShape1[Around]#T] =
      new SingleCheck[Around] {
        override def pmapO[O, S, E, X](c: C[O, S, E])(f: X => E Or O): C[X, S, E] =
          c match {
            case Around.Point(p, w) => Around.Point(p pmapO f, w)
            case Around.Delta(d)    => Around.Delta(d pmapO f)
          }
      }

    implicit val checkOpsInstanceForInvariant: CheckOps[CheckShape1[Invariant]#T] =
      new SingleCheck[Invariant] {
        override def pmapO[O, S, E, X](c: C[O, S, E])(f: X => E Or O): C[X, S, E] =
          c match {
            case Invariant.Point(x) => Invariant.Point(x pmapO f)
            case Invariant.Delta(x) => Invariant.Delta(x pmapO f)
          }
      }

    type CheckOpsCheckShape[C[-_, _]] = CheckOps[CheckShape[C, *, *, *]]

    private def checkOpsInstanceForChecks[C[-_, _]](implicit sub: CheckOps[CheckShape1[C]#T]): CheckOpsCheckShape[C] =
      new CheckOps[CheckShape[C, *, *, *]] {
        import Sack._

        override def mapE[O, S, E, F](c: CheckShape[C, O, S, E])(f: E => F): CheckShape[C, O, S, F] =
          c.rmap(_.bimap(_ map (_ map f), _ mapE f))

        override def map_OS[O, S, E, X, Y](c: CheckShape[C, O, S, E])(f: OS[X, Y] => OS[O, S]): CheckShape[C, X, Y, E] =
          c.dimap(f, _ map (_ map_OS f))

        override def pmapO[O, S, E, X](m: CheckShape[C, O, S, E])(f: X => E Or O): CheckShape[C, X, S, E] =
          m match {
            case Value(o)        => Value(o map (_ pmapO f))
            case Product(ss)     => Product(ss map (pmapO(_)(f)))
            case CoProduct(n, p) =>
              CoProduct(
                n pmapO f,
                _.emapO(f) match {
                  case Right(os) => pmapO(p(os))(f)
                  case Left(e)   => Value(Left(NamedError(n(None), Failure NoCause e)))
                }
            )
          }
      }

    implicit val checkOpsInstanceForPoints    : CheckOpsCheckShape[Point    ] = checkOpsInstanceForChecks[Point    ]
    implicit val checkOpsInstanceForArounds   : CheckOpsCheckShape[Around   ] = checkOpsInstanceForChecks[Around   ]
    implicit val checkOpsInstanceForInvariants: CheckOpsCheckShape[Invariant] = checkOpsInstanceForChecks[Invariant]

    implicit def pointsToCheckOps    [O, S, E](c: Points    [O, S, E]): Ops[Points    , O, S, E] = new Ops(c)
    implicit def aroundsToCheckOps   [O, S, E](c: Arounds   [O, S, E]): Ops[Arounds   , O, S, E] = new Ops(c)
    implicit def invariantsToCheckOps[O, S, E](c: Invariants[O, S, E]): Ops[Invariants, O, S, E] = new Ops(c)

    implicit def checkToCheckOps[C[-_, _], O, S, E](c: C[OS[O, S], E])(implicit tc: CheckOps[CheckShape1[C]#T]): Ops[CheckShape1[C]#T, O, S, E] =
      new Ops[CheckShape1[C]#T, O, S, E](c)
  }

// // ↓ works but IntelliJ highlights everything red as usual so ↑ for a nicer experience
//  trait ToOps {
//     implicit def toCheckOps[C[_, _, _], O, S, E](c: C[O, S, E])(implicit tc: CheckOps[C]): Ops[C, O, S, E] =
//       new Ops[C, O, S, E](c)(tc)
//  }
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy