Getting list head inside for yield in Scala - scala

I am trying to compile following Scala code but getting compiler error value map is not a member of ....
for {
myList: List[MyObj] = findSomeLis(List(someParam), anotherParam)
myItem <- myList.head
} yield activate(myItem )
Any suggestion?

Lift the list and get the first possible entry, as in the following examples,
for { h <- List(1,2,3).lift(0) } yield h
Some(1)
namely the list is not empty; and
for { h <- List().lift(0) } yield h
None
where the list is empty. Hence
for { h <- findSomeLis(List(someParam), anotherParam).lift(0) } yield h

Related

for-comprehension: type mismatch - required Option[?]

I am relatively new to using Scala in a project and stumbled over a problem I just don't understand.
This is my code. I annotated types for clarity:
val fcs = for {
fc: Call <- funCalls
ann: List[CapabilityArg] <- context.annotationOption(Annotations.CapabilityArguments, fc)
arg: CapabilityArg <- ann
} yield CapabilityInfo(
"CapabilityArgument", arg.id.name, new LSPRange, new LSPRange
)
which errors with:
type mismatch;
found : List[LSPServer.this.CapabilityInfo]
required: Option[?]
If if remove the last line in the for-comprehension, the error disappears. Shouldn't that line be okay, since I only map over a List[CapabilityArg]?
Thank you in advance!
The problem is that you are mixing List and Option in the for. The result collection type of a for is the collection type of the first line, which I assume is Option. The last line in the for will generate multiple values but Option can only hold one value.
This is probably what you want:
val fcs = for {
fc: Call <- funCalls
ann: List[CapabilityArg] <- context.annotationOption(Annotations.CapabilityArguments, fc)
} yield {
ann.map(arg => CapabilityInfo(
"CapabilityArgument", arg.id.name, new LSPRange, new LSPRange
))
}
This will return Option[List[CapabilityInfo]]
Thanks to #Tim I came up with this solution:
val fcs = for {
fc: Call <- funCalls : Array[Call]
ann: List[CapabilityArg] <- context.annotationOption(Annotations.CapabilityArguments, fc) : Option[List[CapabilityArg]]
} yield {
ann.toArray.map(arg =>
CapabilityInfo("CapabilityArgument", arg.id.name, new LSPRange, new LSPRange)
)
}
return fcs.flatten
I switched funCalls to type Array[Call] so I yield an Array[CapabilityInfo] that is then packed into another array. The result is then flattened to give me the desired one-dimensional array of all CapabilityInfos.
In hindsight I probably should have gone for a regular nested for-each construct?!

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]]

Why are these scalacheck recursive generators not equivalent?

While trying to learn the ScalaCheck tool, I wrote two versions of a Map generator (I know there is one of these built in, but this was an exercise).
It seems that genMap0 and genMap00 should be equivalent, and genMap00 is bit cleaner, but in fact genMap0 works, but genMap00 fails miserably.
The yield is adorned with a println that can be turned on to see what happening (just edit the speak method), but even with this information I cannot say I really understand why the difference. This makes me think that another generator I try to write may also be flawed.
Can someone give a nice explanation of what is different between genMap0 and genMap00?
import org.scalacheck._
import Arbitrary._
import Gen._
import Prop._
def speak(message: String): Unit = if (false) println(message)
lazy val genMap0: Gen[Map[Int, Int]] = for {
k <- arbitrary[Int]
v <- arbitrary[Int]
b <- arbitrary[Boolean]
m <- if (b) value(Map.empty[Int, Int]) else genMap0
} yield if (b) {
speak("false"); m
} else {
speak("true"); m.updated(k, v)
}
lazy val genMap00: Gen[Map[Int, Int]] = for {
k <- arbitrary[Int]
v <- arbitrary[Int]
m <- oneOf(Map.empty[Int, Int], genMap00)
} yield if (m.isEmpty) {
speak("empty:" + m); m
} else {
speak("not empty:" + m); m.updated(k, v)
}
val n = 5
for (i <- 1 to n; m <- genMap0.sample) println(m)
println("--------------")
for (i <- 1 to n; m <- genMap00.sample) println(m)
This is the output (genMap00 always generates the empty map):
scala -cp scalacheck_2.10-1.10.1.jar
...
// Exiting paste mode, now interpreting.
Map()
Map(1 -> 1, 1530546613 -> -1889740266, -187647534 -> 0)
Map()
Map(-1 -> 2039603804)
Map(646468221 -> 1)
--------------
Map()
Map()
Map()
Map()
Map()
The problem the recursive generation always starts with an empty map, so gen00 always ends up with a generator that produces an empty map. The problem is the empty condition is also being used to detect termination.
This is fixed by gen000:
lazy val genMap000: Gen[Map[Int, Int]] = for {
k <- arbitrary[Int]
v <- arbitrary[Int]
m <- oneOf(None, genMap000.map(g => Some(g)))
} yield (for (x <- m) yield x.updated(k, v)).getOrElse(Map())
This uses an intermediate Option[Map], with the None state indicating termination.
Using the explicit Boolean generator appears to be cleaner.

Scala Option return type

I am newbie in Scala programming world but loving it. Recently I have started porting my research App into Scala and one of thing I am still struggling is the return keyword. For example in below code
def readDocument(dbobj:MongoDBObject) = Option[ContainerMetaData]
{
for(a <- dbobj.getAs[String]("classname");
b <- dbobj.getAs[Long]("id");
c <- dbobj.getAs[Long]("version");
d <- dbobj.getAs[String]("description");
e <- dbobj.getAs[String]("name");
f <- dbobj.getAs[String]("tag");
g <- dbobj.getAs[Int]("containertype");
h <- dbobj.getAs[Date]("createddate")
)
{
val ctype = ContainerType(g)
val jodadt = new DateTime(h)
val data = new ContainerMetaData(a,b,c,d,e,f,ctype,jodadt)
Some(data)
}
None
}
In above code I get the error message:
type mismatch; found : None.type required: om.domain.ContainerMetaData
So if I remove the explicit return type the code works but then without explicit return keyword I am not able to terminate my code at Some(data).
def readDocument(dbobj:MongoDBObject)=
{
for(a <- dbobj.getAs[String]("classname");
b <- dbobj.getAs[Long]("id");
c <- dbobj.getAs[Long]("version");
d <- dbobj.getAs[String]("description");
e <- dbobj.getAs[String]("name");
f <- dbobj.getAs[String]("tag");
g <- dbobj.getAs[Int]("containertype");
h <- dbobj.getAs[Date]("createddate")
)
{
val ctype = ContainerType(g)
val jodadt = new DateTime(h)
val data = new ContainerMetaData(a,b,c,d,e,f,ctype,jodadt)
Some(data)
}
None
}
And if add a return keyword then compiler complains
method `readDocument` has return statement; needs result tye
Few more additional info, this is the trait I am extending
trait MongoDAOSerializer[T] {
def createDocument(content:T) : DBObject
def readDocument(db:MongoDBObject) : Option[T]
}
The problem is, that you are missing the yield keyword in the for-comprehension. And also the None at the end is unnecessary, as the for-comprehension will yield None, if one of the values is missing and also the explicit creation of a Some in the comprehension is not needed, as it will create an Option anyway. Your code hase to look like this (not tested)
def readDocument(dbobj: MongoDBObject): Option[ContainerMetaData] = {
for {
a <- dbobj.getAs[String]("classname")
b <- dbobj.getAs[Long]("id")
c <- dbobj.getAs[Long]("version")
d <- dbobj.getAs[String]("description")
e <- dbobj.getAs[String]("name")
f <- dbobj.getAs[String]("tag")
g <- dbobj.getAs[Int]("containertype")
h <- dbobj.getAs[Date]("createddate")
} yield {
val ctype = ContainerType(g)
val jodadt = new DateTime(h)
new ContainerMetaData(a,b,c,d,e,f,ctype,jodadt)
}
}

How can I do 'if..else' inside a for-comprehension?

I am asking a very basic question which confused me recently.
I want to write a Scala For expression to do something like the following:
for (i <- expr1) {
if (i.method) {
for (j <- i) {
if (j.method) {
doSomething()
} else {
doSomethingElseA()
}
}
} else {
doSomethingElseB()
}
}
The problem is that, in the multiple generators For expression, I don't know where can I put each for expression body.
for {i <- expr1
if(i.method) // where can I write the else logic ?
j <- i
if (j.method)
} doSomething()
How can I rewrite the code in Scala Style?
The first code you wrote is perfectly valid, so there's no need to rewrite it. Elsewhere you said you wanted to know how to do it Scala-style. There isn't really a "Scala-style", but I'll assume a more functional style and tack that.
for (i <- expr1) {
if (i.method) {
for (j <- i) {
if (j.method) {
doSomething()
} else {
doSomethingElseA()
}
}
} else {
doSomethingElseB()
}
}
The first concern is that this returns no value. All it does is side effects, which are to be avoided as well. So the first change would be like this:
val result = for (i <- expr1) yield {
if (i.method) {
for (j <- i) yield {
if (j.method) {
returnSomething()
// etc
Now, there's a big difference between
for (i <- expr1; j <- i) yield ...
and
for (i <- expr1) yield for (j <- i) yield ...
They return different things, and there are times you want the later, not the former. I'll assume you want the former, though. Now, before we proceed, let's fix the code. It is ugly, difficult to follow and uninformative. Let's refactor it by extracting methods.
def resultOrB(j) = if (j.method) returnSomething else returnSomethingElseB
def nonCResults(i) = for (j <- i) yield resultOrB(j)
def resultOrC(i) = if (i.method) nonCResults(i) else returnSomethingC
val result = for (i <- expr1) yield resultOrC(i)
It is already much cleaner, but it isn't returning quite what we expect. Let's look at the difference:
trait Element
object Unrecognized extends Element
case class Letter(c: Char) extends Element
case class Punct(c: Char) extends Element
val expr1 = "This is a silly example." split "\\b"
def wordOrPunct(j: Char) = if (j.isLetter) Letter(j.toLower) else Punct(j)
def validElements(i: String) = for (j <- i) yield wordOrPunct(j)
def classifyElements(i: String) = if (i.nonEmpty) validElements(i) else Unrecognized
val result = for (i <- expr1) yield classifyElements(i)
The type of result there is Array[AnyRef], while using multiple generators would yield Array[Element]. The easy part of the fix is this:
val result = for {
i <- expr1
element <- classifyElements(i)
} yield element
But that alone won't work, because classifyElements itself returns AnyRef, and we want it returning a collection. Now, validElements return a collection, so that is not a problem. We only need to fix the else part. Since validElements is returning an IndexedSeq, let's return that on the else part as well. The final result is:
trait Element
object Unrecognized extends Element
case class Letter(c: Char) extends Element
case class Punct(c: Char) extends Element
val expr1 = "This is a silly example." split "\\b"
def wordOrPunct(j: Char) = if (j.isLetter) Letter(j.toLower) else Punct(j)
def validElements(i: String) = for (j <- i) yield wordOrPunct(j)
def classifyElements(i: String) = if (i.nonEmpty) validElements(i) else IndexedSeq(Unrecognized)
val result = for {
i <- expr1
element <- classifyElements(i)
} yield element
That does exactly the same combination of loops and conditions as you presented, but it is much more readable and easy to change.
About Yield
I think it is important to note one thing about the problem presented. Let's simplify it:
for (i <- expr1) {
for (j <- i) {
doSomething
}
}
Now, that is implemented with foreach (see here, or other similar questions and answer). That means the code above does exactly the same thing as this code:
for {
i <- expr1
j <- i
} doSomething
Exactly the same thing. That is not true at all when one is using yield. The following expressions do not yield the same result:
for (i <- expr1) yield for (j <- i) yield j
for (i <- expr1; j <- i) yield j
The first snippet will be implemented through two map calls, while the second snippet will use one flatMap and one map.
So, it is only in the context of yield that it even makes any sense to worry about nesting for loops or using multiple generators. And, in fact, generators stands for the fact that something is being generated, which is only true of true for-comprehensions (the ones yielding something).
The part
for (j <- i) {
if (j.method) {
doSomething(j)
} else {
doSomethingElse(j)
}
}
can be rewritten as
for(j <- i; e = Either.cond(j.method, j, j)) {
e.fold(doSomething _, doSomethingElse _)
}
(of course you can use a yield instead if your do.. methods return something)
Here it is not so terrible useful, but if you have a deeper nested structure, it could...
import scalaz._; import Scalaz._
val lhs = (_ : List[X]) collect { case j if j.methodJ => doSomething(j) }
val rhs = (_ : List[X]) map doSomethingElse
lhs <-: (expr1 partition methodI) :-> rhs
You can not. The for(expr; if) construct just filter the element that must be handled in the loop.
If the order isn't important for the calls to doSomething() and doSomethingElse() then you can rearrange the code like this.
val (tmp, no1) = expr1.partition(_.method)
val (yes, no2) = tmp.partition(_.method)
yes.foreach(doSomething())
no1.foreach(doSomethingElse())
no2.foreach(doSomethingElse())
To answer your original question, I think that for comprehensions can be quite nice for specific use cases, and your example doesn't fit nicely.
The conditions specified in a Scala for operation act to filter the elements from the generators. Elements not satisfying the conditions are discarded and are not presented to the yield / code block.
What this means is that if you want to perform alternate operations based on a conditional expression, the test needs to be deferred to the yield / code block.
Also be aware that the for operation is relatively expensive to compute (currently) so perhaps a simpler iterative approach might be more appropriate, perhaps something like:
expr1 foreach {i =>
if (i.method) {
i foreach {j =>
if (j.method)
doSomething()
else
doSomethingElseA()
}
}
else
doSomethingElseB()
}
Update:
If you must use a for comprehension and you can live with some restrictions, this might work:
for (i <- expr1; j <- i) {
if (i.method) {if (j.method) doSomething() else doSomethingElseA()} else doSomethingElseB()
}