5
votes

I have a function that computes sum of squares for numeric types as shown below.

import cats.syntax.functor._
import cats.syntax.applicative._
import cats.{Id, Monad}

import scala.language.higherKinds

object PowerOfMonads {
       /**
        * Ultimate sum of squares method
        *
        * @param x First value in context
        * @param y Second value in context
        * @tparam F Monadic context
        * @tparam T Type parameter in the Monad
        * @return Sum of squares of first and second values in the Monadic context
        */
    def sumOfSquares[F[_]: Monad, A, T >: A](x: F[A], y: F[A])(implicit num: Numeric[T]) : F[T] = {
        def square(value:T): T = num.times(value, value)
        def sum(first:T, second:T): T = num.plus(first, second)

        for {
            first <- x
            second <- y
        } yield sum(square(first), square(second))
    }
}

From the client code, I would like to utilise the function as shown below

import cats.Id
import cats.instances.future._
import cats.instances.list._
import cats.instances.option._
import cats.syntax.applicative._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future

println(s"Sum of squares for list ${PowerOfMonads.sumOfSquares(  List(1,2,3), List(1,2,3) )}")
println(s"Sum of squares for options ${PowerOfMonads.sumOfSquares(  Option(1), 2.pure[Option] )}")
println(s"Sum of squares for future ${PowerOfMonads.sumOfSquares(  1.pure[Future], Future(2) ).value}")
println(s"Sum of squares for id ${PowerOfMonads.sumOfSquares(1.pure[Id], 2.pure[Id])}")

Now, I would like to use implicit conversion from a numeric type T to Id[T] to invoke the function sumOfSquares as shown below

println(s"Sum of squares for int ${PowerOfMonads.sumOfSquares(1, 2)}")

using a function as shown below

import cats.syntax.applicative._
import scala.language.implicitConversions
   /**
    * Implicit conversion for any numeric type T to Id[T]
    * @param value value with type T
    * @tparam T numeric type
    * @return numeric type wrapped in the context of an Identity Monad
    */
implicit def anyNum2Id[T](value:T)(implicit num: Numeric[T]):Id[T] = value.pure[Id]

However, when executing the program, I get the following errors

could not find implicit value for evidence parameter of type cats.Monad[[A]Any] println(s"Sum of squares for int

${PowerOfMonads.sumOfSquares(1, 2)}") not enough arguments for method sumOfSquares: (implicit evidence$1: cats.Monad[[A]Any], implicit num: Numeric[T])Any. Unspecified value parameters evidence$1, num. println(s"Sum of squares for int ${PowerOfMonads.sumOfSquares(1, 2)}")

Please help me resolve the error.

1
Could you please specify the exact scala and cats versions you are using? It doesn't seem to compile out of the box, at least not with my settings: PowerOfMonads gives "value flatMap is not a member of type parameter F[A]: first <- x"Andrey Tyukin
Do the List version of the client code fail? For the Id case, have you tried specifying the type parameter in the function call? What I see from the error logs is that the interpreter is not guessing that the type would be Id and tries to find evidence of Monad and Numberic for type AnyEuge
@Andrey Tyukin: Scala version I used is 2.12.4 and cats version is 1.0.1. Don't forget the compiler flag scalacOptions += "-Ypartial-unification"Viswanath
@Euge: List version of client code works. The idea is not to let the compiler infer types not requiring one to specify type parameters. The problem here is, compiler doesn't trigger implicit conversion from "Int to Id" imported and brought into scope instead it directly tries to seek evidence for Monad and Numeric for type Any. The real question is, is there any neat trick to aid and guide the compiler? Is my objective even feasible or it's beyond the bounds of the language?Viswanath
The thing is, there's no way the compiler can guess that you specifically want a conversion from Int to Id. Even if it was the only choice, it has to search for all other possible choices to get to the conclusion that it was in fact the only one. It's beyond the bounds of computability I'd say.Euge

1 Answers

2
votes

Change your imports:

  import cats.syntax.flatMap._
  import cats.syntax.functor._
  import cats.Monad

  import scala.language.higherKinds

  object PowerOfMonads {
  ...

You can help compiler to infer types:

PowerOfMonads.sumOfSquares(1: Id[Int], 2: Id[Int])

or

PowerOfMonads.sumOfSquares[Id, Int, Int](1, 2)