1

I'm pretty sure this can be done using reflection API.

I'm using both Casbah and spray.json for creating JSON documents in a Scala project - one towards MongoDB, the other towards a REST API.

This can be confusing, at times. I would like to detect and disallow import of the "wrong" library in a certain source file, making sure that future edits would never, ever mix the two approaches in the same code.

Probably there is no compile time mechanism for doing this (except for macros, not wanting to go so deep).

A runtime approach (e.g. placing within an object body so it gets run right up front) would be fine. I.e. how to tell spray.json namespace has been imported to this scope, in Scala?


Earlier questions: Prevent imports of unauthorized classes in Scala touches close, but is more about sandboxing (non-trusted code)

Community
  • 1
  • 1
akauppi
  • 17,018
  • 15
  • 95
  • 120
  • I don't think reflection is the right approach. Shouldn't the type system make the requirements clear? You can have a single access point for your "restification" that takes `[T: JsonFormat]` and that will require spray, and similarly for your mongo stuff you have a single "dao" that requires a Casbah type, then it doesn't matter what's imported, the correct types will be used. If you really insist on doing this I would recommend using something like Checkstyle as part of your build process; that's likely to be simpler and less confusing than reflection. – lmm Dec 01 '14 at 12:16
  • That's a great idea, @lmm . Let me rephrase: you mean imposing the "this part uses Casbah" by having a type constraint / implicit given to it via function or class prototypes, not needing imports at all. That would work. However, it still does not prevent people to accidentally get to the "wrong side" by simply typing `JsString` and letting the IDE add the import for them, automatically. This is part of the 'problem' - IDE makes it very easy to bring in unwanted dependencies, and in this case, they'll be found out as weird serialization exceptions, much later. I'd like to avoid that. – akauppi Dec 01 '14 at 12:26
  • I don't understand how you're getting "weird serialization exceptions". The only way you can serialize something with spray-json is if it has a `JsonFormat`; if there isn't one (as is the case with the Casbah types), you should be getting a compilation error. I don't know Casbah but either it already works the same way or you could make it so (by having only one place that serializes to Casbah, creating your own implicit (`CasbahFormat`?), and only having instances of that implicits for the Casbah types, not the spray-json types). – lmm Dec 01 '14 at 13:38
  • What I did experience is the opposite: Casbah choking on BigInteger that is due to feeding a JsNumber to it, by accident. Which lead me to think how to prevent that from happening for others, maintaining the code later. Just asking... I may be peeling this out the wrong way. – akauppi Dec 01 '14 at 15:56

1 Answers1

1

After the clarification of your comments, what I'd do is wrap up Casbah serialization according to which types are "safe". Have a single "gateway" method that does your Casbah serialization, and a marker interface that says which types are "safe" to send to Casbah

trait CasbahSafe[T]

object CasbahSafe {
  implicit val intsAreSafe = new CasbahSafe[Int]{}
  implicit val stringsAreSafe = new CasbahSafe[String]{}
  ...
}

def saveToMongo[T: CasbahSafe](t: T) = ...

Then saveToMongo can only be called with appropriate types; if you try to call it with a JsNumber you'll get a compilation error. For composite types you can use Shapeless automatic typeclass derivation to derive CasbahSafe for any case class where all the fields are CasbahSafe.

The overall effect is very similar to what spray-json does with JsonFormat. It should already be impossible to "make the same mistake in the opposite direction", because the presence of a JsonFormat is enforced by the compiler.

lmm
  • 17,386
  • 3
  • 26
  • 37