I can collect the results at the inner-most for body into a List[Output] and return them. But I want to use yield. How can this method be converted into using for-yield pattern:
def useForComprehension(input : Input): List[Output] = {
for (o <- splitInputIntoPieces(input)) {
for (restResults <- useForComprehension(subtract(input, o)) ) {
for (w <- f3(o)) {
yield w::restResults // !!!!! Error
}
}
}
}
In Scala, nested iteration is handled by adding additional <- clauses.
For example, let's say we have two lists l1 and l2 and we want generate over every pair of elements (x,y) where x is in l1 and y is in l2. The syntax in Scala is:
for { x <- l1
y <- l2
} yield (x,y)
When no yield keyword follows the for then the entire expression results in a type of Unit, which is the source of your type error. This is useful for performing side effects on the iteration, for example
for { x <- l1
y <- l2
} println((x,y))
For more information on for comprehensions see What is Scala's yield?
Your error may be because you are surrounding yield in {}.
for {stuff} {yield otherstuff}
The form should be:
for {stuff} yield otherstuff
You can of course replace "otherstuff" with a block if you want it to contain multiple expressions so you have:
for {stuff} yield {otherstuff}
Using your example I suspect you want something like:
def useForComprehension(input: Input): List[Output] =
for {
o <- splitInputIntoPieces(input)
restResults <- useForComprehension(subtract(input, o))
w <- f3(o)
} yield w :: restResults
Related
Sorry if this is a stupid question as I am a total beginner. I have a function factors which looks like this:
def factors (n:Int):List[Int] = {
var xs = List[Int]()
for(i <- 2 to (n-1)) {
if(n%i==0) {xs :+ i}
}
return xs
}
However if I do println(factors(10)) I always get List().
What am I doing wrong?
The :+ operation returns a new List, you never assign it to xs.
def factors (n:Int):List[Int] = {
var xs = List[Int]()
for (i <- 2 to (n - 1)) {
if(n%i==0) {xs = xs :+ i}
}
return xs
}
But, you really shouldn't be using var. We don't like them very much in Scala.
Also don't don't don't use return in Scala. It is a much more loaded keyword than you might think. Read about it here
Here is a better way of doing this.
def factors (n:Int): List[Int] =
for {
i <- (2 to (n - 1)).toList
if (n % i) == 0
} yield i
factors(10)
You don't need .toList either but didn't want to mess with your return types. You are welcome to adjust
Working link: https://scastie.scala-lang.org/haGESfhKRxqDdDIpaHXfpw
You can think of this problem as a filtering operation. You start with all the possible factors and you keep the ones where the remainder when dividing the input by that number is 0. The operation that does this in Scala is filter, which keeps values where a particular test is true and removes the others:
def factors(n: Int): List[Int] =
(2 until n).filter(n % _ == 0).toList
To keep the code short I have also used the short form of a function where _ stands for the argument to the function, so n % _ means n divided by the current number that is being tested.
I am using ZIO: https://github.com/zio/zio
in my build.sbt:
"dev.zio" %% "zio" % "1.0.0-RC9"
No matter what I tried, my results are always being computed each time I need them:
val t = Task {
println(s"Compute")
12
}
val r = unsafeRun(for {
tt1 <- t
tt2 <- t
} yield {
tt1 + tt2
})
println(r)
For this example, the log look like :
Compute
Compute
24
I tried with Promise:
val p = for {
p <- Promise.make[Nothing, Int]
_ <- p.succeed {
println(s"Compute - P")
48
}
r <- p.await
} yield {
r
}
val r = unsafeRun(for {
tt1 <- p
tt2 <- p
} yield {
tt1 + tt2
})
And I get the same issue:
Compute - P
Compute - P
96
I tried with
val p = for {
p <- Promise.make[Nothing, Int]
_ <- p.succeed(48)
r <- p.await
} yield {
println(s"Compute - P")
r
}
first and I was thinking that maybe the pipeline is executed but not the value recomputed but I does not work either.
I would like to be able to compute asynchronously my values and be able to reuse them.
I looked at How do I make a Scalaz ZIO lazy? but it does not work for me either.
ZIO has memoize, which should do essentially what you want. I don't have a way to test it just now, but it should work something like:
for {
memoized <- t.memoize
tt1 <- memoized
tt2 <- memoized
} yield tt1 + tt2
Note that unless the second and third lines of your real code have some branching that might result in the Task never getting called, or getting called only once, this yields the same answer and side effects as the much simpler:
t flatMap {tt => tt + tt}
Does computing the results have side effects? If it doesn't you can just use a regular old lazy val, perhaps lifted into ZIO.
lazy val results = computeResults()
val resultsIO = ZIO.succeedLazy(results)
If it does have side effects, you can't really cache the results because that wouldn't be referentially transparent, which is the whole point of ZIO.
What you'll probably have to do is flatMap on your compute Task and write the rest of your program which needs the result of that computation inside that call to flatMap, threading the result value as a parameter through your function calls where necessary.
val compute = Task {
println(s"Compute")
12
}
compute.flatMap { result =>
// the rest of your program
}
I have two lists one is:
val l1 = List[Either[A,B]]
and
val l2 = List[String]
Now, all I need to do is map these two. i.e., if l1 is A then return corresponding value from l2. Tried something like:
for {
elem1 <- l1
elem2 <- l2
result <- if(elem1.isLeft) url
} yield result
This doesn't work. Because, I am not handling the else case. Similarly with match instead of if. How do I go about to achieve this?
You could do something like this (I'm assuming l2 has at least the same number of elements of type A as Lefts in l1):
val result: List[String] = l1.zip(l2).filter(_._1.isLeft).map(_._2)
Otherwise, if you prefer using for, this will also do the trick:
scala> for {
| e1 <- l1.zip(l2)
| if e1._1.isLeft
| } yield e1._2
I'm new to Scala so please bear with me.
I'm confused about the behaviors below:
val l = List(Option(1))
for (i <- l; x <- i) yield x //Example 1: gives me List(1)
for(x <- Option(1)) yield x //Example 2: gives me Some(1)
Why doesn't the second for comprehension give me 1 instead? Because that would look more consistent to me, intuitively, since the second for comprehension in the first example x <- i looks like it should behave exactly the same way as the second example, as the second example basically has extracted the option out of the list to begin with.
Simply put, for comprehension wraps into the type that was used the first time.
for (x <- Option(1)) yield x // Returns Option
for (x <- List(1)) yield x // Returns List
for (x <- Array(1)) yield x // Returns Array
This:
for (i <- List(Some(1)); x <- i) yield x
Desugares into this:
List(Some(1)).flatMap { case i => i.map { case x => x } }
flatMap of List returns List[T], that's why it behaves like that
This is the code from scalaz document, so basically, if the value is left value, it will take this left value as the final result and stop evaluating the rest, which is very useful.
for {
e1 <- "event 1 ok".right
e2 <- "event 2 failed!".left[String]
e3 <- "event 3 failed!".left[String]
} yield (e1 |+| e2 |+| e3) // event 2 failed
However, I have this code and I am using reduceLeft to append the value
object processor {
def evaluate(x: Int): \/[String, Int] = {
if (x <= 3) x.right[String] else ("some exception about " + x).left[Int]
}
}
val result = (1 to 6).map(processor.evaluate).reduceLeft(_ |+| _)
//\/-(-\/(some exception about 4some exception about 5some exception about 6))
The left value is accumulated, which is not what I want. I guess the different behaviour caused by reduceLeft is "left" value is already evaluated while for operation (flatMap and map) would not.
How should I change this code to take the left result as the final result
You can use traverse syntax
val result = (1 to 6).toList.traverseU(processor.evaluate)
I convert range to List to get List type class in scope
I think there may be some confusion about what is exactly going on in the following code
for {
e1 <- "event 1 ok".right
e2 <- "event 2 failed!".left[String]
e3 <- "event 3 failed!".left[String]
} yield (e1 |+| e2 |+| e3) // event 2 failed
In the code above, the for comprehension is using map and flatMap for \/[+A, +B]. The function defined by the yield statement will never be evaluated because of the implementation of map/flatMap for \/[+A, +B]. In this case it is map/flatMap which is doing the merging of the different \/[String, _]. The |+| operator used by the function in yield, is defined in SemiGroup syntax is simply using Semigroup[String] to combine the strings on the right side into one string. In the case above, it may as well be using String.append. I get why one might want to use Semigroup[String] to combine these opposed to simply String.append here, but the important part is that the function defined by yield is using Semigroup[String] and not something like Semigroup[A \/ B].
In the case below, you are using Semigroup[A \/ B] to combine the String \/ Int instances into a single String \/ Int. reduceLeft (or foldLeft if you chose that route instead) is simply doing the wiring of each element in the list to the accumulation function it is passed.
object processor {
def evaluate(x: Int): \/[String, Int] = {
if (x <= 3) x.right[String] else ("some exception about " + x).left[Int]
}
}
val result: String \/ Int = (1 to 6).map(processor.evaluate).reduceLeft(_ |+| _)
From the definition of Semigroup[A \/ B], we can see that it requires Semigroup[A] and Semigroup[B].
If you instead used Monad[A \/ _] or Applicative[A \/ _] to combine the A \/ B in the function passed to reduceLeft, the As would not be combined.
The following uses Applicative[A \/ _]
val result: String \/ Int = (1 to 6).map(processor.evaluate).reduceLeft {
(xs, x) => (xs |#| x)(_ |+| _)
}
The following uses map/flatMap defined for A \/ B and is most similar to the code at the top.
val result: String \/ Int = (1 to 6).map(processor.evaluate).reduceLeft {
(xs, x) => for {
xsA <- xs
xA <- x
} yield xsA |+| xA
}
foldMapM might do what you want, but uses Foldable[List].foldsRight so your error will be different than if you used foldLeft. Unfortunately, an ugly type lambda, or the type alias below is needed.
type StringEither[B]=String \/ B
val result: String \/ Int = (1 to 6).toList.foldMapM[StringEither, Int](processor.evaluate)