简体   繁体   中英

Scalaz implementation of Semigroup using advanced Scala features

I was digging through the implementation of Monoid s in Scalaz. I came across the |+| operator that is supposed to come out of the box if you define the append operation on Monoid. The definition of this operator is in SemigroupSyntax . That class gets to Monoid through Semigroup .

After examining these three classes I have one major question - How exactly is the comment from SemigroupSyntax achieved /** Wraps a value `self` and provides methods related to `Semigroup` */

There is some magic with implicits, calling .this on trait and more in the SemigroupSyntax that I honestly don't understand.

I would love if someone could take the time to enlighten me.

Thank you in advance!

EDIT:

I am keen to understand the workings of this class:

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)

  ////
}

And its call site in Semigroup:

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

Most disorienting thing here is that there's actually two routes to getting operations on object.

First route, the default one, is by import scalaz.syntax.semigroup._ . It adds operators for all implicitly available Semigroup instances.

  1. Any Semigroup instance creates an implementation of SemigroupSyntax for itself, defining its F method in terms of this .
  2. In scalaz/syntax/package.scala, there's a syntax singleton object that extends Syntaxes trait. It is the first part of import definition.
  3. In scalaz/syntax/Syntax.scala, there's a semigroup singleton object within Syntaxes trait used in syntax , which extends ToSemigroupOps . We're importing contents of this object, containing only the implicit conversion. The purpose of conversion is to capture implicitly provided Semigroup instance and construct a wrapper, SemigroupOps , which contains all the operations.

Second route is a shortcut by import [your_semigroup_instance].semigroupSyntax._ , a call site in Semigroup you're listed. It adds operators to a particular type, for which Semigroup instance is.

  1. semigroupSyntax is an anonymous implementation of SemigroupSyntax trait, which F method is defined to be a particular instance of Semigroup .
  2. SemigroupSyntax trait itself, like ToSemigroupOps , offers an implicit conversion to SemigroupOps , but instead of capturing implicitly provided instance, it uses its F method. So we get operators on type F , using particular Semigroup typeclass implementation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM