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

zio.prelude.experimental.BothCompose.scala Maven / Gradle / Ivy

There is a newer version: 1.0.0-RC34
Show newest version
/*
 * Copyright 2020-2023 John A. De Goes and the ZIO Contributors
 *
 * 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 zio.prelude
package experimental

trait BothCompose[=>:[-_, +_]] extends AssociativeCompose[=>:] {

  type :*:[+_, +_]

  def fromFirst[A]: (A :*: Any) =>: A
  def fromSecond[B]: (Any :*: B) =>: B
  def toBoth[A, B, C](a2b: A =>: B)(a2c: A =>: C): A =>: (B :*: C)

  def bothCompose[A, B, C](
    a2b: A =>: B,
    a2c: A =>: C,
    a2bc: A =>: (B :*: C)
  )(implicit eqA2B: Equal[A =>: B], eqA2C: Equal[A =>: C], eqA2BC: Equal[A =>: (B :*: C)]): Boolean = {
    val law1 = compose[A, B :*: C, B](fromFirst, toBoth(a2b)(a2c)) === a2b
    val law2 = compose[A, B :*: C, C](fromSecond, toBoth(a2b)(a2c)) === a2c
    val law3 = toBoth(compose[A, B :*: C, B](fromFirst, a2bc))(compose[A, B :*: C, C](fromSecond, a2bc)) === a2bc

    law1 && law2 && law3
  }
}

object BothCompose {

  type Aux[=>:[-_, +_], Product[+_, +_]] = BothCompose[=>:] {
    type :*:[+f, +s] = Product[f, s]
  }

  implicit val FunctionApplicationCompose: ApplicationCompose[Function] = new ApplicationCompose[Function] {

    type :*:[+f, +s]  = Tuple2[f, s]
    type -->:[-t, +r] = Function[t, r]

    override def compose[A, B, C](bc: B => C, ab: A => B): A => C =
      AssociativeCompose.FunctionIdentityCompose.compose(bc, ab)

    override def fromFirst[A]: Function[(A, Any), A] = _._1

    override def fromSecond[B]: Function[(Any, B), B] = _._2

    override def toBoth[A, B, C](a2b: Function[A, B])(a2c: Function[A, C]): Function[A, (B, C)] = { a =>
      (a2b(a), a2c(a))
    }

    override def application[A, B]: Function[(Function[A, B], A), B] = { case (a2b, a) =>
      a2b(a)
    }

    override def curry[A, B, C](f: Function[(A, B), C]): Function[A, Function[B, C]] = { a => b =>
      f((a, b))
    }

    override def uncurry[A, B, C](g: Function[A, Function[B, C]]): Function[(A, B), C] = { case (a, b) =>
      g(a)(b)
    }

  }

}

trait BothComposeSyntax {
  implicit class BothComposeOps[A, B, =>:[-_, +_]](private val a2b: A =>: B) {

    /** A symbolic alias for `toBoth`. Composes `A -> B` with `A -> C` to form `A -> (B, C)`. */
    def &&&[C, :*:[+_, +_]](implicit both: BothCompose.Aux[=>:, :*:]): (A =>: C) => (A =>: (B :*: C)) =
      both.toBoth(a2b)

    /** Composes `A -> B` with `A -> C` to form `A -> (B, C)`. */
    def toBoth[C, :*:[+_, +_]](implicit both: BothCompose.Aux[=>:, :*:]): (A =>: C) => (A =>: (B :*: C)) =
      both.toBoth(a2b)
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy