0

In one file, I have:

trait JsonSchema[T] {
  val propertyType: String

  override def toString: String = propertyType
}

object JsonSchema {
  implicit def stringSchema: JsonSchema[String] = new JsonSchema[String] {
    override val propertyType: String = "string"
  }

  implicit def intSchema: JsonSchema[Int] = new JsonSchema[Int] {
    override val propertyType: String = "integer"
  }

  implicit def booleanSchema: JsonSchema[Boolean] = new JsonSchema[Boolean] {
    override val propertyType: String = "boolean"
  }
}

In my main file:

case class MetaHolder[T](v: T)(implicit val meta: JsonSchema[T])

object JsonSchemaExample extends App {
  println(MetaHolder(3).meta.toString)
  println(MetaHolder("wow").meta.toString)
}

That works hunky-dory. Now suppose I do this instead:

case class MetaHolder[T](v: T) {
  val meta: JsonSchema[T] = implicitly[JsonSchema[T]]
}

It no longer compiles. Why?

My goal is to modify the anonymous Endpoint classes in the scala Finch library by adding a val meta to everything. I've been able to do this without any fancy-business so far, but now I want to do some fancy implicit resolution with shapeless to provide a JsonSchema definition for arbitrary case classes. My question is how to do this while maintaining backward compatibility. As in: provide the jsonschema meta feature for people who want to opt in, don't change the compilation burden for anyone who does not want to use meta,

If instead I go the first route, with an added implicit parameter, wouldn't that require a special import to be added by everyone? Or am I missing something and would backward compatibility still be maintained?

1
  • 2
    It is recommended to first understand how implicits work. See where does Scala looks for implicits it should be clear why you redactor didn't make sense. Also, I do not understand what do you mean with users requiring an special import. - Finally, your implicits definitions at the beginning should have been vals instead of defs. Commented Dec 18, 2019 at 12:17

2 Answers 2

4

There is big difference between implicit x: X among parameters and implicitly[X] inside body.

When you say implicitly[X] this means "check now whether in the current scope there is an implicit X".

When you say def foo(...)(implicit x: X) = ... this means "check later when foo is called that in the scope of the call site there will be an implicit X (and for now inside foo just assume without checking that there is)".

class Foo(...)(implicit x: X) is similar to the latter, "check when constructor is called that there will be an implicit X".

Regarding whether users have to import or not. If you put implicits for type X to companion object of X then they will be found automatically (implicits for type X[Y] should be put to companion object of either X or Y). If you put them somewhere else then they have to be imported to the current scope.

Sign up to request clarification or add additional context in comments.

Comments

0

In order for implicitly[JsonSchema[T]] to compile, there must be a JsonSchema[T] in the implicit scope, which means that there must be a JsonSchema[T] (or something implicitly convertible to a JsonSchema[T]) passed through as an implicit argument, as you had with:

case class MetaHolder[T](v: T)(implicit val meta: JsonSchema[T])

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.