Why does Option require explicit toList inside for loops? - scala

Using a for loop with a simple Option works:
scala> for (lst <- Some(List(1,2,3))) yield lst
res68: Option[List[Int]] = Some(List(1, 2, 3))
But looping over the contents of the Option does not:
scala> for (lst <- Some(List(1,2,3)); x <- lst) yield x
<console>:8: error: type mismatch;
found : List[Int]
required: Option[?]
for (lst <- Some(List(1,2,3)); x <- lst) yield x
^
...unless the Option is explicitly converted to a List:
scala> for (lst <- Some(List(1,2,3)).toList; x <- lst) yield x
res66: List[Int] = List(1, 2, 3)
Why is the explicit list conversion needed? Is this the idiomatic solution?

for (lst <- Some(List(1,2,3)); x <- lst) yield x
is translated to
Some(List(1,2,3)).flatMap(lst => lst.map(x => x))
The flatMap method on Option expects a function that returns an Option, but you're passing a function that returns a List and there is no implicit conversion from List to Option.
Now if you convert the Option to a list first, the flatMap method of List will be called instead, which expects a function returning a List, which is what you are passing to it.
In this particular case, I think the most idiomatic solution is
Some(List(1,2,3)).flatten.toList

Related

Typed/Racket - How do I make this function work, keep getting TypeChecker errors

