scalavalidationscala-catssemigroup

Using Validated from the Cats library in Scala


Cannot understand the actual difference between Semigroupal.product and Semigroupal.tuple2. Here is a short example:

import cats.Semigroupal
import cats.data.Validated
import cats.data.Validated.Invalid
import cats.instances.list._ // for Monoid

  type AllErrorsOr[A] = Validated[List[String], A]
  def bothInvalid = {
    Semigroupal[AllErrorsOr].product(
      Validated.invalid(List("Error 1")),
      Validated.invalid(List("Error 2"))
    )
  }

  def bothInvalidTuple = {
    Semigroupal.tuple2(
      Validated.invalid(List("Error 1")),
      Validated.invalid(List("Error 2"))
    )
  }

  def bothValid = {
    Semigroupal[AllErrorsOr].product(
      Validated.valid(10),
      Validated.valid(20)
    )
  }

  def bothValidTuple = {
    Semigroupal.tuple2(
      Validated.valid(10),
      Validated.valid(20)
    )
  }

With invalids both bothInvalid and bothInvalidTuple give the same result. With valid values, only the first one is compiled. The error I am getting:

Error:(40, 23) could not find implicit value for parameter semigroupal: cats.Semigroupal[[+A]cats.data.Validated[Nothing,A]] Semigroupal.tuple2(

It seems (if I am not wrong) Scala tries to find Monoid to combine Nothing, but not List[String]. How to get it work with tuple2?


Solution

  • Just some generics were not inferred. Try to specify them explicitly

      type AllErrorsOr[A] = Validated[List[String], A]
    
      def bothInvalid: AllErrorsOr[(Int, Int)] = {
        Semigroupal[AllErrorsOr].product[Int, Int](
          Validated.invalid(List("Error 1")),
          Validated.invalid(List("Error 2"))
        )
      }
    
      def bothInvalidTuple: AllErrorsOr[(Int, Int)] = {
        Semigroupal.tuple2[AllErrorsOr, Int, Int](
          Validated.invalid(List("Error 1")),
          Validated.invalid(List("Error 2"))
        )
      }
    
      def bothValid: AllErrorsOr[(Int, Int)] = {
        Semigroupal[AllErrorsOr].product[Int, Int](
          Validated.valid(10),
          Validated.valid(20)
        )
      }
    
      def bothValidTuple: AllErrorsOr[(Int, Int)] = {
        Semigroupal.tuple2[AllErrorsOr, Int, Int](
          Validated.valid(10),
          Validated.valid(20)
        )
      }