Akka group runForeach - scala

I have an akka source that I want to group and run on batch. I am facing a problem and I am not really sure what is going wrong.
I have a source that looks something like this
val source = Source(facts.toList)
source
.grouped(config.batchSize)
.runForeach(batch => {
//Do something
})
But I am getting
[error] found : scala.concurrent.Future[akka.Done]
[error] required: scala.concurrent.Future[Unit]
[error] .runForeach(batch => {
[error]
^
Why isn't it not able to run the source?

You're not showing the larger context in which your code snippet resides, but I'm guessing your code is the last expression inside a method that is expecting a return value of Future[Unit]. The compiler is complaining because Source#runForeach returns a Future[Done] instead of a Future[Unit].
A quick fix could be to change your method's return type to Future[Done].

Related

Why is Either expected in the following for comprehension?

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

Modify via to accept method returning a Future

I am calling a method using via like below:
myRawStr(id)
.take(1)
.via(myMethod("someString", someSource)
.zip(Source.fromIterator(() => Iterator.from(1)))
.collect {
...
}
myMethod returns type Flow[ByteString, MyValidated[MyClass], NotUsed] but now it will be returning Future[Flow[ByteString, MyValidated[MyClass], NotUsed]] (Note: Future)
but doing this gives me compilation error on via. The error states:
[error] found : [as, mat, ec]scala.concurrent.Future[akka.stream.scaladsl.Flow[akka.util.ByteString,MyValidated[MyClass],akka.NotUsed]]
[error] (which expands to) [as, mat, ec]scala.concurrent.Future[akka.stream.scaladsl.Flow[akka.util.ByteString,scala.util.Either[List[ValidationError],MyClass],akka.NotUsed]]
[error] required: akka.stream.Graph[akka.stream.FlowShape[akka.util.ByteString,?],?]
[error] .via(myMethod("someString", someSource))
[error]
How can I modify this to accept a Future OR based on this question return a non future by adding another step in the flow?
via accepts a Flow as argument. You either have to change MyMethod to return a Flow instead of a Future. Or instead of using via you can use mapAsyc that maps your flow using a method that returns a Future (MyMethod):
https://doc.akka.io/docs/akka/2.5/stream/operators/Source-or-Flow/mapAsync.html

Scala: Int doesn't take parameters on a recursive call

I get a weird compilation error in the small Scala exercise I am working on.
I have this method that is supposed to keep on asking user input until a correct answer is provided. Alas I am stumbled at the first case in my pattern matching:
override def guess(guess: Int):Unit = {
val guessIndex = binary(array, guess)
guessIndex match {
case -1 => {
val nextAttempt = StdIn.readLine(s"Please be attentive $guess is outside the search range"
+" (0 to $upperBound). Try again: \n");
val a = validateType[Int](nextAttempt)
guess(a)
}
}
}
The IDE underlines guess(a) with the error "Int doesn't take parameters". Running sbt compile from the console confirms this error:
> compile
[info] Compiling 2 Scala sources to /home/vgorcinschi/Documents/eclipseProjects/Algorithms/Chapter 1 Fundamentals/algorithms1_4_34/target/scala-2.12/classes...
[error] /home/vgorcinschi/Documents/eclipseProjects/Algorithms/Chapter 1 Fundamentals/algorithms1_4_34/src/main/scala/ca/vgorcinschi/algorithms1_4_34/hotandcold/HotAndColdImpl.scala:23: Int does not take parameters
[error] guess(a)
[error] ^
[error] one error found
[error] (compile:compileIncremental) Compilation failed
[error] Total time: 0 s, completed 6-May-2017 6:47:58 PM
There are few different Stackoverflow tickets for the same error message, but they're for different scenarios. In mine here it looks like a method who takes an Int parameter is being rejected. If you could please give me a hint this would help me a lot.
Rename the guess parameter (or the method name, so it's something different) - the parameter is the first guess in scope, so the compiler thinks you're trying to call it as a function.

akka-http has mixed java and scala dsl definitions, preventing compilation

There's the following error that happens when trying to compile the line of code in the error message. Removing withStatus makes the code compile.
[error] /home/anton/code/flow-mobile/server/src/main/scala/in/flow/server/FlowServerStack.scala:108: type mismatch;
[error] found : akka.http.javadsl.model.HttpResponse
[error] required: akka.http.scaladsl.model.HttpResponse
[error] r mapEntity {_ transformDataBytes errorFlow(ermsg) } withStatus code
For some reason the function signature is this (even though it is found in the scala dsl package)
override def withStatus(statusCode: Int):
akka.http.javadsl.model.HttpResponse = copy(status = statusCode)
override def withStatus(statusCode: akka.http.javadsl.model.StatusCode):
akka.http.javadsl.model.HttpResponse = copy(status = statusCode.asInstanceOf[StatusCode])
Whats going on?
The withStatus method is probably intended as a builder pattern helper to be used with Java.
If you want to alter a HttpResponse from Scala I reckon it would be more idiomatic to use .copy(status = StatusCodes.OK).
The thing is that you are supposed to use the copy method to change statusCode, headers etc with Scala dsl's HttpResponse. Other withXYZ methods are more for the internal workings of Java api.
val originalResponse = ...
val newResponse = originalResponse.copy(status = StatusCodes.OK)
// or
val newResponse = originalResponse.copy(status = StatusCodes.NotFound)
You can look at defined StatusCodes here - http://doc.akka.io/api/akka-http/current/akka/http/scaladsl/model/StatusCodes$.html

Scala: "required: scala.concurrent.Future[?]"

Edit
Still haven't found a solution so I ended up creating two someFuture methods. One that returns a future & one that doesn't (to get otherFuture to compile)
I'm trying to return Future[Option[JsObject]] but keep getting this error:
required: scala.concurrent.Future[?]
What I'm doing
def someFuture:Future[Option[JsObject]] =
Future {
Option(JsObject())
}
def otherFuture:Future[Option[JsObject]] =
Future {
Option(JsObject(
someFuture.flatMap(_.get)
))
}
// get error here
found : JsObject
[error] required: scala.concurrent.Future[?]
How can I return the JsObject without getting an error?
The problem is that someFuture.flatMap(_.get) won't compile—you need to provide a function that takes a JsObject and returns a Future[Whatever] to use flatMap on someFuture.
You probably want something like this:
def otherFuture: Future[Option[JsObject]] = someFuture.map { opt =>
Option(JsObject(opt.get))
}
There's not really any reason to use Option if you're just going to call .get on it like this, though, so the following might be better:
def otherFuture: Future[Option[JsObject]] = someFuture.map(_.map(JsObject(_)))
Now if the future is satisfied by a non-empty option, the contents of the option will be wrapped in another layer of JsObject, which seems to be what you're aiming for?
Note that if you're using Option to represent failure, you may want to consider the failure-handling that's built into Future instead.