Scalaz implementation of Semigroup using advanced Scala features

164 views Asked by At

I was digging through the implementation of Monoids 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 }
1

There are 1 answers

2
P. Frolov On BEST ANSWER

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.