Why does Scala warn about type erasure in the first case but not the second? - scala

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 ()

Related

Why do `Left` and `Right` have two type parameters?

I understand it would be difficult to change now without breaking existing code, but I'm wondering why it was done that way in the first place.
Why not just:
sealed trait Either[+A, +B]
case class Left[A](x: A) extends Either[A, Nothing]
case class Right[B](x: B) extends Either[Nothing, B]
Is there some drawback here that I'm failing to see...?
Not sure how relevant this answer really is to Scala, but it certainly is in Haskell which is evidently where Scala's Either was borrowed from and so that's probably the best historical reason for why Scala did it this way.
Either is the canonical coproduct, i.e. for any types A and B you have
The type EitherA,B ≈ A ⊕ B
Two coprojections LeftA,B : A -> A⊕B and RightA,B : B -> A⊕B
such that for any type Y and any functions fA : A -> Y and fB : B -> Y, there exists exactly one function f : A⊕B -> Y with the property that fA = f ∘ LeftA,B and fB = f ∘ RightA,B.
To formulate this mathematically, it is quite helpful to have the information which particular Left you're working with explicit, because else the domains of the morphisms would be all unclear. In Scala this may be unnecessary because of implicit covariant conversion, but not in maths and not in Haskell.
In Haskell it isn't really an issue at all, because type inference will automatically do what's needed:
GHCi, version 8.6.5: http://www.haskell.org/ghc/ :? for help
Loaded GHCi configuration from /tmp/haskell-stack-ghci/2a3bbd58/ghci-script
Prelude> let right2 = Right 2
Prelude> let left42 = Left 42.0
Prelude> (+) <$> right2 <*> left42
Left 42.0
Unlike, apparently, in Scala, Haskell just leaves the unspecified second argument of left42 as a type variable (unless the monomorphism restriction is enabled), so you can later use it in any context requiring some Either Double R for any type R. Of course it's possible to make that explicit too
right2 :: Either a Int
right2 = Right 2
left42 :: Either Double a
left42 = Left 42
main :: IO ()
main = print $ (+) <$> right2 <*> left42
which surely is possible in Scala just as well.
There's no meaningful drawback that I've found to your scheme. For the last eight years or so I've used my own variant of Either which is exactly as you describe under another name (Ok[+Y, +N] with Yes[+Y] and No[+N] as the alternatives). (Historical note: I started when Either was not right-biased, and wanted something that was; but then I kept using my version because it was more convenient to have only half the types.)
The only case I've ever found where it matters is when you pattern match out one branch and no longer have access to the type information of the other branch.
def foo[A, B: Typeclass](e: Either[A, B]) =
implicitly[Typeclass[B]].whatever()
// This works
myEither match {
case l: Left[L, R] => foo(l)
case r: Right[L, R] => foo(r)
}
def bar[N, Y: Typeclass](o: Ok[N, Y]) =
implicitly[Typeclass[Y]].whatever()
// This doesn't work
myOk match {
case y: Yes[Y] => bar(y) // This is fine
case n: No[N] => bar(n) // Y == Nothing!
}
However, I never do this. I could just use o to get the right type. So it doesn't matter! Everything else is easier (like pattern matching and changing one case and not the other...you don't need case Left(l) => Left(l) which rebuilds the Left for no reason except to switch the type of the uninhabited branch).
There are other cases (e.g. setting types in advance) that seem like they should be important, but in practice are almost impossible to make matter (e.g. because covariance will find the common supertype anyway, so what you set doesn't constrain anything).
So I think the decision was made before there was enough experience with the two ways to do it, and the wrong choice was made. (It's not a very wrong choice; Either is still decent.)

flatMap function in scala

In the below two scenarios, I have flatMap function invoked on a list. In both the cases, the map portion of the flatMap function returns an array which has a iterator. In the first case, the code errors out where in the second case, it produces the expected result.
Scenario-1
val x = List("abc","cde")
x flatMap ( e => e.toArray)
<console>:13: error: polymorphic expression cannot be instantiated to expected type;
found : [B >: Char]Array[B]
required: scala.collection.GenTraversableOnce[?]
x flatMap ( e => e.toArray)
Scenario-2
val x = List("abc,def")
x flatMap ( e => e.split(",") )
res1: List[String] = List(abc, def) //Result
Can you please help why in the first case, it is not behaving as expected ?
I think the difference is that in scenario 1 you actually have a Array[B] where B is some yet-to-be-decided supertype of Char. Normally the compiler would look for an implicit conversion to GenTraversableOnce but because B is not yet known you run into a type inference issue / limitation.
You can help type inference by filling in B.
List("abc", "cde").flatMap(_.toArray[Char])
Or even better, you don't need flatMap in this case. Just call flatten.
List("abc", "cde").flatten
It is worth keeping in mind that Array is not a proper Scala collection so compiler has to work harder to first convert it to something that fits with the rest of Scala collections
implicitly[Array[Char] <:< GenTraversableOnce[Char]] // error (Scala 2.12)
implicitly[Array[Char] <:< IterableOnce[Char]] // error (Scala 2.13)
Hence because flatMap takes a function
String => GenTraversableOnce[Char]
but we are passing in
String => Array[Char]
the compiler first has to find an appropriate implicit conversion of Array[Char] to GenTraversableOnce[Char]. Namely this should be wrapCharArray
scala.collection.immutable.List.apply[String]("abc", "cde").flatMap[Char](((e: String) => scala.Predef.wrapCharArray(scala.Predef.augmentString(e).toArray[Char]((ClassTag.Char: scala.reflect.ClassTag[Char])))))
or shorter
List("abc", "cde").flatMap(e => wrapCharArray(augmentString(e).toArray))
however due to type inference reasons explained by Jasper-M, the compiler is unable to select wrapCharArray conversion. As Daniel puts it
Array tries to be a Java Array and a Scala Collection at the same
time. It mostly succeeds, but fail at both for some corner cases.
Perhaps this is one of those corner cases. For these reasons it is best to avoid Array unless performance or compatibility reasons dictate it. Nevertheless another approach that seems to work in Scala 2.13 is to use to(Collection) method
List("abc","cde").flatMap(_.to(Array))
scala> val x = List("abc","cde")
x: List[String] = List(abc, cde)
scala> x.flatMap[Char](_.toArray)
res0: List[Char] = List(a, b, c, c, d, e)
As the error mentions, your type is wrong.
In the first case, if you appy map not flatmap, you obtain List[Array[Char]]. If you apply flatten to that, you get a List[Char]
In the second case, if you appy map not flatmap, you obtain List[Array[String]]. If you apply flatten to that, you get a List[String]
I suppose you need to transform String to Char in your Array, in order to make it work.
I use Scala 2.13 and still have the same error.

how scala folding works?

I am trying to understand the following code but can't.
it is supposed to create a child actor for an Event if it does not exist, otherwise says that the Event exist as it as an associated child actor.
context.child(name).fold(create())(_ => sender() ! EventExists)
But the fold here does not make sense to me. If the context.child is emtpty we get the creation and i understand that. However if there is children we are still going to create why ?
Akka's child returns an Option
As you can see from Option's scaladoc:
fold[B](ifEmpty: ⇒ B)(f: (A) ⇒ B): B Returns the result of
applying f to this scala.Option's value if the scala.Option is
nonempty. Otherwise, evaluates expression ifEmpty.
Or making it more clear:
This (fold) is equivalent to scala.Option map f getOrElse ifEmpty.
So the first parameter of fold is lazy (call-by-name) and evaluates only if Option is empty. The second parameter (a function) is called only if Option is not empty.
Experiment:
scala> Some(0).fold({println("1");1}){_ => println("2"); 2}
2
res0: Int = 2
scala> None.fold({println("1");1}){_ => println("2"); 2}
1
res1: Int = 1
Here's some readings about:
https://kwangyulseo.com/2014/05/21/scala-option-fold-vs-option-mapgetorelse/
And some critics of that approach:
http://www.nurkiewicz.com/2014/06/optionfold-considered-unreadable.html
But in Option.fold() the contract is different: folding function takes
just one parameter rather than two. If you read my previous article
about folds you know that reducing function always takes two
parameters: current element and accumulated value (initial value
during first iteration). But Option.fold() takes just one parameter:
current Option value! This breaks the consistency, especially when
realizing Option.foldLeft() and Option.foldRight() have correct
contract (but it doesn't mean they are more readable).

What does the ++: operator do to a list?

Alright, Scala has me feeling pretty dense. I'm finding the docs pretty impenetrable -- and worse, you can't Google the term "Scala ++:" because Google drops the operator terms!
I was reading some code and saw this line:
Seq(file) ++: children.flatMap(walkTree(_))
But couldn't figure it out. The docs for Seq show three things:
++
++:
++:
Where the latter two are over loaded to do.. something. The actual explanation in the doc says that they do the same thing as ++. Namely, add one list to another.
So, what exactly is the difference between the operators..?
++ and ++: return different results when the operands are different types of collection. ++ returns the same collection type as the left side, and ++: returns the same collection type as the right side:
scala> List(5) ++ Vector(5)
res2: List[Int] = List(5, 5)
scala> List(5) ++: Vector(5)
res3: scala.collection.immutable.Vector[Int] = Vector(5, 5)
There are two overloaded versions of ++: solely for implementation reasons. ++: needs to be able to take any TraversableOnce, but an overloaded version is provided for Traversable (a subtype of TraversableOnce) for efficiency.
Just to make sure:
A colon (:) in the end of a method name makes the call upside-down.
Let's make two methods and see what's gonna happen:
object Test {
def ~(i: Int) = null
def ~:(i: Int) = null //putting ":" in the tail!
this ~ 1 //compiled
1 ~: this //compiled
this.~(1) //compiled
this.~:(1) //compiled.. lol
this ~: 1 //error
1 ~ this //error
}
So, in seq1 ++: seq2, ++: is actually the seq2's method.
edited: As #okiharaherbst mentions, this is called as right associativity.
Scala function naming will look cryptic unless you learn a few simple rules and their precedence.
In this case, a colon means that the function has right associativity as opposed to the more usual left associativity that you see in imperative languages.
So ++: as in List(10) ++: Vector(10) is not an operator on the list but a function called on the vector even if it appears on its left hand-side, i.e., it is the same as Vector(10).++:(List(10)) and returns a vector.
++ as in List(10) ++ Vector(10) is now function called on the list (left associativity), i.e., it is the same as List(10).++(Vector(10)) and returns a list.
what exactly is the difference between the operators..?
The kind of list a Seq.++ operates on.
def ++[B](that: GenTraversableOnce[B]): Seq[B]
def ++:[B >: A, That](that: Traversable[B])(implicit bf: CanBuildFrom[Seq[A], B, That]): That
def ++:[B](that: TraversableOnce[B]): Seq[B]
As commented in "What is the basic collection type in Scala?"
Traversable extends TraversableOnce (which unites Iterator and Traversable), and
TraversableOnce extends GenTraversableOnce (which units the sequential collections and the parallel.)

Why is zipWithIndex implemented in Iterable and not Traversable?

I'm reading "Programming in Scala 2ed". In section 24.4, it's noted that Iterable contains many method that cannot be efficiently written without an iterator. Table 24.2 contains these methods. However, I don't understand why some of them cannot be efficiently implemented on iterator. For example, consider zipWithIndex.
def zipWithIndex[A1 >: A, That](implicit bf: CanBuildFrom[Repr, (A1, Int), That]): That = {
val b = bf(repr)
var i = 0
for (x <- this) {
b += ((x, i))
i +=1
}
b.result
}
Why not move this definition to traversable? It seems to me that the code could be exactly the same and there would be no difference in efficienty.
You're completely correct, and your implementation should work. No good reason to have zipWithIndex defined in Iterable and not Traversable; neither makes any guarantee about the ordering of the elements under traversal.
(This is my first answer on StackOverflow. Hope I've been helpful. :) If I've not, please tell me.)
Traversable does not guarantee the order in which the elements will be visited and only requires you to define a foreach method with the following signature:
def foreach[U](f: Elem => U): Unit
Since this method just needs to call f for each element in any order, it doesn't make sense to have an index on elements since the order could be different for each invocation of foreach.
Edit: This is really just an explanation, why it's not on Traversable. As Luigi pointed out in the comments, zipWithIndex would make more sense on Seq.