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

monocle.std.Cofree.scala Maven / Gradle / Ivy

package monocle.std

import monocle.function._
import monocle.{Lens, PIso, Iso, Optional, PTraversal, Traversal}

import scalaz.Cofree._
import scalaz.Tree.Node
import scalaz.{Cofree, Free, Applicative, Traverse, OneAnd, Tree}

object cofree extends CofreeOptics

trait CofreeOptics {

  private def toStream[A](optC: Option[Cofree[Option, A]]): Stream[A] =
    optC.fold(Stream.empty[A])(c => c.head #:: toStream(c.tail))

  private def fromStream[A, B](z: A, c: Stream[A]): Cofree[Option, A] = c match {
    case head #:: tail => Cofree.delay(z, Some(fromStream(head, tail)))
    case _ => Cofree(z, None: Option[Cofree[Option, A]])
  }

  /** Polymorphic isomorphism between `Cofree[Option, _]` and `OneAnd[Stream, _]` */
  def pCofreeToStream[A, B]: PIso[Cofree[Option, A], Cofree[Option, B],
                                  OneAnd[Stream, A], OneAnd[Stream, B]] =
    PIso[Cofree[Option, A], Cofree[Option, B], OneAnd[Stream, A], OneAnd[Stream, B]](
      (c: Cofree[Option, A]) => OneAnd[Stream, A](c.head, toStream(c.tail))
    ){ case OneAnd(head, tail) => fromStream(head, tail) }

  /** [[Iso]] variant of [[pCofreeToStream]]  */
  def cofreeToStream[A]: Iso[Cofree[Option, A], OneAnd[Stream, A]] =
    pCofreeToStream[A, A]


  private def toTree[A](c: Cofree[Stream, A]): Tree[A] =
    Node(c.head, c.tail.map(toTree[A]))

  private def fromTree[A](c: Tree[A]): Cofree[Stream, A] =
    Cofree.delay(c.rootLabel, c.subForest.map(fromTree[A]))

  /** Polymorphic isomorphism between `Cofree[Stream, _]` and `Tree` */
  def pCofreeToTree[A, B]: PIso[Cofree[Stream, A], Cofree[Stream, B],
                                Tree[A], Tree[B]] =
    PIso[Cofree[Stream, A], Cofree[Stream, B], Tree[A], Tree[B]](toTree[A])(fromTree[B])

  /** [[Iso]] variant of [[pCofreeToTree]] */
  def cofreeToTree[A]: Iso[Cofree[Stream, A], Tree[A]] =
    pCofreeToTree[A, A]


  /** Evidence that cofree structures can be split up into a `head` and a `tail` */
  implicit def cofreeCons1[S[_], A]: Cons1[Cofree[S, A], A, S[Cofree[S, A]]] =
    new Cons1[Cofree[S, A], A, S[Cofree[S, A]]] {

      def cons1: Iso[Cofree[S, A], (A, S[Cofree[S, A]])]  =
        Iso((c: Cofree[S, A]) => (c.head, c.tail)){ case (h, t) => Cofree(h, t) }

      /** Overridden to prevent forcing evaluation of the `tail` when we're only
        * interested in using the `head` */
      override def head: Lens[Cofree[S, A], A] =
        Lens((c: Cofree[S, A]) => c.head)(h => c => Cofree.delay(h, c.tail))
    }


  /** Trivial `Each` instance due to `Cofree S` being traversable when `S` is */
  implicit def cofreeEach[S[_]: Traverse, A]: Each[Cofree[S, A], A] =
    Each.traverseEach[({type L[X] = Cofree[S, X]})#L, A]

  implicit def cofreePlated[S[_]: Traverse, A]: Plated[Cofree[S, A]] = new Plated[Cofree[S, A]] {
    val plate: Traversal[Cofree[S, A], Cofree[S, A]] = new Traversal[Cofree[S, A], Cofree[S, A]] {
      def modifyF[F[_]: Applicative](f: Cofree[S, A] => F[Cofree[S, A]])(s: Cofree[S, A]): F[Cofree[S, A]] =
        Applicative[F].map(Traverse[S].traverse(s.t.run)(f))(Cofree(s.head, _))
    }
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy