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

hael.jjm-core_2.13.0.2.3.source-code.DependentMap.scala Maven / Gradle / Ivy

The newest version!
package jjm

import cats.~>
import cats.Eq
import cats.Monoid
import cats.MonoidK
import cats.implicits._

import io.circe.Encoder
import io.circe.Decoder
import io.circe.KeyEncoder
import io.circe.KeyDecoder
import io.circe.Json
import io.circe.HCursor

import monocle.function.At
import monocle.function.Index

final class DependentMap[F[_], G[_]] private (private val map: Map[F[_], G[_]]) {

  def get[A](key: F[A]): Option[G[A]] =
    map.get(key).asInstanceOf[Option[G[A]]]

  def put[A](key: F[A], value: G[A]): DependentMap[F, G] =
    new DependentMap[F, G](map + (key -> value).asInstanceOf[(F[_], G[_])])

  def put[A](pair: DependentPair[F, G]): DependentMap[F, G] =
    new DependentMap[F, G](map + (pair.fst -> pair.snd).asInstanceOf[(F[_], G[_])])

  def remove[A](key: F[A]): DependentMap[F, G] =
    new DependentMap[F, G](map - key)

  def keys: Iterable[F[_]] =
    map.keys

  def keySet: Set[F[_]] =
    map.keySet

  private[this] def getPair[A0](key: F[A0]): Option[DependentPair[F, G] { type A = A0 }] =
    get(key).map(DependentPair[F, G, A0](key, _))

  def iterator: Iterator[DependentPair[F, G]] =
    map.keys.iterator.map(key => getPair(key).get)

  def size: Int =
    map.size

  override def toString =
    map.toString

  override def equals(that: Any): Boolean = that match {
    case that: DependentMap[_, _] => this.map == that.map
    case _ => false
  }

  override def hashCode: Int = map.hashCode

  def toFunctionK: (F ~> λ[A => Option[G[A]]]) = new (F ~> λ[A => Option[G[A]]]) {
    override def apply[A](fa: F[A]): Option[G[A]] = get(fa)
  }
}

object DependentMap extends DependentMapInstances {
  def empty[F[_], G[_]] =
    new DependentMap[F, G](Map.empty[F[_], G[_]])
}

abstract private[jjm] class DependentMapInstances {

  // TODO should be based on eqs of arg types etc.?
  // probably would only make sense if underlying Map was based on Hash and Eq
  // that's a bit harder to do in this setting bc we basically need EqK, HashK, etc.
  // implicit def dependentMapEq[F[_], G[_]] = new Eq[DependentMap[F, G]] {
  //   override def eqv(x: DependentMap[F, G], y: DependentMap[F, G]): Boolean =
  //     x.map == y.map
  // }

  implicit def dependentMapMonoid[F[_], G[_]: MonoidK]: Monoid[DependentMap[F, G]] =
    new Monoid[DependentMap[F, G]] {
      override def empty =
        DependentMap.empty[F, G]
      override def combine(x: DependentMap[F, G], y: DependentMap[F, G]) = {
        (x.keys ++ y.keys).toSet.foldLeft(empty) {
          case (dMap, key) => dMap.put(key, (x.get(key).foldK <+> y.get(key).foldK))
        }
      }
    }

  implicit def dependentMapEncoder[F[_], G[_]](
    implicit keyEncoder: KeyEncoder[F[_]],
    dependentEncoder: DependentEncoder[F, G]
  ): Encoder[DependentMap[F, G]] = new Encoder[DependentMap[F, G]] {
    final def apply(m: DependentMap[F, G]) = Json.obj(
      m.iterator.map(pair =>
        keyEncoder(pair.fst) -> dependentEncoder(pair.fst)(pair.snd)
      ).toSeq: _*
    )
  }

  // TODO Really would like to fix up the Decoder.
  // Not sure how to get the types to work out without this auxiliary extra Foo
  // to hold the type skolem

  private case class Foo[F[_], A](fa: F[A]) {
    type Out = A
  }

  implicit def dependentMapDecoder[F[_], G[_]](
    implicit keyDecoder: KeyDecoder[F[_]],
    dependentDecoder: DependentDecoder[F, G]
  ): Decoder[DependentMap[F, G]] = new Decoder[DependentMap[F, G]] {
    final def apply(c: HCursor): Decoder.Result[DependentMap[F, G]] = {
      // TODO aah replace the get
      c.keys.get.toList.foldM[Decoder.Result, DependentMap[F, G]](DependentMap.empty[F, G]) { (m, keyStr) =>
        import scala.language.existentials
        val key = keyDecoder(keyStr).get // TODO aah replace the get
        val value = dependentDecoder(key).tryDecode(c.downField(keyStr))
        val foo = Foo(key)
        type Out = foo.Out
        value.map(v => m.put[Out](key.asInstanceOf[F[Out]], v.asInstanceOf[G[Out]]))
      }
    }
  }

  implicit def dependentMapAt[F[_], G[_], I]: At[DependentMap[F, G], F[I], Option[G[I]]] =
    At[DependentMap[F, G], F[I], Option[G[I]]](
      (i: F[I]) => (map: DependentMap[F, G]) => map.get(i))(
      (i: F[I]) => (optV: Option[G[I]]) => (map: DependentMap[F, G]) =>
      optV.fold(map.remove(i))(v => map.put(i, v))
    )

  implicit def dependentMapIndex[F[_], G[_], I]: Index[DependentMap[F, G], F[I], G[I]] = Index.fromAt
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy