I've a trait:
trait Crawler {
implicit def system: ActorSystem
implicit def executionContext: ExecutionContext
implicit def materializer: Materializer
// other methods
}
And a test class:
class CrawlerSpec extends AsyncFlatSpec with Matchers with Crawler {
override implicit val system: ActorSystem = ActorSystem("ufo-sightings")
override implicit val executionContext: ExecutionContext = implicitly[ExecutionContext]
override implicit val materializer: Materializer = ActorMaterializer()
// test
}
According to Scalatest doc:
Asynchronous style traits extend AsyncTestSuite, which provides an
implicit scala.concurrent.ExecutionContext named executionContext.
But the test blows up with a NPE due to the ExecutionContext being null (should fail gracefully, but that's another matter).
java.lang.NullPointerException was thrown.
java.lang.NullPointerException
at scala.concurrent.impl.Future$.apply(Future.scala:31)
at scala.concurrent.Future$.apply(Future.scala:494)
Why isn't the implicit ExecutionContext picked up?
<rant>
Implicit resolution is a nightmare. At the expense of saving a few
keystrokes, it makes code so fragile that removal of a single
import breaks it. There's a reason other statically typed languages like
Haskell or Kotlin don't have it; it's a stinking mess.
</rant>
Let's see what happens here:
trait A {
implicit def executionContext: ExecutionContext
}
you declare here that A would provide implicit value. Then
class B extends A {
override implicit val executionContext: ExecutionContext = implicitly[ExecutionContext]
}
So what does happen here?
value executionContext is initialized during B construction.
then implicitly tries to find a value with ExecutionContext type.
it finds such value: executionContext.
So effectively you did something like:
class B extends A {
val executionContext: ExecutionContext = executionContext
}
You created circular dependency on initialization: you are initializing value with itself. So you take a value though a "getter" returning a property that is still null (as it is just being initialized).
I agree, that implicits are concepts are something that requires a lot of effort, though I would not antagonize them as much as you. Here you had a problem with circular dependency on initialization. It cannot fail gracefully, anything other than Exception would put the program into invalid state.
Solution would be initializing you implicit value without the usage of implicitly. Just put some value there by hand. Or don't use that trait and import implicit from somewhere else.
Related
I have a case class:
case Context(tracker: Tracker)
I have a processor class with a get def, that expects an implicit parameter of Tracker defined as:
class Processor(){
def get(id: String)(implicit tracker: Tracker): Unit
}
and I have my calling class:
class repo(process: Processor){
def get(id: String)(implicit ctx : Context): Unit{
process.get(id)
}
}
Is there an easy way for my to map from context -> Tracker? I've been trying to use an implicit def in the companion of 'repo' but am still seeing 'no implicit val available' for the Tracker when calling process.get(id)
You can define implicit in the scope
implicit def ifContextThenTracker(implicit c: Context): Tracker = c.tracker
I've been trying to use an implicit def in the companion of 'repo' but am still seeing 'no implicit val available' for the Tracker when calling process.get(id)
Please see rules of implicit resolution in Where does Scala look for implicits?
You can put ifContextThenTracker into companion object of Tracker if you have access to it. Otherwise you can import implicit.
I am observing a strange behavior. In on of my test cases, I am using contentAsJson. In that test case, the compiler is not complaining that I have to provide an implicit value for Timeout and Materializer
class UserControllerUnitSpec extends PlaySpec with BeforeAndAfterAll with BeforeAndAfterEach with OneAppPerSuiteWithComponents{
..
"User signup request with body but with incorrect profile data " should {
"return error message " in {
...val resultFuture: Future[Result] = testEnv.controller.signupUser(request)
val responseBodyAsJsValue: JsValue = contentAsJson(resultFuture)//works
...
}
}
But in another test case, the compiler gives error that I need to provide the value
class QuestionsControllerUnitSpec extends PlaySpec with BeforeAndAfterAll with BeforeAndAfterEach with OneAppPerSuiteWithComponents{
...
"newQuestion" should {
"should return error if the size of the body in the request is more than the maximum allowed size" in {
...
val response:Accumulator[ByteString,Result] = questionController.newQuestion(request)
val responseBody = contentAsJson(response)//(Timeout(Duration(5000,"millis")),testEnv.testEnv.mat).
...
}
I get error
Error:(1485, 39) could not find implicit value for parameter mat: akka.stream.Materializer
val responseBody = contentAsJson(response)//(Timeout(Duration(5000,"millis")),testEnv.testEnv.mat)
How can I debug why one is working but the other isn't?
UPDATE - added return types after Mario's answer.
Try providing implicit Materializer like so
import play.api.test.Helpers._
implicit val actorSystem = ActorSystem("test")
implicit val materializer = ActorMaterializer()
val responseBody = contentAsJson(response)
instead of explicit testEnv.testEnv.mat
contentAsJson(response)(Timeout(Duration(5000,"millis")),testEnv.testEnv.mat)
Regarding the difference between the two tests, note there are two overloaded versions of contentAsJson
def contentAsJson(of: Future[Result])(implicit timeout: Timeout, mat: Materializer = NoMaterializer): JsValue
def contentAsJson(of: Accumulator[ByteString, Result])(implicit timeout: Timeout, mat: Materializer): JsValue
where in the first case we see default Materializer argument is provided
mat: Materializer = NoMaterializer
while in the second case we have to provided our own. Therefore it is likely that in the first test the type of resultFuture is Future[Result] whilst in the second test the return type of response is Accumulator.
Regarding finding out where the implicit is provided from, personally I use IntelliJ's View | Show Implicit Hints feature.
I'm trying to write a nice generic persistence service with one implementation for mongo using Reactive Mongo and I'm struggling with providing the implicit reader/writer for my record class children. Here's the code.
First the base record class (each persisted record must implement it.
abstract class Record {
val _id: BSONObjectID = BSONObjectID.generate()
}
Second one record child case class (very simple) with its writer/reader (both possible way to do it, Macros vs custom in comment)
case class TestRecord() extends Record {}
object TestRecord {
// implicit object TestRecordWriter extends BSONDocumentWriter[TestRecord] {
// def write(testRecord: TestRecord): BSONDocument = BSONDocument()
// }
//
// implicit object TestRecordReader extends BSONDocumentReader[TestRecord] {
// def read(doc: BSONDocument): TestRecord = TestRecord()
// }
implicit def reader = Macros.reader[TestRecord]
implicit def writer = Macros.writer[TestRecord]
}
And then the service itself
class MongoPersistenceService[R <: Record] #Inject()()
(implicit ec: ExecutionContext, tag: ClassTag[R]) {
val collectionName: String = tag.runtimeClass.getSimpleName
def db: Future[DefaultDB] = MongoConnectionWrapper.getMongoConnection("mongodb://127.0.0.1", "27017")
.flatMap(_.database("testDb"))
def collection: Future[BSONCollection] = db.map(_.collection(collectionName))
def persist(record: R): Future[Unit] = {
collection.flatMap(_.insert(record)).map(_ => {})
}
def read(id: BSONObjectID): Future[R] = {
for {
coll <- collection
record <- coll.find(BSONDocument("_id" -> id)).one[R].mapTo[R]
} yield record
}
}
And here's my failing test:
import scala.concurrent.ExecutionContext.Implicits.global
class MongoPersistenceServiceSpec extends WordSpec with Matchers with BeforeAndAfter {
val persistenceService = new MongoPersistenceService[TestRecord]()
"persist" when {
"called" should {
"succeeds when passing a not persisted record" in {
val testRecord = TestRecord()
persistenceService.persist(testRecord)
val persistedRecord = Await.result(persistenceService.read(testRecord._id), Duration(1000, "millis"))
assert(persistedRecord.eq(testRecord))
}
}
}
}
Compiler complains with following messages:
Error:(33, 32) could not find implicit value for parameter writer: reactivemongo.bson.BSONDocumentWriter[R]
collection.flatMap(_.insert(record)).map(_ => {})
Error:(33, 32) not enough arguments for method insert: (implicit writer: reactivemongo.bson.BSONDocumentWriter[R], implicit ec: scala.concurrent.ExecutionContext)scala.concurrent.Future[reactivemongo.api.commands.WriteResult].
Unspecified value parameters writer, ec.
collection.flatMap(_.insert(record)).map(_ => {})
Error:(39, 57) could not find implicit value for parameter reader: reactivemongo.bson.BSONDocumentReader[R]
record <- coll.find(BSONDocument("_id" -> id)).one[R].mapTo[R]
Error:(39, 57) not enough arguments for method one: (implicit reader: reactivemongo.bson.BSONDocumentReader[R], implicit ec: scala.concurrent.ExecutionContext)scala.concurrent.Future[Option[R]].
Unspecified value parameters reader, ec.
record <- coll.find(BSONDocument("_id" -> id)).one[R].mapTo[R]
Anybody has a idea what I might be missing?
I'm still new to scala so help me find the issue in here.
I already tried writing custom BSONWriter/BSONReader instead of the BSONDocumentWriter/BSONDocumentReader provided here without success.
In short, Scala doesn't search everywhere throughout your code for implicits. They need to be brought into scope, either through imports, passing them as parameters, or, as I would suggest here, through a context bound:
class MongoPersistenceService[R <: Record : BSONDocumentReader : BSONDocumentWriter]
This is a kind of short hand for (a) requiring that an implicit BSONDocumentReader[R] (and writer) can be found at the time that the class is constructed with a specific R, and (b) bringing those implicits into scope within the class's implementation so they can be implicitly passed to other methods like insert.
In order to fulfill that new requirement (a) you may have to import TestRecord._ within your test.
Your persist function doesn't have access to the fact that you have defined those implicit functions. The signature should be something like this:
def persist(record: R)(implicit def writer: BSONDocumentWriter[R]): Future[Unit]
And wherever you call persist and pass a TestRecord make sure that the implicit functions are in scope.
How to define a wrapper function/class addActorToSystem() for
trait Stage extends Actor
class Stage1(i:Int) extends Stage
class Stage2(i:Int) extends Stage
and
implicit val system = ActorSystem("mySystem")
instead of the call to
system.actorOf(Props(new Stage1(123)), "myStage1")
The following does not work
You cannot create an instance of [Stage2] explicitly using the constructor (new)
def addActorToSystem(act: Stage, i: Int)(name: String)(implicit sys: ActorSystem) = {
sys.actorOf(Props(act(i)), name)
}
Maybe something like this would help you:
def addActorToSystem[T <: Stage](act: Class[T], i: Int)(name: String)(implicit sys: ActorSystem) = {
sys.actorOf(Props(act, i), name)
}
And usage like follows:
val s1 = addActorToSystem(classOf[Stage1], 1)("s1")
val s2 = addActorToSystem(classOf[Stage2], 2)("s2")
Creating actor without ActorContext defined (by using constructor) is not allowed.
The only one reason why this works in Props is the fact that constructor invocation is handled as by-name parameter thus its evaluation is deffered
I am learning Scala and Akka and I came across an example that confused me. The following code sample was in 1 file.
class RestApi(system: ActorSystem, timeout: Timeout) extends RestRoutes {
implicit val requestTimeout = timeout
implicit def executionContext = system.dispatcher
def createBoxOffice = system.actorOf(BoxOffice.props, BoxOffice.name)
}
trait BoxOfficeApi {
import BoxOffice._
def createBoxOffice(): ActorRef
implicit def executionContext: ExecutionContext
implicit def requestTimeout: Timeout
lazy val boxOffice = createBoxOffice()
// Uninteresting methods here
}
The part that confuses me is the createBoxOffice() call in the BoxOfficeAPI trait. It clearly states that the BoxOfficeAPI has a function which will return an ActorRef. However the implementation for that function is located in the RestApi class which doesn't have any reference to the trait.
How is it possible that the trait can access this method? What is the scope of a trait?
How is it possible that the trait can access this method?
I think you're missing the fact that the trait itself has an abstract method called createBoxOffice:
def createBoxOffice(): ActorRef
Which is why it has such a method in scope. It has nothing to do with the declaration of RestApi. Whoever mixes in this trait will have to implement that said method. That means, for example, that if the RestApi class decides to mix in BoxOfficeApi, it already has an implementation available which it will use. For example:
class Baz extends Foo {
override def quuux(): Unit = println("quuux")
}
trait Foo {
def quuux(): Unit
}