0
import cats._
import cats.implicits._

object SemigroupInstances {
  implicit val intSemigroup: Semigroup[Int] = new Semigroup[Int] {
    override def combine(a: Int, b: Int) = a * b
  }
}

import SemigroupInstances._

object Semigroup {
  def apply[T](implicit instance: Semigroup[T]): Semigroup[T] = instance
}

val intSemigroup = Semigroup[Int]

intSemigroup.combine(4, 4) // expected 16, actual 8

So, how to make custom intSemigroup working?

Lesha Pipiev
  • 3,251
  • 4
  • 31
  • 65
  • 1
    You import two instances of `Semigroup[Int]` - one via `import cats.implicits._`, one via `import SemigroupInstances._`. Apparently, the compiler choose the one from `import cats.implicits._` over your custom instance. If you remove that import, it works: https://scastie.scala-lang.org/avsmEturTRGkNEOCDxuXOA – MartinHH May 14 '23 at 14:15
  • If removing that import is not an option for you, you might want to look into the details of implicit resolution precedence. I'm sure there are ways to ensure that your instance given higher precedence. – MartinHH May 14 '23 at 14:18
  • 1
    @MartinHH Appreciate your help. Post please an answer, I'll accept it. – Lesha Pipiev May 14 '23 at 14:40
  • Is this in the REPL? The REPL has some implementation details (compilation units and such) which sometimes mess up implicit search. – Levi Ramsey May 14 '23 at 15:31
  • @LeviRamsey, no, this is from Scala Worksheet – Lesha Pipiev May 14 '23 at 17:07
  • 1
    @LeshaPipiev I posted an answer now. I did not really want to answer without understanding why the compiler chooses the instance from cats over your instance, but I figured that out now as well (see bottom part of the answer). – MartinHH May 15 '23 at 16:22
  • 1
    For reference, a more idiomatic way to solve this is by using a newtype around int to drive selection of different instances – Daenyth Jun 06 '23 at 14:26

1 Answers1

0

You import two instances of Semigroup[Int]:

  1. via import cats.implicits._
  2. via import SemigroupInstances._

The compiler chooses the one from cats.implicits._ over your custom instance.

If you remove that import, it works: https://scastie.scala-lang.org/avsmEturTRGkNEOCDxuXOA

If you want to know more about how the compiler chooses which implicit instance to use, see this post: Where does Scala look for implicits?

In your specific case, the reason appears to be that the type of the instance from cats is actually CommutativeGroup[Int]:

implicit val catsKernelStdGroupForInt: CommutativeGroup[Int] = new IntGroup

Hence, being a derived class (CommutativeGroup extends Semigroup), it is considered more specific and hence preferred.

If you change the type of your instance to CommutativeGroup[Int] (which is not possible without being mathematically incorrect, but that's not the point here), you get an "Ambiguous given instances" error because the compiler cannot decide which instance to use: https://scastie.scala-lang.org/dzClDOYDSJ20P1SRpH72nA

MartinHH
  • 982
  • 4
  • 7