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.
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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With