import scala.util.Try import cats.Functor import cats.Align import cats.data.Ior type Decimal = Int type Roman = String def f[F[_]: Align: Functor](fi: F[Decimal], fs: F[Roman]): F[String] = import cats.syntax.align.* // <- HERE import cats.syntax.functor.* // `align` is like a "zip on steroids"! // It pairs 2 effects regardless whether they are symmetric or not // i.e they have the same size, they are both defined, they are both successful, etc. // // The `Align` typeclass provides more interesting "zippers" like this. // Check them out! https://typelevel.org/cats/api/cats/Align.html fi.align(fs).map { ior => ior match case Ior.Both(decimal, roman) => s"$decimal is $roman in Roman" case Ior.Left(decimal) => s"$decimal has no Roman" case Ior.Right(roman) => s"$roman has no Decimal" } /// val decimals1 = List(1, 2, 3, 0) val romans1 = List("I", "II", "III") val out1 = f(decimals1, romans1) // List[String] = List(1 is I in Roman, 2 is II in Roman, 3 is III in Roman, 0 has no Roman) val decimals2 = List(1, 2, 3) val romans2 = List("I", "II", "III", "IV") val out1a = f(decimals2, romans2) // List[String] = List(1 is I in Roman, 2 is II in Roman, 3 is III in Roman, IV has no Decimal) val out1b = f(List.empty, List.empty) // List[String] = List() /// import cats.syntax.option.* val out2 = f(0.some, None) // Option[String] = Some(1 is I in Roman) val out2a = f(1.some, "I".some) // Option[String] = Some(1 is I in Roman) val out2b = f(None, "X".some) // Option[String] = Some(X has no Decimal) val out2c = f(None, None) /// type Either_[A] = Either[String, A] val out3 = f(Right(1), Left("ERROR")) // Either[String, String] = Right(1 has no Roman) val out3a = f[Either_](Right(1), Right("I")) // Either[String, String] = Right(1 is I in Roman) val out3b = f(Left("ERROR"), Right("X")) // Either[String, String] = Right(X has no Decimal) val out3c = f[Either_](Left("ERROR"), Left("ERROR")) // Either[String, String] = Left(ERROR)