【问题标题】:Scalaz implementation of Semigroup using advanced Scala features使用高级 Scala 功能的 Semigroup 的 Scalaz 实现
【发布时间】:2017-12-29 09:18:23
【问题描述】:

我正在研究 Scalaz 中 Monoids 的实现。我遇到了|+| 运算符,如果您在 Monoid 上定义 append 操作,它应该是开箱即用的。该运算符的定义在 SemigroupSyntax 中。该类通过Semigroup 进入 Monoid。

在检查了这三个类之后,我有一个主要问题 - SemigroupSyntax 的评论究竟是如何达到/** Wraps a value `self` and provides methods related to `Semigroup` */

有一些隐含的魔力,在 trait 上调用 .this 等等,我真的不明白 SemigroupSyntax

如果有人能花时间启发我,我会很高兴。

提前谢谢你!

编辑:

我很想了解这个类的工作原理:

package scalaz
package syntax

/** Wraps a value `self` and provides methods related to `Semigroup` */
final class SemigroupOps[F] private[syntax](val self: F)(implicit val F: Semigroup[F]) extends Ops[F] {
  ////
  final def |+|(other: => F): F = F.append(self, other)
  final def mappend(other: => F): F = F.append(self, other)
  final def ⊹(other: => F): F = F.append(self, other)
  ////
}

trait ToSemigroupOps  {
  implicit def ToSemigroupOps[F](v: F)(implicit F0: Semigroup[F]) =
    new SemigroupOps[F](v)

  ////
  ////
}

trait SemigroupSyntax[F]  {
  implicit def ToSemigroupOps(v: F): SemigroupOps[F] = new SemigroupOps[F](v)(SemigroupSyntax.this.F)

  def F: Semigroup[F]
  ////
  def mappend(f1: F, f2: => F)(implicit F: Semigroup[F]): F = F.append(f1, f2)

  ////
}

以及它在 Semigroup 中的调用点:

val semigroupSyntax = new scalaz.syntax.SemigroupSyntax[F] { def F = Semigroup.this }

【问题讨论】:

  • 如果您粘贴您不理解的代码,它会更容易帮助您。

标签: scala traits implicits monoids semigroup


【解决方案1】:

这里最令人迷惑的是,实际上有两种方法可以对对象进行操作。

第一个路由,默认路由,是import scalaz.syntax.semigroup._。它为所有隐式可用的Semigroup 实例添加了运算符。

  1. 任何Semigroup 实例都会为自己创建SemigroupSyntax 的实现,根据this 定义其F 方法。
  2. 在 scalaz/syntax/package.scala 中,有一个扩展 Syntaxes 特征的 syntax 单例对象。它是导入定义的第一部分。
  3. 在 scalaz/syntax/Syntax.scala 中,Syntaxes 中的 semigroup 单例对象用于 syntax,它扩展了 ToSemigroupOps。我们正在导入这个对象的内容,只包含隐式转换。转换的目的是捕获隐式提供的Semigroup 实例并构造一个包装器SemigroupOps,其中包含所有操作。

第二条路线是import [your_semigroup_instance].semigroupSyntax._ 的快捷方式,Semigroup 中的呼叫站点您已列出。它将运算符添加到特定类型,Semigroup 实例就是该类型。

  1. semigroupSyntaxSemigroupSyntax trait 的匿名实现,其中 F 方法被定义为 Semigroup 的特定实例。
  2. SemigroupSyntax trait 本身,就像 ToSemigroupOps 一样,提供到 SemigroupOps 的隐式转换,但它不是捕获隐式提供的实例,而是使用其 F 方法。因此,我们使用特定的 Semigroup 类型类实现来获取类型 F 的运算符。

【讨论】:

  • 感谢您的详尽解释。即使我读了好几遍,我也不是很明白,但我有一些简单的问题。在您看来,图书馆设计师如何从所有这些误导中受益?是关于代码可扩展性吗?因为在我看来,这件事很难维护。
  • 设计实际上是非常模块化的:每个类型类一个文件,一个用于语法的文件,以及在 Syntax.scala 中的单行注册(可能还有scalaz.std 中的一些实例)。类型类及其语法包装器的代码遵循通用模式,虽然在 Scalaz 中没有使用,但它有 code-generation compiler plugin。虽然包装机制本身的设计与 Haskell 编码实践非常相似,但看起来可能很复杂,但库架构仍然清晰且松散耦合。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-29
  • 2016-05-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多