Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

|+| is a semigroup, why it needs a monoid implicit resolution

The aim of Semigroup is to make sure Associativity and closure The aim of monoid is based on Semigroup and provide additional Identity. When I use |+| semigroup appender, why I have define implicit monoid not implicit semigroup

Here is the code I am using reduceLeft which doesnt require the initial value

    val result1 = List(Staff("John", 36), Staff("Andrew", 30))
    val result2 = List(Staff("John", 40), Staff("Danny", 30))
    val result3 = List(Staff("Andrew", 30))
    val result4: List[Staff] = List()

    implicit val staffListSemigroup = new Monoid[List[Staff]] {

      override def zero: List[Staff] = Nil

      override def append(f1: List[Staff], f2: => List[Staff]): List[Staff] = {

        val mapSemigroup = f1.map(t => (t.name, t.numberOfTasks)).toMap |+| f2.map(t => (t.name, t.numberOfTasks)).toMap

        mapSemigroup.map(t => Staff(t._1, t._2)).toList

      }
    }

    val result = List(result1, result2, result3, result4).reduceLeft(_ |+| _)

    assert(result.size == 3)

if staffListSemigroup is Semigroup[List[Staff]], the compilation error is value |+| is not a member of List[SemigroupSpec.this.Staff]

Also, the definition of |+| is inside the 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)
  ////
}

Many thanks in advance

Edit

followed by @Travis answer, I dont think it is correct. For the implicit value, the specific value will always override the generic one. Here is a code example I just wrote:

case class Foo(i : Int, s : String)
class Test[T] {
  def print = "print test"
}

implicit val test = new Test[Any]
implicit val testMoreSpecific = new Test[Foo] {
  override def print = "print Foo"
}

def doStuff[A](implicit test: Test[A]) = {
  test.print
}

doStuff[Foo] //it successfully print out print Foo
doStuff //compilation error, ambiguous implicit value found

Is that because in the Scalaz, no type specified in the method like Foo is not specified.

like image 759
Xiaohe Dong Avatar asked Aug 24 '14 02:08

Xiaohe Dong


1 Answers

The problem is that there's already a Semigroup for List[A] for any A. You've defined a more specific instance for List[Staff], which leads to an ambiguity, as you can see by asking for the instance:

scala> Semigroup[List[Staff]]
<console>:17: error: ambiguous implicit values:
 both method listMonoid in trait ListInstances of type [A]=> scalaz.Monoid[List[A]]
 and value staffListSemigroup of type => scalaz.Semigroup[List[Staff]]
 match expected type scalaz.Semigroup[List[Staff]]
              Semigroup[List[Staff]]
                       ^

You could jump through some hoops and try to keep the Scalaz-provided instance out of scope, but please don't!—it's potentially very confusing for other users and is a violation of some basic good principles for working with type classes. Instead you can write a wrapper for List[Staff] (a simple case class would do) and then provide an instance for that type.


For the sake of completeness, it's worth noting that the version with Monoid compiles because Monoid is more specific than Semigroup (see section 6.26.3 of the language specification for the rules that apply here, but be warned that they're kind of a mess).

like image 128
Travis Brown Avatar answered Oct 18 '22 20:10

Travis Brown