Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error handling and abstract effects #313

Closed
erdeszt opened this issue Apr 29, 2019 · 3 comments
Closed

Error handling and abstract effects #313

erdeszt opened this issue Apr 29, 2019 · 3 comments

Comments

@erdeszt
Copy link
Contributor

erdeszt commented Apr 29, 2019

I'm trying to use an abstract effect type when defining my routes but I run into errors due to some missing implicits(if I understand it correctly). Here's the code:

  trait Api[F[_]] {
    def isOk: F[Boolean]
  }

  object IOApi extends Api[IO] {
    def isOk: IO[Boolean] = IO.pure(true)
  }

  // This works fine
  val swaggerRoutesIO = new RhoRoutes[IO] {
    "Example api endpoint" **
      GET / "example" |>> {
      IOApi.isOk.flatMap {
        case true  => Ok("ok")
        case false => Forbidden("not allowed")
      }
    }
  }

  // This fails, error message in separate block below
  def swaggerRoutesF[F[_]: Sync](api: Api[F]) =
    new RhoRoutes[F] {
      "Example api endpoint" **
        GET / "example" |>> {
        api.isOk.flatMap {
//          case _ => Ok("ok") // This case works if the others are omitted
          case true  => Ok("ok")
          case false => Forbidden("not allowed")
        }
      }
    }

The error is:

Error:(41, 18) no type parameters for method flatMap: (f: Boolean => F[B])F[B] exist so that it can be applied to arguments (Boolean => F[_ >: this.Ok.T[String] with this.Forbidden.T[String] <: org.http4s.rho.Result[F,Nothing,Nothing,Nothing,Nothing,String,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,String,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing]])
 --- because ---
argument expression's type is not compatible with formal parameter type;
 found   : Boolean => F[_ >: this.Ok.T[String] with this.Forbidden.T[String] <: org.http4s.rho.Result[F,Nothing,Nothing,Nothing,Nothing,String,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,String,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing]]
    (which expands to)  Boolean => F[_ >: org.http4s.rho.Result[F,Nothing,Nothing,Nothing,Nothing,String,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing] with org.http4s.rho.Result[F,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,String,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing] <: org.http4s.rho.Result[F,Nothing,Nothing,Nothing,Nothing,String,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,String,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing]]
 required: Boolean => F[?0B]
        api.isOk.flatMap {

It looks like it can't merge the two Result type, but when I use IO I get something like this: Result[F, ...bunch of Nothing..., String, ..more Nothing..., String, ... more Nothing ...] which includes both possibilities(the generated docs also look good for the IO version, I can see the 2 different kind of response types).

I also get these two errors:

Error:(40, 25) could not find implicit value for parameter hltf: org.http4s.rho.bits.HListToFunc[cats.effect.IO,shapeless.HNil,F[B]]
        GET / "example" |>> {

Error:(40, 25) not enough arguments for method |>>: (implicit hltf: org.http4s.rho.bits.HListToFunc[cats.effect.IO,shapeless.HNil,F[B]], implicit srvc: org.http4s.rho.CompileRoutes[cats.effect.IO,R])R.
Unspecified value parameters hltf, srvc.
        GET / "example" |>> {

Which leads me to believe that I'm missing some implicit, but couldn't figure out what exactly. Here's the full source of the poc: https://gist.github.com/erdeszt/fc2b461e9b0ae70c6dd9fd7e432392e4

@erdeszt
Copy link
Contributor Author

erdeszt commented Apr 29, 2019

It might be an issue with type inference because if I widen the types manually to the correct one it compiles:

class Y[F[_]: Sync]() extends CirceEntityEncoder with SwaggerSyntax[F] {
  type YT =
    Result[F,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           String,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           String,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing,
           Nothing]
  type YTT = F[YT]
  def swaggerRoutesF(api: Api[F]) =
    new RhoRoutes[F] {
      val yo: YTT = {
        api.isOk.flatMap {
          case true  => Ok("ok").widen[YT]
          case false => Forbidden("not allowed").widen[YT]
        }
      }
      "Example api endpoint" **
        GET / "example" |>> yo
    }
}

@erdeszt
Copy link
Contributor Author

erdeszt commented Apr 29, 2019

I've played around a bit more with the explicitly typed example (YT) and got this slightly better error message:

Error:(181, 27) type mismatch;
 found   : F[this.Ok.T[String]]
    (which expands to)  F[org.http4s.rho.Result[F,Nothing,Nothing,Nothing,Nothing,String,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing]]
 required: F[Y.this.YT]
    (which expands to)  F[org.http4s.rho.Result[F,Nothing,Nothing,Nothing,Nothing,String,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,String,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing,Nothing]]
Note: this.Ok.T[String] <: Y.this.YT, but type F is invariant in type _.
You may wish to define _ as +_ instead. (SLS 4.5)
          case true  => Ok("ok")

The important part is the suggestion that I should make the argument to the effect type covariant. That fixed the example and my original code as well so it's not a bug, I'll close this issue.

@erdeszt erdeszt closed this as completed Apr 29, 2019
@erdeszt
Copy link
Contributor Author

erdeszt commented Apr 29, 2019

Follow up notes:
It worked with both responses types being String but I got diverging implicit errors(similar to #292 and the fix suggested there also fixes my issue (manual EntityEncoder instances for my response types)). Surprisingly it worked in Intellij with the Settings > Build, Execution, Deployment > Scala Compiler > Enable specialization set to true. I couldn't find much info on that feature or how can reproduce that behavior in scalac but if anyone knows please share it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant