Is there any difference between calls in main thread and unsafeRunSync? - scala

Is there any difference between just
call1()
call2()
and
val res = for {
_ <- IO(call1())
_ <- IO(call2())
...
} yield somevalue
res.unsafeRunSync
What variant is safer (and is there any difference at all)?

Related

Difference between parSequence and starting fibers manually

I have got two program's implementatnion
def program_valid: IO[Unit] = for {
interpreter <- Interpreter[IO]
fib1 <- display(interpreter).start
fib2 <- read(interpreter).start
_ <- fib1.join
_ <- fib2.join
} yield ()
def program_invalid: IO[Unit] = for {
interpreter <- Interpreter[IO]
_ <- (read(interpreter), display(interpreter)).parSequence
} yield ()
First one works perfectly well which means both fibers keep running (dispaly and read). Unfortunately second implementatnion works different. It looks like only the display fiber would run. Why it goes like that? What is the difference here?
parSequence is for collections (well actually for types with an instance of Traverse), not sure how it even compiles; well I mean there is probably a Traverse instance for Tuple2 but it definitively doesn't do what you want.
You may use:
_ <- (read(interpreter), display(interpreter)).parTupled.void
// Or
_ <- IO.both(read(interpreter), display(interpreter)).void
// Or
_ <- read(interpreter).both(display(interpreter)).void
// Or
_ <- List(read(interpreter), display(interpreter)).parSequence_

List to multiple anonymous/underscore parameters in for-comprehension

I'm kind of new to Scala/functional so I'm not yet able to use technical language.
I'm experiencing problems with a for-comprehension
val queries =
for {
_ <- createBanco
_ <- createBancoMedio
bankInsertions <- Update[Banco](insertStr).updateMany(NonEmptyList.fromList(createBankList(1, maxBanks)).get)
mediumInsertions <- Update[BancoMedio](mediumInsert).updateMany(NonEmptyList.fromList(mediumList).get)
bankCount <- BancoStatements.getCount().unique
bankGetIds <- BancoStatements.getIds(0, maxBanks).to[List]
bankSome <- BancoStatements.getSome(halfBanks).to[List]
} yield (bankCount, bankGetIds, bankSome)
//Execute database queries, saves them on tuple
val transactionResults : (Int, List[String], List[Banco]) =
queries.transact(h2Transactor).unsafeRunSync()
I'm trying to refactor the _ <- createBanco & _ <- createBancoMedio, which are both a ConnectionIO[Int] object.
Id like to convert those to a single List(createBanco, createBancoMedio) and then execute transact.
However, i'd be altering the return type of the for-comprehension by doing that. I'd like to know if there is any way on doing that without affecting the for output value
Basically, treat the list as if I was writing multiple anonymous parameters manually.
You can use .sequence to turn a List[G[A]] into a G[List[A]] if G has an Applicative instance, which ConnectionIO does:
val queries =
for {
_ <- List(createBanco, createBancoMedio).sequence
...
Just solved it, did another for comprehension for the List
val createList = for {
m <- createBancoMedio
b <- createBanco
} yield List(b, m)
val queries =
for {
_ <- createList ....
This way i had a ConnectionIO[List[Int]]

Conditionally running Slick statements in a for comprehension

Given the following Slick code:
val doUpdate = true
val table1 = TableQuery[Table1DB]
val table2 = TableQuery[Table2DB]
val table3 = TableQuery[Table3DB]
val action = (for {
_ <- table1.filter(_.id === id).update(table1Record)
_ <- table2.filter(_.id === id).update(table2Record)
_ <- table3.filter(_.id === id).update(table3Record)
} yield ()).transactionally
What I need is to update table2 and table3 ONLY if the variable doUpdate is true. This is my attempt:
val action = (for {
_ <- table1.filter(_.id === id).update(table1Record)
_ <- table2.filter(_.id === id).update(table2Record)
if (doUpdate == true)
_ <- table3.filter(_.id === id).update(table3Record)
if (doUpdate == true)
} yield ()).transactionally
This compiles, but when I run it I get the following error Action.withFilter failed. How to add the condition?
As you notices if you use if inside a for it will call withFilter underneath. In case of pipelines (DBIO, Futures, Tasks) that filtering boils down to whether or not continue with a pipeline (happy path) or fail it (generating Failure or similar).
If all you want is to update, then you can go with the if road and recover the Future. However, it would we ugly, since depending on how you write the case it might also recover from errors you would like to be informed about.
Alternative is to simply use if-else to provide a different DBIOAction:
(for {
_ <- {
if (doUpdate) conditionalAction.map(_ => ())
else DBIOAction.successful(())
}
} yield ()).transactionally
In your case it could look something like:
val action = (for {
_ <- table1.filter(_.id === id).update(table1Record)
_ <- {
if (doUpdate) DBIOAction.seq(
table2.filter(_.id === id).update(table2Record),
table3.filter(_.id === id).update(table3Record)
)
else DBIOAction.successful(())
}
} yield ()).transactionally

Scala syntax problems in for comprehension statement

Is it possible to make an if statement in a for block?
I have the following:
val process = for {
stepIds <- processTemplateDTO.getProcessStepTemplateIds(processTemplateId)
allApprovedProcessTemplates <- processTemplateDTO.getApprovedProcessTemplates(clientId) //Get all approved process templates
processTemplate <- processTemplateDTO.getProcessTemplate(processTemplateId, clientId) // Get the Process Template
prerequisites <- getProcessTemplateForEdit(processPrerequisitesDTO.getProcessPrerequisiteProcessTemplateIdsByProcessTemplateId(processTemplateId), clientId)
postConditions <- getProcessTemplateForEdit(processPostConditionsDTO.getProcessPostConditionProcessTemplateIdsByProcessTemplateId(processTemplateId), clientId)
approvedProcessTemplate <- processTemplateDTO.getProcessTemplate(processTemplate.get.approveprocess, clientId)
if (processTemplate.get.trainingsprocess.isDefined) {
trainedProcessTemplate <- processTemplateDTO.getProcessTemplate(processTemplate.get.trainingsprocess.get, clientId)
}
And I only want to call the processTemplateDTO.getProcessTemplate if processTemplate.get.trainingsprocess.isDefined is true
is this possible?
Thanks
also tried this way:
trainedProcessTemplate <- {
if (processTemplate.get.trainingsprocess.isDefined) {
processTemplateDTO.getProcessTemplate(processTemplate.get.trainingsprocess.get, clientId)
} else {
None
}
}
UPDATE
trainedProcessTemplate <- Nil
_ = if (processTemplate.get.trainingsprocess.get != null) {
processTemplateDTO.getProcessTemplate(processTemplate.get.trainingsprocess.get, clientId)
}
Yes, It is possible.
You can see the below code for reference -
val list = List(1,2,3,4)
val result = for {
id <- list
_ = if (id < 2) {
println(s"Hello I am $id")
}
} yield id
Yes it is possible to use if block in for block, there are two ways
for {
a <- somework1 // returns boolean
b <- somework2 if(a)
} yield (a, b) //anything you like
or you can try
for {
a <- somework1 // returns boolean
b <- if(a) somework2 else somework3
} yield (a, b) //anything you like
In 2nd way you have to give else block as well because without that it will return Any
Hope this helps!

How to improve the code of a method which uses 'Free monad'?

I'm trying some code which inspects this slides about Free Monad in Scala, and made a small project with some slightly changed code.
The project is here: https://github.com/freewind/free-the-monads
Everything seems good at first, the code is clean and beautiful:
def insertAndGet() = for {
_ <- Script.insert("name", "Freewind")
value <- Script.get("name")
} yield value
def insertAndDelete() = for {
_ <- Script.insert("name", "Freewind")
_ <- Script.delete("name")
value <- Script.get("name")
} yield value
def insertAndUpdateAndDelete() = for {
_ <- Script.insert("name", "Freewind1")
oriValue <- Script.update("name", "Freewind2")
_ <- Script.delete("name")
finalValue <- Script.get("name")
} yield (oriValue, finalValue)
But when my logic is complex, e.g. there is some Script[Option[_]], and I need to check the option value to decide to do something, I can't use the for-comprehension any more, the code is like:
private def isLongName(name: String): Script[Boolean] = for {
size <- Script.getLongNameConfig
} yield size.exists(name.length > _)
def upcaseLongName(key: String): Script[Option[String]] = {
Script.get(key) flatMap {
case Some(n) => for {
isLong <- isLongName(n)
} yield isLong match {
case true => Some(n.toUpperCase)
case false => Some(n)
}
case _ => Script.pure(None)
}
}
I found the Free Monad approach is really interesting and cool, but I'm not familiar with scalaz, and just beginning to learn Monad things, not sure how to improve it.
Is there any way to make it better?
PS: You can just clone the project https://github.com/freewind/free-the-monads and try it yourself
This is a good use case for the OptionT monad transformer:
import scalaz.OptionT, scalaz.syntax.monad._
def upcaseLongName(key: String): OptionT[Script, String] = for {
n <- OptionT.optionT(Script.get(key))
isLong <- isLongName(n).liftM[OptionT]
} yield if (isLong) n.toUpperCase else n
Here OptionT.optionT converts a Script[Option[String]] into an OptionT[Script, String], and .liftM[OptionT] raises a Script[Boolean] into the same monad.
Now instead of this:
println(upcaseLongName("name1").runWith(interpreter))
You'd write this:
println(upcaseLongName("name1").run.runWith(interpreter))
You could also have upcaseLongName return an Script[Option[String]] directly by calling run there, but if there's any chance you'll need to be composing it with other option-y script-y things it's probably best to have it return OptionT[Script, String].