5

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.

Viswanath
  • 1,413
  • 13
  • 25
  • 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 Feb 10 '18 at 13:44
  • 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 `Any` – Euge Feb 10 '18 at 14:17
  • 1
    @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 Feb 12 '18 at 19:32
  • @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 Feb 12 '18 at 19:40
  • 1
    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 Feb 13 '18 at 19:06

1 Answers1

2

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)
Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
  • Thanks for the suggestion but I was looking for a solution that wouldn't require me to explicitly specify the types but let the compiler work harder and find candidates that match through implicit conversion. In my particular case, apply implicit conversion from Int to Id and then apply those as parameters for the function. As my objective is to attain elegance of client code, overloaded function definition would also suffice if implicit conversion cannot be applied. – Viswanath Feb 12 '18 at 19:45
  • If you don't specify types: `PowerOfMonads.sumOfSquares(1, 2)`, how is compiler supposed to infer that you meant `PowerOfMonads.sumOfSquares[Id, Int, Int](1, 2)` and not for example `PowerOfMonads.sumOfSquares[Const[Int]#λ, Int, Int](1, 2)` where `type Const[C] = { type λ[T] = C }`? – Dmytro Mitin Feb 12 '18 at 20:51
  • @Dmytro Mitin: I don't understand how could be possible that Scala to suppose that you want to use the custom type: `type Const[C] = { type λ[T] = C }` given by you as example, when this type, doesn't have anything in common with Monads? – Octavian R. Feb 12 '18 at 23:16
  • @OctavianR. Firstly, it's not true that there's nothing in common. `Const[Int]#λ` is a `cats.FlatMap` and so is `Id`. And `Monad` is `FlatMap` plus `pure`. `sumOfSquares` doesn't use `pure`, so it can be defined with FlatMap. If we don't expect type inference for `FlatMap` why should we do for `Monad`? Secondly, we could define some instance even of Monad for `Const[Int]#λ`. This will be illegal one but why should this be relevant? I hope we don't expect Scala compiler to check Monad laws, do we? Yes, `Const[Int]#λ` is a custom type, but so is `Id`. – Dmytro Mitin Feb 13 '18 at 21:02
  • I understand your explanation, but maybe I missing something, because I don't see any `Const` type in `cats.FlatMap` [link](https://github.com/typelevel/cats/blob/ff7bd409ea21775087552daba12078b79f3d2abf/core/src/main/scala/cats/FlatMap.scala) – Octavian R. Feb 14 '18 at 10:27
  • @OctavianR. Why is that relevant? There could be. We are talking about how compiler can infer types, not about some specific library (for example we can define instance manually). There is `cats.data.Const` in Cats, `Const[Int]#λ` is from Shapeless. – Dmytro Mitin Feb 14 '18 at 10:40