scala - Scala 中的 ISO 宏

标签 scala implicit-conversion scalaz scala-macros scalaz7

如果我想将两个对象从一个隐式转换为另一个对象,是否可以使用 Iso 宏之类的东西来执行此操作?

例如,如果我有这个:

implicit def listToMap[A, B](l: List[(A, B)]): Map[A, B] = l.toMap
implicit def mapToList[A, B](m: Map[A, B]): List[(A, B)] = m.toList

我想简单地写:
implicit def[A, B] listMapIso = Iso[List[(A, B)], Map[A, B]] {_.toMap, _.toList}

注意:如下所述,我计划在我的 web 框架中使用它,我将我的数据库模型转换为中间件/前端模型。

最佳答案

您似乎混淆了几个不同的概念。 Iso、隐式转换和宏彼此完全不同。

我们当然可以为参数化类型定义一个等效的 Iso,尽管语法变得有点麻烦:

import scalaz._, Scalaz._
case class BiIso[F[_, _], G[_, _]](left: F ~~> G,
  right: G ~~> F)

type PairList[A, B] = List[(A, B)]
val listToMap = new (PairList ~~> Map) {
  def apply[A, B](l: PairList[A, B]) = l.toMap
}
val mapToList = new (Map ~~> PairList) {
  def apply[A, B](m: Map[A, B]) = m.toList
}

val listMapIso = BiIso(listToMap, mapToList)

我们当然可以使部分隐含,尽管这是一个正交问题。我们可以隐式构建 BiIso:
implicit val listToMap = new (PairList ~~> Map) {
  def apply[A, B](l: PairList[A, B]) = l.toMap
}
implicit val mapToList = new (Map ~~> PairList) {
  def apply[A, B](m: Map[A, B]) = m.toList
}

implicit def biIso[F[_, _], G[_, _]](implicit left: F ~~> G, right: G ~~> F) =
  BiIso(left, right)

implicitly[BiIso[PairList, Map]]

我们可以让任何 BiIso 充当隐式转换,尽管我建议不要这样做。唯一棘手的部分是正确引导类型推断。这是那里的大部分方式,但由于某种原因,不会推断出 GAB 参数(非常欢迎更正):
sealed trait BiAny[F[_, _]] {}
object BiAny {
  implicit def any[F[_, _]] = new BiAny[F] {}
}

sealed trait ApplyBiIso[FAB, GAB] {
  type A1
  type B1
  type F[_, _]
  type G[_, _]
  type Required = BiIso[F, G]

  val unapplyL: Unapply2[BiAny, FAB] {
    type A = A1; type B = B1;
    type M[C, D] = F[C, D]
  }
  val unapplyR: Unapply2[BiAny, GAB] {
    type A = A1; type B = B1;
    type M[C, D] = G[C, D]
  }

  def liftBI(bi: Required): Iso[FAB, GAB] =
    Iso({ fab: FAB =>
      val f: F[A1, B1] = Leibniz.witness(unapplyL.leibniz)(fab)
      val g: G[A1, B1] = bi.left(f)
      Leibniz.witness(Leibniz.symm[⊥, ⊤, GAB, G[A1, B1]](unapplyR.leibniz))(g): GAB
    },
      { gab: GAB =>
        val g: G[A1, B1] = Leibniz.witness(unapplyR.leibniz)(gab)
        val f: F[A1, B1] = bi.right(g)
        Leibniz.witness(Leibniz.symm[⊥, ⊤, FAB, F[A1, B1]](unapplyL.leibniz))(f): FAB
      }
    )
}

object ApplyBiIso {
  implicit def forFG[FAB, A2, B2, GAB, A3, B3](
    implicit u1: Unapply2[BiAny, FAB] { type A = A2; type B = B2 },
    u2: Unapply2[BiAny, GAB] { type A = A3; type B = B3 }) = new ApplyBiIso[FAB, GAB] {
    type A1 = A2
    type B1 = B2
    type F[C, D] = u1.M[C, D]
    type G[C, D] = u2.M[C, D]

    //Should do the conversion properly with Leibniz but I can't be bothered
    val unapplyL = u1.asInstanceOf[Unapply2[BiAny, FAB] {
      type A = A1; type B = B1;
      type M[C, D] = F[C, D]
    }]
    val unapplyR = u2.asInstanceOf[Unapply2[BiAny, GAB] {
      type A = A1; type B = B1;
      type M[C, D] = G[C, D]
    }]
  }
  type Aux[FAB, GAB, Required1] = ApplyBiIso[FAB, GAB] { type Required = Required1 }
  def apply[FAB, GAB](implicit abi: ApplyBiIso[FAB, GAB]): Aux[FAB, GAB, abi.Required] = abi
}

sealed trait AppliedBiIso[FAB, GAB] {
  val iso: Iso[FAB, GAB]
}
object AppliedBiIso {
  implicit def applyAndIso[FAB, GAB, Required1](
    implicit ap: ApplyBiIso.Aux[FAB, GAB, Required1],
    iso1: Required1) = new AppliedBiIso[FAB, GAB] {
    //Should do the conversion properly with Leibniz but I can't be bothered
    val iso = ap.liftBI(iso1.asInstanceOf[BiIso[ap.F, ap.G]])
  }
}

implicit def biIsoConvert[FAB, GAB](
  f: FAB)(implicit ap: AppliedBiIso[FAB, GAB]): GAB =
  ap.iso.left(f)

val map: Map[String, Int] = Map("Hello" -> 4)

val list: PairList[String, Int] =
  biIsoConvert[Map[String, Int], PairList[String, Int]](map)

我毫不怀疑有可能使这项工作正常进行。

这仍然留下宏,这又是一个或多或少的正交问题。我可以看到它们可能相关的一个地方是,如果不使用宏,就不可能在 Scala 中对种类进行抽象。您是否想要一个适用于任何“形状”的等效 Iso,而不仅仅是 F[_, _] ?这将是宏的一个很好的用例 - 尽管在我不羡慕任何试图实现它的人之前已经编写了这种宏。

关于scala - Scala 中的 ISO 宏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24703424/

相关文章:

scala:无论函数有多少参数,都要记住一个函数?

scala - 如何关闭 Akka 流?

java - java运算符中的隐式转换+=

scala - 将 Option[Validation[E, A]] 转换为 Validation[E, Option[A]]

scala - 在 Scala 中创建动态增长数组的最佳方法是什么?

scala - 为 Spark 作业的单元测试模拟 HTable 数据

function - Scala 中具有重载方法的方法和函数之间的 Eta 扩展

scala - 如何向 scala 集合对象添加采用隐式排序的方法

scala - 通过 `State` 映射 `Lens`

scala - Scala 中的协变树