Having
(: f (-> Procedure (Pairof Integer Integer) Boolean))
(define (f comparator pair)
(comparator (first pair) (second pair)))
in TypedRacket, how can I make this function work? The function is supposed to work like this:
(f = '(1 2)) >>>> false.
(f > '(4 2)) >>>> true.
I get following errors:
Type Checker: Polymorphic function first could not be applied to arguments
Type Checker: Polymorphic function second could not be applied to arguments
Type Checker: cannot apply a function with unknown arity
So probably it is the function definition which causes this error but how can I fix this?
Here is a definition that does work for your examples:
(: f (-> (-> Integer Integer Boolean) (Listof Integer) Boolean))
(define (f comparator pair)
(comparator (first pair) (second pair)))
(f = '(1 2)) ; => #f
(f > '(4 2)) ; => #t
You must define the type of the first parameter as function from two integers to a boolean, and the second argument as a list (since you used a list in the call of the function).
This is a simple definition, just to start working with types. Than you can change it to apply the function to values with more general types, like Number instead of Integer, polymorphic functions, etc.

for comprehension in scala for implementing flatMap

Prof. Odersky in Coursera course has implemented flatMap using for loops as following:
def flatMap[T, U](xs: List[T], f: T => Iterable[U]) = for (x <- xs; y <- f(x)) yield y
And here's some application on the two functions (result as expected):
val xs = List(1, 2, 3, 4, 5)
flatMap[Int, Int](xs, x=> List(x /2))
However, When I try implement the flatMap as:
for (x <- xs) yield f(x)
It does not return the correct answer, it returns a List of Lists (it should return one big list contains all elements of the inner lists)
My question is, What is the different that make flatMap works as expected; between both:
for (x <- xs; y <- f(x)) yield y
//And
for (x <- xs) yield f(x)
And Why for (x <- xs; y <- x * x) yield y not compiling?
Your second version doesn't involve a call to flatMap at all. You can use reify to de-sugar the for comprehension and see:
scala> import reflect.runtime.universe._
import reflect.runtime.universe._
scala> val f: Int => Iterable[Int] = null
f: Int => Iterable[Int] = null
scala> val xs = List(1, 2, 3, 4, 5)
xs: List[Int] = List(1, 2, 3, 4, 5)
scala> reify { for (x <- xs; y <- f(x)) yield y }
res0: reflect.runtime.universe.Expr[List[Int]] = Expr[List[Int]]($read.xs.flatMap(((x) => $read.f.apply(x).map(((y) => y))(Iterable.canBuildFrom)))(List.canBuildFrom))
scala> reify { for (x <- xs) yield f(x) }
res1: reflect.runtime.universe.Expr[List[Iterable[Int]]] = Expr[List[scala.Iterable[Int]]]($read.xs.map(((x) => $read.f.apply(x)))(List.canBuildFrom))
the first can be simplified to:
xs.flatMap(x => f(x).map(identity))
which is equivalent to:
xs.flatMap(f)
and the second can be simplified to:
xs.map(f)

Sort elements as part of concatenating elements of 2 Lists

Here is a function I wrote for concatenating elements of a List using an accumulator with tail recursion :
val l1 = List(1, 2, 3) //> l1 : List[Int] = List(1, 2, 3)
val l2 = List(1, 2, 3) //> l2 : List[Int] = List(1, 2, 3)
def func(l1: List[Int], l2: List[Int], acc: List[Int]): List[Int] = {
(l1, l2) match {
case (Nil, Nil) => acc.reverse
case (h1 :: t1, h2 :: t2) => {
func(t1, t2, h1 :: h2 :: acc)
}
}
} //> func: (l1: List[Int], l2: List[Int], acc: List[Int])List[Int]
func(l1, l2, List()) //> res0: List[Int] = List(1, 1, 2, 2, 3, 3)
This is my understanding of the call order
func( 1 :: 1 :: () )
func( 2 :: 2 :: 1 :: 1 : () )
func( 3 :: 3 :: 2 :: 2 :: 1 :: 1 : () )
So the call order is the reason why I must call reverse on base call of acc so that the List is ordered in same ordering initial List elements. To try to minimize the steps required to concatenate the lists I have tried to add the elements like this :
func(t1, t2, acc :: h1 :: h2)
instead of
func(t1, t2, h1 :: h2 :: acc)
but receive compile time error :
value :: is not a member of Int
So it seems I cannot prepend these elements to this List ?
When you write x :: y, y must be a list and x the element you want to prepend.
You can use acc :+ h1 :+ h2 to append h1 and h2 to acc, but note that adding elements to the end of the list is a relatively expensive operation (linear with the length of the list).

for/list annotations in typed/racket

I'm trying to add types to some numerical racket code in the hopes of making it faster, but I am stuck dealing with for/list macro expansion in the code below.
(: index-member ((Listof Any) (Listof Any) -> (Listof Index)))
(define (index-member xs ys)
(filter-not negative?
(for/list ([(ann i Index) (in-range (ann (length xs) Index))])
(if (member (list-ref xs i) ys) i -1))))
This function returns a list of indexes foreach x which is a member of y. It works in Racket, but I can't seem to get it past the type checker for Typed Racket. Specifically, the error is:
Type Checker: Error in macro expansion -- insufficient type information to typecheck. please add more type annotations in: (for/list (((ann i Index) (in-range (ann (length xs) Index)))) (if (member (list-ref xs i) ys) i -1))
Can you provide annotations that get this past the type checker and/or explain why these type annotations are insufficient?
The key is to use the for/list: form instead since it allows you to add type annotations over the basic for/list form to give Typed Racket more guidance. I've made a few other adjustments to get the types to line up (e.g., using filter over filter-not, avoiding in-range, etc.):
#lang typed/racket
(: index-member ((Listof Any) (Listof Any) -> (Listof Index)))
(define (index-member xs ys)
(filter index?
(for/list: : (Listof Integer) ([i : Index (length xs)])
(if (member (list-ref xs i) ys) i -1))))
This actually exposes a weakness in the type of filter-not (filter is smarter about the type of the list it returns), which I'll look into fixing.

scala: yield pairwise combinations of two loops

I would like to create a collection with tuples containing all pairwise combinations of two lists. Something like:
for ( x <- xs )
for ( y <- ys )
yield (x,y)
In Python this would work, in Scala apparently for yields only for the last loop (so this evaluates to Unit)
What is the cleanest way to implement it in Scala?
You were almost there:
scala> val xs = List (1,2,3)
xs: List[Int] = List(1, 2, 3)
scala> val ys = List (4,5,6)
ys: List[Int] = List(4, 5, 6)
scala> for (x <- xs; y <- ys) yield (x,y)
res3: List[(Int, Int)] = List((1,4), (1,5), (1,6), (2,4), (2,5), (2,6), (3,4), (3,5), (3,6))
A little bit more explicit according to Nicolas:
In Scala you can use multiple generators in a single for-comprehension.
val xs = List(1,2,3)
val ys = List(4,5)
for {
x <- xs
y <- ys
} yield (x,y)
res0: List[(Int, Int)] = List((1,4), (1,5), (2,4), (2,5), (3,4), (3,5))
You can even evaluate in the comprehension.
for {
x <- xs
y <- ys
if (x + y == 6)
} yield (x,y)
res1: List[(Int, Int)] = List((1,5), (2,4))
Or make an assignment.
for {
x <- xs
y <- ys
val z = x + y
} yield (x,y,z)
res2: List[(Int,Int,Int)] = List((1,4,5), (1,5,6), (2,4,6), (2,5,7), (3,4,7), (3,5,8))