【问题标题】:How monoids generalize over types幺半群如何泛化类型
【发布时间】:2021-11-21 15:09:45
【问题描述】:

我在 scala 中玩猫的 Monoid,当时我看到 Monoid 操作以自然的方式扩展到 Tuples:

import cats.Monoid

object mon {
  implicit object IntMonoid extends Monoid[Int] {
    def combine(a: Int, b: Int) = a*a + b*b
    def empty = 0
  }

  implicit object ListMonoid extends Monoid[List[Int]] {
    def combine(a: List[Int], b: List[Int]): List[Int] =
      a.zip(b).map(z => z._1 * z._2)
    def empty = List(1)
  }

  def comb[T](a: T, b: T)(implicit m: Monoid[T]) =
    m.combine(a, b)
}

val list1 = List(1, 2, 3)
val list2 = List(2, 3, 4)
println(mon.comb(list1, list2)) // outputs: List(2, 6, 12) as expected

val int1 = 2
val int2 = 4
println(mon.comb(int1, int2)) // outputs: 20 as expected

val x = (list1, int1)
val y = (list2, int2)
println(mon.comb(x, y)) // outputs: (List(2, 6, 12),20)

最后的输出是“自然”的方式,但是 de 编译器怎么知道怎么做呢? 我一直在尝试在 Cats 的源代码中寻找它,但我在 Scala 方面的经验不如能够知道要寻找什么。我想同样的方法也适用于类似的结构,比如半群。

【问题讨论】:

  • 我复制/粘贴了您的整个示例并收到此错误:error: could not find implicit value for parameter m: Monoid[(List[Int], Int)]。在某处必须有这些类型的实例。
  • 我认为您将自己的 Monoidcats 混在一起,对吗?无论如何,cats 为元组提供一个实例,就像它为任何List 或任何Option 提供一个实例一样,通过使用隐式推导并要求证明元组的类型也具有@987654327 @ 与它们关联的实例。 - 也许你的问题是这样的?如何编码复杂类型的typeclass派生?
  • 是的@LuisMiguelMejíaSuárez,我在询问这些事情是如何实现的,以便让编译器知道如果我只为这两种类型提供幺半群操作,当我调用 comb 函数时,它必须对每个单独执行组件。
  • @Dioni 您要查找的代码在此处生成:github.com/typelevel/cats/blob/…

标签: scala functional-programming scala-cats


【解决方案1】:

您的问题归结为泛型类型的类型类的隐式派生如何工作;让我们看两个例子:

不管泛型是什么,我们都想提供一个实例:

// Similar to the code you had, but without being tied to just List[Int],
// Since in this case the Int part is irrelevant.
implicit def monoidList[A]: Monoid[List[A]] =
  new Monoid[List[A]] {
    override final val empty: List[A] = Nil

    override final def combine(l1: List[A], l2: List[A]): List[A] =
      l1 ::: l2
  }

我们需要泛型类型的证明来提供复杂类型的实例的情况:

implicit def optionMonoid[A](implicit aMonoid: Monoid[A]): Monoid[Option[A]] =
  new Monoid[Option[A]] {
    override final val empty: Option[A] = None

    override final def combine(o1: Option[A], o2: Option[A]): Option[A] =
      (o1, o2) match {
        case (None, None)         => None
        case (Some(a), None)      => Some(a)
        case (None, Some(a))      => Some(a)
        case (Some(a1), Some(a1)) => Some(aMonoid.combine(a1, a2))
      }
  }

因此,您现在可以想象 catsMonoid[Tuple2[A, B]] 是如何工作的,但为了完整起见,代码如下所示:

implicit def tuple2Monoid[A, B](implicit aMonoid: Monoid[A], bMonoid: Monoid[B]): Monoid[(A, B)] =
  new Monoid[(A, B)] {
    override final def empty: (A, B) =
      (aMonoid.empty, bMonoid.empty)

    override final def combine(t1: (A, B), t2: (A, B)): (A, B) =
      (t1, t2) match {
        case ((a1, b1), (a2, b2)) => (aMonoid.combine(a1, a2), bMonoid.combine(b1, b2))
      }
  }

【讨论】:

  • 更准确地说,该行为的实现可以在cats code中找到
  • @Dioni 这是MonoidK 的代码,而不是Monoid。如果我没记错的话,元组实例的代码是由猫内核中的 sbt 插件生成的。
  • 这仍然不知何故并没有真正回答“de compiler如何知道如何做到这一点?”,指向实际cats实现的链接,以及有关如何在文档中找到它的提示将乐于助人。
  • @AndreyTyukin Monoid 的元组实例在此处生成:github.com/typelevel/cats/blob/… 这里还有一些其他样板生成位置:github.com/typelevel/cats/tree/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-12
  • 2018-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多