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

Auto should not use implicit conversion functions #45

Open
ncreep opened this issue Jun 4, 2017 · 2 comments
Open

Auto should not use implicit conversion functions #45

ncreep opened this issue Jun 4, 2017 · 2 comments

Comments

@ncreep
Copy link
Contributor

ncreep commented Jun 4, 2017

Hi,

Currently io.fintrospect.formats.Auto uses implicit functions in various methods. E.g.:

def Out[OUT](svc: Service[Request, OUT], successStatus: Status = Status.Ok)
            (implicit transform: OUT => R): Service[Request, Response]

This can be problematic, for example in cases where one is trying to serialize raw JSON (e.g., Play's JsValue). Since in this case OUT =:= R , and Fintrospect's built-in implicit conversion competes with the standard library's <:< (which extends Function1).

The workaround for this particular problem is to pass the implicit argument explicitly. But a more general solution would be to introduce a dedicated type for the conversions that take place in Auto. This will avoid polluting the implicit scope with a common type and thus won't compete with the standard library (or anything else for that matter).

@daviddenton
Copy link
Owner

Thanks for the suggestion. Have you got a quick Gist to demonstrate the problem in action, or even better something to show the kind of solution that you were thinking of?

@ncreep
Copy link
Contributor Author

ncreep commented Jun 5, 2017

I can try both...

I'll use Play for the example, but it's problematic for all other formats as well.
Given this code:

import io.fintrospect.formats.Play.Auto._

val js: JsValue = ???
Out {
  Service.mk { _: Request =>
    Future(js)
  }
}

You get this compilation error:

Error:(99, 9) ambiguous implicit values:
 both method $conforms in object Predef of type [A]=> <:<[A,A]
 and method tToJsValue in object Auto of type [T](implicit db: play.api.libs.json.Writes[T])T => play.api.libs.json.JsValue
 match expected type play.api.libs.json.JsValue => play.api.libs.json.JsValue
Error occurred in an application involving default arguments.
    Out {

My proposed solution would be to introduce a new function-like type:

trait ToResponse[-A, +B] {
  def apply(a: A): B
}

object ToResponse {
  def apply[A, B](f: A => B): ToResponse[A, B] = new ToResponse {
    def apply(a: A): B = f(a)
  } 
}

(you might consider extending Function1 which will enable maping ToResponse over things, but that adds some implicit-scope pollution)

Now in the Auto type you rewrite the methods to take implicit ToResponse instances, e.g.:

def Out[OUT](svc: Service[Request, OUT], successStatus: Status = Status.Ok)
            (implicit transform: ToResponse[OUT, R]): Service[Request, Response]

And the relevant implicit functions should be wrapped accordingly, for example in io.fintrospect.formats.Play:

implicit def tToJsValue[T](implicit db: Writes[T]): ToResponse[T, JsValue] = 
  ToResponse { (t: T) => 
    JsonFormat.encode[T](t)
  }

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

2 participants