2017-07-23 4 views
0

Je recherchais l'implémentation de Monoid dans Scalaz. Je suis tombé sur l'opérateur |+| qui est censé venir hors de la boîte si vous définissez l'opération append sur Monoid. La définition de cet opérateur est SemigroupSyntax. Cette classe obtient à Monoid par Semigroup.Implémentation Scalaz de Semigroup à l'aide des fonctionnalités avancées de Scala

Après avoir examiné ces trois classes, j'ai une grande question - Comment est exactement le commentaire de SemigroupSyntax réalisé /** Wraps a value `self` and provides methods related to `Semigroup` */

Il y a un peu de magie avec implicits, appelant .this sur trait et plus dans le SemigroupSyntax que je Honnêtement, ne comprends pas.

J'aimerais que quelqu'un prenne le temps de m'éclairer.

Merci d'avance!

EDIT:

Je suis désireux de comprendre le fonctionnement de cette classe:

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) 

    //// 
} 

et son site d'appel à Semigroup:

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

Si vous collez le code que vous ne comprenez pas, il serait beaucoup plus facile de vous aider. – pedrofurla

Répondre

0

La chose la plus déroutante est ici qu'il ya effectivement deux routes pour obtenir des opérations sur l'objet. La première route, celle par défaut, est import scalaz.syntax.semigroup._. Il ajoute des opérateurs pour toutes les instances Semigroup implicitement disponibles.

  1. Tout Semigroup exemple crée une implémentation de SemigroupSyntax pour lui-même, définir sa méthode F en termes de this.
  2. Dans scalaz/syntax/package.scala, il y a un objet singleton syntax qui étend le trait Syntaxes. C'est la première partie de la définition d'importation.
  3. Dans scalaz/syntax/Syntax.scala, il y a un objet singleton semigroup dans Syntaxes trait utilisé dans syntax, qui s'étend ToSemigroupOps. Nous importons le contenu de cet objet, contenant uniquement la conversion implicite. Le but de la conversion est de capturer l'instance Semigroup fournie implicitement et de construire un wrapper, SemigroupOps, qui contient toutes les opérations.

La deuxième route est un raccourci par import [your_semigroup_instance].semigroupSyntax._, un site d'appel dans Semigroup vous êtes répertorié. Il ajoute des opérateurs à un type particulier, pour lequel l'instance Semigroup est.

  1. semigroupSyntax est une implémentation anonyme de SemigroupSyntax trait, la méthode F est définie comme une instance particulière de Semigroup.
  2. SemigroupSyntax lui-même, comme ToSemigroupOps, offre une conversion implicite en SemigroupOps, mais au lieu de capturer l'instance implicitement fournie, il utilise sa méthode F. Nous obtenons donc des opérateurs sur le type F, en utilisant notamment Semigroup implémentation typeclass.
+0

Merci pour l'explication détaillée. Je ne le comprends pas vraiment même si je l'ai lu plusieurs fois mais j'ai quelques questions simples. Comment les concepteurs de bibliothèque bénéficient-ils à votre avis de toutes ces erreurs de direction? S'agit-il de l'extensibilité du code? Parce que dans mon esprit, cette chose est l'enfer à maintenir. – zaxme

+1

La conception est en fait très modulaire: un fichier par classe de type, un autre pour la syntaxe, et un enregistrement par doublure dans Syntax.scala (plus, peut-être, quelques instances dans 'scalaz.std'). Le code pour la classe de type et ses wrappers de syntaxe suit le modèle commun, et, bien qu'il ne soit pas utilisé dans Scalaz, il y a [plugin de compilateur de code-génération] (https://github.com/mpilquist/simulacrum) pour cela. Alors que le mécanisme d'emballage lui-même, conçu pour ressembler étroitement aux pratiques de codage Haskell, peut sembler compliqué, l'architecture de la bibliothèque reste claire et couplée de manière lâche. –