I'm trying to make an async call to different sub-actors, such as:
A ---> B ---> C
\---> D
Actor A sends a request message to Actor B and B send two task messages to C and D, when C and D send the results back, B merge the results up and send it back to the A.
I was trying to use ask pattern and onSuccess to solve this:
class B(fakeInterface: String, subInterface: Array[ActorRef]) extends Actor {
val subCount = subInterface.length
var finishCount = 0
def receive = {
case reqMsg(msg) =>
if (subInterface.length == 0){
sender ! DoneMessage(msg)
} else {
implicit val timeout = Timeout(5 minutes)
val composedFutures = subInterface map { x =>
(x ? DoItMessage(msg)).mapTo[DoneMessage]
}
val allResult = Future.sequence(composedFutures)
allResult.onSuccess {
case _ => sender ! DoneMessage(msg)
}
}
}
}
But the code above does not compile at all, I got three error:
[error] inferred type arguments [dummy.DoneMessage,Array] do not conform to method sequence's type parameter bounds [A,M[_] <: TraversableOnce[_]]
[error] val allResult = Future.sequence(composedFutures)
[error] ^
[error] type mismatch;
[error] found : Array[scala.concurrent.Future[dummy.DoneMessage]]
[error] required: M[scala.concurrent.Future[A]]
[error] val allResult = Future.sequence(composedFutures)
[error] ^
[error] Cannot construct a collection of type M[A] with elements of type A based on a collection of type M[scala.concurrent.Future[A]].
[error] val allResult = Future.sequence(composedFutures)
[error] ^
[error] three errors found
How can I fix this? Or is there a more proper way to solve this scenario?
Array is not a TraversableOnce. There is an implicit conversion from Array to WrappedArray which is a TraversableOnce, but by the time the type parameter has been inferred to Array it is too late for the implicit conversion. If you replace Array with one of the classes from the collections library, e.g. List you can get past these compiler errors.
The ask pattern is inefficient because it has to create a fake actor for every request, which creates considerable overhead. It is useful for allowing non-actor code to communicate with actors, but actors themselves should message each other directly. Actor B will have to be stateful so it can keep track of messages from C and D as they come back, but it would make for a better solution.
scala.Array does not inherit from scala.collection.TraversableOnce so it cannot be used with Future.sequence. Try using a List or Seq instead.
http://www.scala-lang.org/api/current/index.html#scala.Array
Related
I am playing with tagless final in scala. I use pureconfig to load the configuration and then use the configuration values to set the server port and host.
Snippet
def create[F[_]: Async] =
for {
config <- ConfigSource.default.at("shopkart").load[AppConfig]
httpApp = EndpointApp.make[F]
server <- BlazeServerBuilder[F]
.bindHttp(port = config.http.port, host = config.http.host)
.withHttpApp(httpApp)
.resource
} yield server
The compilation error is ambiguous to me. This is the compilation error.
type mismatch;
[error] found : cats.effect.kernel.Resource[F,Unit]
[error] required: scala.util.Either[?,?]
[error] server <- BlazeServerBuilder[F]
[error] ^
[error] one error found
I understand that the ConfigSource.default.at("shopkart").load[AppConfig] returns Either[ConfigReaderFailures, AppConfig]. But within the context of for-comprehension, it is an instance of AppConfig. So, why in the following line where BlazeServerbuilder an Either is expected ?
My understanding is with in the context of for-comprehension, these are two different instances. Also, I came across a similar example in scala pet store https://github.com/pauljamescleary/scala-pet-store/blob/master/src/main/scala/io/github/pauljamescleary/petstore/Server.scala#L28
How to de-sugar for to understand this error better?
The code below that you would have got if you have used flatMap/map instead of for-comprehension.
ConfigSource.default.at("shopkart").load[AppConfig] // Either[E, AppConfig]
.flatMap { config => // in flatMap you should have the same type of Monad
BlazeServerBuilder[F] // Resource[F, BlazeServerBilder[F]]
.bindHttp(port = config.http.port, host = config.http.host)
.withHttpApp(EndpointApp.make[F])
.resource
}
The cause of your error that you can't use different types of a monad in one for-comprehension block. If you need that you should convert your monads to the same type. In your case the easiest way is converting your Either to Resource[F, AppConfig]. But you have to consider using F that can understand an error type of Either, like MonadError to handle error from Either and convert it to F. After you can use Resource.eval that expects F. I see that you use Async, so you could use Async[F].fromEither(config) for that.
def create[F[_]: Async] =
for {
config <- Resource.eval(
Async[F].fromEither(ConfigSource.default.at("shopkart").load[AppConfig])
)
httpApp = EndpointApp.make[F]
server <- BlazeServerBuilder[F]
.bindHttp(port = config.http.port, host = config.http.host)
.withHttpApp(httpApp)
.resource
} yield server
In order to try and understand Scala's type system I'm attempting
to implment a custom implementation for List.foreach method :
package com
object customForEach extends App {
class customForEach[B, A] extends Iterable[A] with collection.Seq[A] {
def foreach[B](f: A ⇒ B) {
var these = this
while (!these.isEmpty) {
f(these.head)
these = these.tail
}
}
def tail = this match {
case h :: t ⇒ t
}
}
}
When I complile this code I receive errors :
[error] \Desktop\Scala\src\main\scala\typeparam.scala:16: constructor cannot be instantiated to expected type;
[error] found : scala.collection.immutable.::[B(in class ::)]
[error] required: com.customForEach.customForEach[B(in class customForEach),A]
[error] case h :: t ? t
[error] ^
[error] \Desktop\Scala\src\main\scala\typeparam.scala:16: not found: value t
[error] case h :: t ? t
[error] ^
[error] \Desktop\Scala\src\main\scala\typeparam.scala:11: type mismatch;
[error] found : Seq[A]
[error] required: com.customForEach.customForEach[B,A]
[error] these = these.tail
[error] ^
[error] three errors found
[error] (compile:compile) Compilation failed
[error] Total time: 0 s, completed 31-Jan-2015 11:53:40
In particular I find it iteresting how println can be composed with List in this fashion : List(1,2,3).foreach(println)
Do I need to add extend another trait in order to access the .tail function ?
For this error :
not found: value t
[error] case h :: t ? t
Shouldn't t be found since it is created using pattern match operator :: ?
There are many reasons why this code won't work. In order to understand the first compiler error not found: value t, you must look at the error immediately before it. :: exists solely for List, but here you do not have a List, only Iterable with Seq. That pattern match can't work, which causes t to become "not found".
There are even larger problems than that, though. Even if you remove your definition of tail (which is unnecessary), you'll then find that you're missing abstract method definitions for apply, iterator, and length from the Seq trait. I imagine you're doing this because you can't extend List, which is sealed. You can copy the implementations of apply, and length from LinearSeqOptimized, then easily implement an iterator method, but there's still another problem: your class does not have a constructor.
Okay, well we'll look at what List does again. List is abstract and has two sub-types, :: and Nil. Nil is just a case object, and :: has a constructor that accepts the head and tail of the List. This isn't going to help you very much, unless you also want to duplicate the code for :: and Nil as well.
Scala collections are very large complicated beasts, and extending them to override one method is not a simple process.
import scala.slick.driver.MySQLDriver.simple._
class RichTable[T](tag: Tag, name: String) extends Table[T](tag, name) {
case class QueryExt[B](q: Query[RichTable.this.type, B]) {
def whereEq[C](col: RichTable.this.type => Column[C], c: C) = {
q.filter { fields =>
col(fields) === c
}
}
}
}
Then it complains
[error] /home/jilen/workspace/play-slick/src/main/scala/play/slick/SlickQueryExtension.scala:10: value === is not a member of slick.driver.MySQLDriver.simple.Column[C]
[error] col(fields) === c
[error] ^
[error] /home/jilen/workspace/play-slick/src/main/scala/play/slick/SlickQueryExtension.scala:9: ambiguous implicit values:
[error] both value BooleanColumnCanBeQueryCondition in object CanBeQueryCondition of type => scala.slick.lifted.CanBeQueryCondition[scala.slick.lifted.Column[Boolean]]
[error] and value BooleanOptionColumnCanBeQueryCondition in object CanBeQueryCondition of type => scala.slick.lifted.CanBeQueryCondition[scala.slick.lifted.Column[Option[Boolean]]]
[error] match expected type scala.slick.lifted.CanBeQueryCondition[Nothing]
[error] q.filter { fields =>
[error] ^
[error] two errors found
[error] (compile:compile) Compilation failed
[error] Total time: 0 s, completed Mar 6, 2014 1:21:48 AM
There have been questions about this, but the answers did not work for 2.0
How to parametrize Scala Slick queries by WHERE clause conditions?
Slick doesn't have any information about C, so it doesn't know if it can and how it should map it to a database value and if it can use === on it. So you get a type error. You will have to use Scala's type system to restrict the type to one for which Slick knows how to map it. You can do this by providing a so-called Context Bound, in this case :BaseColumnType.
def whereEq[C:BaseColumnType](col: RichTable.this.type => Column[C], c: C) = {
q.filter { fields =>
col(fields) === c
}
}
BaseColumnType is provided by Slick and using it in this way basically tells the Scala compiler to look for an implicit value of type BaseColumnType[C] in scope, where you call whereEq. Because then it is usually known what C will actually be. Slick comes with BaseColumnType[Int], BaseColumnType[String], etc. so at the call site, the Scala compiler can find one when your C is really an Int or String in that particular call and this way pass the info further to Slick.
Same for LiuTiger's question. abstract class Crud[..., PK:BaseColumnType] should do the trick, a trait doesn't work with context bounds. When implementing an abstract DAO be prepared to face a lot of challenges and get to the edges of your Scala type system skills and learn quite a bit about type inference order, implicit parameters, etc.
Straight forward problem that I don't see what I'm doing wrong in - some type mismatch somewhere. Basically trying to set a default datatype of Long on parameters that are coming in from a web request. Here's the code:
val startTs:Long = params.getOrElse("start_ts", DateTime.yesterdayAsEpoch).toLong
val endTs:Long = params.getOrElse("end_ts", DateTime.todayAsEpoch).toLong
My DateTime helper code:
def todayAsEpoch: Long = {
val c = Calendar.getInstance(TimeZone.getTimeZone("EST"))
c.setTime(new java.util.Date())
c.set(c.get(Calendar.YEAR),c.get(Calendar.MONTH),c.get(Calendar.DAY_OF_MONTH),0,0,0)
c.getTimeInMillis / 1000L
}
def yesterdayAsEpoch: Long = {
val c = Calendar.getInstance(TimeZone.getTimeZone("EST"))
c.setTime(new java.util.Date())
c.set(c.get(Calendar.YEAR),c.get(Calendar.MONTH),c.get(Calendar.DAY_OF_MONTH),0,0,0)
((c.getTimeInMillis / 1000L) - 86400)
}
And finally, the error:
value toLong is not a member of Any
[error] val startTs:Long = params.getOrElse("start_ts", DateTime.yesterdayAsEpoch).toLong
[error] ^
[error] /vagrant/src/main/scala/com/myapp/api/controllers/FooController.scala:437: value toLong is not a member of Any
[error] val endTs:Long = params.getOrElse("end_ts", DateTime.todayAsEpoch).toLong
[error] ^
[error] two errors found
[error] (compile:compile) Compilation failed
You did not say what params is. It looks like it might be a Map[String, X] with some type X. params.getOrElse(key, someLong) will considered to have the best common supertype of X and Long which happens to be Any, according to the error message, and which has no toLong method. As your default value happens to be Long already, and so don't need to be converted, I guess there is a toLong method on X.
If it is so, then you should convert the value retrieved from params to Long (when there is such a value), before providing the default value. That would be :
params.get("key").map(_.toLong).getOrElse(defaultValue)
I'm guessing params is a Map[String, Something], and that Something isn't always a numeric type. (String?) In any case, when you call params.getOrElse, it's inferring a common type between Something and Long, and finding Any, which is why you can't call toLong on it.
I changed one function from:
def submit = Action { request =>
signupForm.bindFromRequest()(request).fold(
// Form has errors
errors => BadRequest(html.signup.form(errors)),
// We got a valid User value, display the summary
user => {
// intensive computation involving database
Ok("okay")
}
)
}
to
def submit = Action { request =>
val result = Akka.future {
signupForm.bindFromRequest()(request).fold(
// Form has errors
errors => BadRequest(html.signup.form(errors)),
// We got a valid User value, display the summary
user => {
// intensive computation involving database
Ok("okay")
}
)
}
Async {
result
}
}
and I get the compilation error of:
[error] found : play.api.mvc.SimpleResult[_ >: java.lang.String with play.api.templates.Html <: java.io.Serializable]
[error] required: play.api.mvc.SimpleResult[_1(in value result)] where type _1(in value result) >: java.lang.String with play.api.templates.Html <: java.io.Serializable
[error] Note: java.io.Serializable >: _1, but class SimpleResult is invariant in type A.
[error] You may wish to define A as -A instead. (SLS 4.5)
[error] signupForm.bindFromRequest()(request).fold(
[error] ^
[error] one error found
The error message seem like it has something to do with variance. Does anyone understand what's going on?
BadRequest is returning the type SimpleResult[Html]
Ok is returning the type SimpleResult[String]
If you make BadRequest and Ok return the same type then it would work.
Try doing Ok(Html("ok")) - or actually render a page.