I would expect #::: and ++ to behave identically when both operands are LazyLists. Indeed, with the following code typed into a VS Code / Metals worksheet:
LazyList.from(0) ++ LazyList.from(0)
LazyList.from(0) #::: LazyList.from(0)
both evaluate to LazyList[Int] = LazyList(<not computed>). Makes perfect sense. And if I do this:
def countFrom(initial: LazyList[Int]): LazyList[Int] =
LazyList(initial.head) #::: countFrom(LazyList(initial.head + 1))
val m1 = countFrom(LazyList(0))
VS Code shows that m1 evaluates to: LazyList[Int] = LazyList(<not computed>) which again makes perfect sense to me. No one asked for any elements, so nothing should be computed yet. But if I change the #::: to ++ as per the following:
def countFrom(initial: LazyList[Int]): LazyList[Int] =
LazyList(initial.head) ++ countFrom(LazyList(initial.head + 1))
val m1 = countFrom(LazyList(0))
then VS Code cannot evaluate m1 due to a stack overflow error while evaluating countFrom calling countFrom calling countFrom, etc. (Btw, yes, this is bizarre, contrived code. It's a simplification of code from a project I was working on that was constructing more interesting LazyLists.)
So apparently there IS a difference between ++ and #::: which is not exposed when concatenating LazyLists generated via the built-in from method, but which IS exposed by my (weird) countFrom method. Can anyone explain what's going on here?
[added after Luis Miguel Mejía Suárez's comment]
Thanks to Luis Miguel Mejía Suárez, I think I get it now, so I'm going to try to explain the details after having looked at the LazyList source (feel free to fix me if I get something wrong!). In the expression X #::: Y, Y is this, and X is the argument (which is why the parameter to #::: is named prefix in the scala docs). Since #::: is defined on Deferrer, this causes an implicit conversion of Y (a LazyList) into a Deferrer, which wraps Y into a thunk (nullary function). X #::: Y then becomes X lazyAppendedAll toDeferrer(Y)(). Then, I guess the key is that the second parameter to lazyAppendedAll is pass-by-name, so toDeferrer(Y)() is not actually evaluated until it is needed. Now as for ++, I did not see a definition of ++ in the LazyList source, so I guess it gets inherited and has a much simpler definition where, with X ++ Y, X is this and Y is the argument and Y is passed by value, and is thus evaluated on entry to ++. Since Y is a LazyList, the elements are not calculated, but Y must be evaluated down to the LazyList itself, and that's where the infinite recursion results.
Related
The scala documentation has a code example that includes the following line:
val numberFunc = numbers.foldLeft(List[Int]())_
What does the underscore after the method call mean?
It's a partially applied function. You only provide the first parameter to foldLeft (the initial value), but you don't provide the second one; you postpone it for later. In the docs you linked they do it in the next line, where they define squares:
val numberFunc = numbers.foldLeft(List[Int]())_
val squares = numberFunc((xs, x) => xs:+ x*x)
See that (xs, x) => xs:+ x*x, that's the missing second parameter which you omitted while defining numberFunc. If you had provided it right away, then numberFunc would not be a function - it would be the computed value.
So basically the whole thing can also be written as a one-liner in the curried form:
val squares = numbers.foldLeft(List[Int]())((xs, x) => xs:+ x*x)
However, if you want to be able to reuse foldLeft over and over again, having the same collection and initial value, but providing a different function every time, then it's very convinient to define a separate numbersFunc (as they did in the docs) and reuse it with different functions, e.g.:
val squares = numberFunc((xs, x) => xs:+ x*x)
val cubes = numberFunc((xs, x) => xs:+ x*x*x)
...
Note that the compiler error message is pretty straightforward in case you forget the underscore:
Error: missing argument list for method foldLeft in trait
LinearSeqOptimized Unapplied methods are only converted to functions
when a function type is expected. You can make this conversion
explicit by writing foldLeft _ or foldLeft(_)(_) instead of
foldLeft. val numberFunc = numbers.foldLeft(ListInt)
EDIT: Haha I just realized that they did the exact same thing with cubes in the documentation.
I don't know if it helps but I prefer this syntax
val numberFunc = numbers.foldLeft(List[Int]())(_)
then numberFunc is basically a delegate corresponding to an instance method (instance being numbers) waiting for a parameter. Which later comes to be a lambda expression in the scala documentation example
I am new to Scala and studying a book about it (Programming in Scala). I am really lost, what is the author trying to explain with the code below. Can anyone explain it in more detail ?
{ val x = a; b.:::(x) }
::: is a method that prepends list given as argument to the list it is called on
you could look at this as
val a = List(1, 2)
val b = List(3, 4)
val x = a
b.prependList(x)
but actually for single argument methods if it's not ambiguous scala allows to skip parenthesis and the dot and this is how this method is supposed to be used to not look ugly
x ::: b
it will just join these two lists, but there is some trick here
if method name ends with : it will be bound the other way
so typing x ::: b works as if this type of thing was done (x):::.b. You obviously can't type it like this in scala, won't compile, but this is what happens. Thanks to this x is on the left side of the operator and it's elements will be on the left side (beginning) of the list that is result of this call.
Oh well, now I found maybe some more explanation for you and also the very same piece of code you posted, in answer to this question: What good are right-associative methods in Scala?
Assuming a and b are lists: It assigns a to x, then returns the list b prepended with the list x.
For example, if val a = List(1,2,3) and val b = List(4,5,6) then it returns List(1,2,3,4,5,6).
This question is not about the recommended practices for notation in method chaining, its about understanding this particular case.
I am learning Scala and Play for about 2 weeks now. I have prior one month learning experience in scala sometime in 2011.
I am having trouble understanding why this line is not working
List(1,2,3) map {x=>x*2}.filter((x:Int)=>x==2)
But this one is working
List(1,2,3).map{x=>x*2}.filter((x:Int)=>x==2)
One reason that I can think of is that the filter is being called upon the function value rather than the resulting collection.
Why is it still not working when Space and Dot notation are mixed? If I keep pure Space or Dot notation then it works otherwise not.
I would have not got confused if I only saw pure notation everywhere. I have seen mixed notation especially in the Play codebase. What am I missing?
It works as expected.
This line:
List(1,2,3) map {x=>x*2}.filter((x:Int)=>x==2)
means
List(1,2,3) map ( {x=>x*2}.filter((x:Int)=>x==2) )
It is definitely a error, but you could use it like this:
val f1 = (x: Int) => x * 2
val f2 = (x: Int) => x + 2
List(1,2,3) map f1.andThen(f2) // List(1,2,3).map( f1.andThen(f2) )
//List[Int] = List(4, 6, 8)
This code creates new function as composition of 2 functions: {(x: Int)=>x*2}.andThen((x:Int)=> x + 2) and then applies map method to it.
You could mix Space or Dot notations, but you should know that "priority" of dot is higher.
Expressions like
ls map (_ + 1) sum
are lovely because they are left-to-right and not nested. But if the functions in question are defined outside the class, it is less pretty.
Following an example I tried
final class DoublePlus(val self: Double) {
def hypot(x: Double) = sqrt(self*self + x*x)
}
implicit def doubleToDoublePlus(x: Double) =
new DoublePlus(x)
which works fine as far as I can tell, other than
A lot of typing for one method
You need to know in advance that you want to use it this way
Is there a trick that will solve those two problems?
You can call andThen on a function object:
(h andThen g andThen f)(x)
You can't call it on methods directly though, so maybe your h needs to become (h _) to transform the method into a partially applied function. The compiler will translate subsequent method names to functions automatically because the andThen method accepts a Function parameter.
You could also use the pipe operator |> to write something like this:
x |> h |> g |> f
Enriching an existing class/interface with an implicit conversion (which is what you did with doubleToDoublePlus) is all about API design when some classes aren't under your control. I don't recommend to do that lightly just to save a few keystrokes or having a few less parenthesis. So if it's important to be able to type val h = d hypot x, then the extra keystrokes should not be a concern. (there may be object allocations concerns but that's different).
The title and your example also don't match:
f(g(h(x))) can be rewritten asf _ compose g _ compose h _ apply x if your concern is about parenthesis or f compose g compose h apply x if f, g, h are function objects rather than def.
But ls map (_ + 1) sum aren't nested calls as you say, so I'm not sure how that relates to the title. And although it's lovely to use, the library/language designers went through a lot of efforts to make it easy to use and under the hood is not simple (much more complex than your hypot example).
def fgh (n: N) = f(g(h(n)))
val m = fgh (n)
Maybe this, observe how a is provided:
def compose[A, B, C](f: B => C, g: A => B): A => C = (a: A) => f(g(a))
basically like the answer above combine the desired functions to a intermediate one which you then can use easily with map.
Starting Scala 2.13, the standard library provides the chaining operation pipe which can be used to convert/pipe a value with a function of interest.
Using multiple pipes we can thus build a pipeline which as mentioned in the title of your question, minimizes the number of parentheses:
import scala.util.chaining._
x pipe h pipe g pipe f
I have two functions (not these have been edited since the original -- some of the answers below are responding to the original ones which returned a sequence of ()):
def foo1[A](ls: Iterable[A]) : Iterator[A] =
for (List(a, b) <- ls sliding 2) yield a
def foo2[A](ls: Iterable[A]) : Iterator[A] =
for (a::b::Nil <- ls sliding 2) yield a
which I naively thought were the same. But Scala gives this waning only for the first one:
warning: non variable type-argument A in type pattern List[A]
is unchecked since it is eliminated by erasure
I think I understand why it gives that error for the first one: Scala thinks that I'm trying to use the type as a condition on the pattern, ie a match against List[B](_, _) should fail if B does not inherit from A, except that this can't happen because the type is erased in both cases.
So two questions:
1) Why does the second one not give the same warning?
2) Is it possible to convince Scala that the type is actually known at compile time, and thus can't possibly fail to match?
edit: I think this answers my first question. But I'm still curious about the second one.
edit: agilesteel mentioned in a comment that
for (List(a, b) <- List(1,2,3,4) sliding 2) yield ()
produces no warning. How is that different from foo1 (shouldn't the [Int] parameter be erased just the same as the [A] parameter is)?
I'm not sure what is happening here, but the static type of Iterable[A].sliding is Iterator[Iterable[A]], not Iterator[List[A]] which would be the static type of List[A].sliding.
You can try receiving Seq instead of Iterable, and that work too. EDIT Contrary to what I previously claimed, both Iterable and Seq are co-variant, so I don't know what's different. END EDIT The definition of sliding is pretty weird too:
def sliding [B >: A] (size: Int): Iterator[Iterable[A]]
See how it requires a B, superclass of A, that never gets used? Contrast that with an Iterator.sliding, for which there's no problem:
def sliding [B >: A] (size: Int, step: Int = 1): GroupedIterator[B]
Anyway, on to the second case:
for (a::b::Nil <- ls sliding 2) yield a
Here you are decomposing the list twice, and for each decomposition the type of head is checked against A. Since the type of head is not erased, you don't have a problem. This is also mostly a guess.
Finally, if you turn ls into a List, you won't have a problem. Short of that, I don't think there's anything you can do. Otherwise, you can also write this:
def foo1[A](ls: Iterable[A]) : Iterator[A] =
for (Seq(a, b) <- ls.iterator sliding 2) yield a
1) The second one does not produce a warning probably because you are constructing the list (or the pattern) by prepending elements to the Nil object, which extends List parameterising it with Nothing. And since everything is Nothing, there is nothing to be worried about ;) But I'm not sure, really guessing here.
2) Why don't you just use:
def foo[A](ls: Iterable[A]) =
for (list <- ls sliding 2) yield ()