Skip to content

Instantly share code, notes, and snippets.

@kailuowang
Last active January 2, 2022 02:41
Show Gist options
  • Save kailuowang/7377633fd8cf691834eb2130afe8c129 to your computer and use it in GitHub Desktop.
Save kailuowang/7377633fd8cf691834eb2130afe8c129 to your computer and use it in GitHub Desktop.
trait IsoK[F[_], G[_]] { self =>
def from[A](fa: F[A]): G[A]
def to[A](ga: G[A]): F[A]
def flip: IsoK[G, F] = new IsoK[G, F] {
def from[A](ga: G[A]): F[A] = self.to(ga)
def to[A](fa: F[A]): G[A] = self.from(fa)
}
def compose[H[_]](that: IsoK[G, H]): IsoK[F, H] = new IsoK[F, H]{
def from[A](fa: F[A]): H[A] = that.from(self.from(fa))
def to[A](ha: H[A]): F[A] = self.to(that.to(ha))
}
//convenient shortcut method
implicit def monad(implicit F: Monad[F]): Monad[G] = IsoK.monad[F, G](F, this)
implicit def applicative(implicit F: Applicative[F]): Applicative[G] = IsoK.applicative[F, G](F, this)
}
object IsoK extends IsoInstances with IsoProvidedInstances {
def apply[F[_], G[_]](implicit inst: IsoK[F, G]): IsoK[F, G] = inst
}
trait IsoInstances {
implicit def optionT[F[_]]: IsoK[OptionT[F, *], Lambda[A => F[Option[A]]]] = new IsoK[OptionT[F, *], Lambda[A => F[Option[A]]]] {
def from[A](fa: OptionT[F, A]): F[Option[A]] = fa.value
def to[A](ga: F[Option[A]]): OptionT[F, A] = OptionT(ga)
}
}
sealed private[cats] trait IsoProvidedInstances {
def applicative[F[_], G[_]](implicit F: Applicative[F], I: IsoK[F, G]): Applicative[G] =
new Applicative[G] {
import I._
def pure[A](r: A): G[A] = from(F.pure(r))
override def map[A, B](ga: G[A])(f: A => B): G[B] =
from(F.map(to(ga))(f))
override def map2[A, B, C](ga: G[A], gb: G[B])(fn: (A, B) => C): G[C] =
from(F.map2(to(ga), to(gb))(fn))
override def product[A, B](ga: G[A], gb: G[B]): G[(A, B)] =
from(F.product(to(ga), to(gb)))
def ap[A, B](f: G[A => B])(ga: G[A]): G[B] =
from(F.ap(to(f))(to(ga)))
}
def monad[F[_], G[_]](implicit F: Monad[F], I: IsoK[F, G]): Monad[G] =
new Monad[G] {
import I._
def pure[A](r: A): G[A] = from(F.pure(r))
def flatMap[A, B](ga: G[A])(f: A => G[B]): G[B] =
from(F.flatMap(to(ga))(f.map(to)))
override def map[A, B](ga: G[A])(f: A => B): G[B] =
from(F.map(to(ga))(f))
override def map2[A, B, C](ga: G[A], gb: G[B])(fn: (A, B) => C): G[C] =
from(F.map2(to(ga), to(gb))(fn))
override def product[A, B](ga: G[A], gb: G[B]): G[(A, B)] =
from(F.product(to(ga), to(gb)))
override def ap[A, B](f: G[A => B])(ga: G[A]): G[B] =
from(F.ap(to(f))(to(ga)))
def tailRecM[A, B](a: A)(fn: A => G[Either[A, B]]): G[B] =
from(F.tailRecM(a)(fn.map(to)))
}
def distributive[F[_], G[_]](implicit F: Distributive[F], I: IsoK[F, G]): Distributive[G] =
new Distributive[G] {
import I._
def map[A, B](ga: G[A])(f: A => B): G[B] = from(F.map(to(ga))(f))
def distribute[H[_]: Functor, A, B](ha: H[A])(f: A => G[B]): G[H[B]] =
from(F.distribute(ha)(f.map(to)))
}
}
@kailuowang
Copy link
Author

Basically this allows you to easily take advantage of the Monad[Function1] if you alg trait is basically a A=>B or Transformer such as OptionT or Nested, if your alg trait is A => T[B]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment