in scala what is the A in sum[B >: A](implicit num: Numeric[B]): B - scala

I see this method in scala List.sum
sum[B >: A](implicit num: Numeric[B]): B
now I understand that it expects any num argument to be implicitly converted to Numeric[B] which means its of typeclass Numeric However what I don't understand is what is this A doing there if the implementation block does not refer to it at all.
the return value is B
and the implementation is
foldLeft(num.zero)(num.plus)
and num is also of type Numeric[B] so if return value does not refer to A and implementation does not refer to A why is it needed?

It needs to be able to act on the contents of the list, which are As. Therefore, B must be a superclass of A, which is what B >: A means.
(In particular, num.plus must accept A arguments to match the signature of fold.)

Related

Scala Generics, which of these 4 generic version is specific to which particular problem

These four versions compile but i'm curious about context in which we should prefer one option rather than another.
// 1
def f[N, S <: Seq[N]](s: S)
// 2
def f[N, S[N] <: Seq[N]](s: S[N])
They are pretty similar when use 1 rather than 2
2 impose that S have N as generic parameter as 1 but then what is the difference between these two ?
Then we have more general settings.
// 3
def f[N, S[X] <: Seq[X]](s: S[N])
// 3.a
def f[N, S[X] <: Seq[X]](s: S[N]): S[Int]
From what i humbly understood 3 authorize to extract the generic container type to reuse it later and get something like 3.a.
But what is the meaning of the undeclared X generic parameter, i suppose it's a way to declare something special but i don't get it.
// 4
def f[N, S[X] <: Seq[_]](s: S[N])
I don't know what to say about 4 except than from what i know Seq[_] stands for Seq[Any]
Finally i'm simply curious to have more information about these tools and their specificity to get things done more properly.
// 2
def f[N, S[N] <: Seq[N]](s: S[N])
The idea here is that the first N parameter and N mentioned in S[N] <: Seq[N] are completely independent parameters. They are just sharing the same name.
N mentioned in S[N] is visible only in a scope of its bound <: Seq[N].
N used in parameter definition (s: S[N]) comes from first N as this is the only N parameter visible for parameter type definition. So instead of N in S[N] <: Seq[N] you can use any letter and this will not affect your parameter type at any way.
// 4
def f[N, S[X] <: Seq[_]](s: S[N])
Here you just ignored X parameter.
Edit: as #alexey-romanov mentioned in a comment. There is difference between S[X] <: Seq[X] and S[X] <: Seq[_]
Here is an example showing the difference:
def f1[N, S[X] <: Seq[X]](s: S[N]) = ""
def f2[N, S[X] <: Seq[_]](s: S[N]) = ""
type Foo[A] = Seq[Int]
val foo: Foo[String] = Seq(2,3)
//f1(foo) -- compilation error
f2(foo)
The problem here is as type constrictor is a kind of "function on types", we can define such "function" accepting one type as a parameter but returning type parametrized by another parameter not related to parameter used in type constructor. (See type Foo)
Passing foo val to f2 is fine because X is infered to String and Foo[String] is a "subtype"(actually they are equal) of Seq[Int], but when we pass foo to f1 the X is still a String but Foo[String] is not "subtype" of Seq[String] (because Foo[String]==Seq[Int] not subtype of Seq[String])
// 1
def f[N, S <: Seq[N]](s: S)
And here you said that N used in Seq[N] is the same as first parameter N. So this is the same N

Is there a difference in scala type bound notation direction?

Is there a difference in scala type bound notation direction, as in is [B <: A] the same as [A >: B]?
B <: A means that B has an upper-bound of A. Which means that B can be any type from Nothing to A in the type hierarchy.
A >: B means that A has a lower-bound of B, which means that A can be anything from B to Any in the type hierarchy.
In general, they do not mean the same thing. Each one imposes a bound on a different type parameter. This isn't variance notation either, these are type bounds.
Here [B <: A] you declare type B which extends A,
here [A >: B] you declare type A which is a parent of type B.

Type inference inconsistency between toList and toBuffer

As per the example below, calling xs.toList.map(_.toBuffer) succeeds, but xs.toBuffer.map(_.toBuffer) fails. But when the steps in the latter are performed using an intermediate result, it succeeds. What causes this inconsistency?
scala> "ab-cd".split("-").toBuffer
res0: scala.collection.mutable.Buffer[String] = ArrayBuffer(ab, cd)
scala> res0.map(_.toBuffer)
res1: scala.collection.mutable.Buffer[scala.collection.mutable.Buffer[Char]] = ArrayBuffer(ArrayBuffer(a, b), ArrayBuffer(c, d))
scala> "ab-cd".split("-").toBuffer.map(_.toBuffer)
<console>:8: error: missing parameter type for expanded function ((x$1) => x$1.toBuffer)
"ab-cd".split("-").toBuffer.map(_.toBuffer)
^
scala> "ab-cd".split("-").toList.map(_.toBuffer)
res3: List[scala.collection.mutable.Buffer[Char]] = List(ArrayBuffer(a, b), ArrayBuffer(c, d))
Look at the definitions of toBuffer and toList:
def toBuffer[A1 >: A]: Buffer[A1]
def toList: List[A]
As you can see, toBuffer is generic, while toList is not.
The reason for this difference is - I believe - that Buffer is invariant, while List is covariant.
Let's say that we have the following classes:
class Foo
class Bar extends Foo
Because List is covariant, you can call toList on an instance of Iterable[Bar] and treat the result as a List[Foo].
If List where invariant, this would not be the case.
Buffer being invariant, if toBuffer was defined as def toBuffer: Buffer[A] you would similarly not be able to treat the result
of toBuffer (on an instance of Iterable[Bar]) as an instance of Buffer[Foo] (as Buffer[Bar] is not a sub-type of Buffer[Foo], unlike for lists).
But by declaring toBuffer as def toBuffer[A1 >: A] (notice the added type parameter A1), we get back the possibility to have toBuffer return an instance of Buffer[Foo] :
all we need is to explcitly set A1 to Foo, or let the compiler infer it (if toBuffer is called at a site where a Buffer[Foo] is expected).
I think this explains the reason why toList and toBuffer are defined differently.
Now the problem with this is that toBuffer is generic, and this can badly affect inference.
When you do this:
"ab-cd".split("-").toBuffer
You never explicitly say that A1 is String, but because "ab-cd".split("-") has unambiguously the type Array[String], the compiler knows that A is String.
It also knows that A1 >: A (in toBuffer), and without any further constraint, it will infer A1 to be exactly A, in other words String.
So in the end the whole expression returns a Buffer[String].
But here's the thing: in scala, type inference happens in an expression as a whole.
When you have something like a.b.c, you might expect that scala will infer an exact type
for a, then from that infer an exact type for a.b, and finally for a.b.c. Not so.
Type inference is deferred to the whole expression a.b.c (see scala specification "6.26.4 Local Type Inference
", "case 1: selections")
So, going back to your problem, in the expression "ab-cd".split("-").toBuffer.map(_.toBuffer), the sub-expression "ab-cd".split("-").toBuffer is not typed Buffer[String], but instead
it stays typed as something like Buffer[A1] forSome A1 >: String. In other words, A1 is not fixed, we just carry the constraint A1 >: String to the next step of inference.
This next step is map(_.toBuffer), where map is defined as map[C](f: (B) ⇒ C): Buffer[B]. Here B is actually the same as A1, but at this point A1
is still not fully known, we only know that A1 >: String.
Here lies our problem. The compiler needs to know the exact type of the anonymous function (_.toBuffer) (simply because instantiating a Function1[A,R] requires to know the exact types of A and R, just like for any generic type).
So you need to tell him explcitly somehow, as it was not able to infer it exactly.
This means you need to do either:
"ab-cd".split("-").toBuffer[String].map(_.toBuffer)
Or:
"ab-cd".split("-").toBuffer.map((_:String).toBuffer)

Weird behavior of & function in Set

Set is defined as Set[A]. It takes a in-variant parameter. Doing below works as expected as we are passing co-variant argument:
scala> val a = Set(new Object)
a: scala.collection.immutable.Set[Object] = Set(java.lang.Object#118c38f)
scala> val b = Set("hi")
b: scala.collection.immutable.Set[String] = Set(hi)
scala> a & b
<console>:10: error: type mismatch;
found : scala.collection.immutable.Set[String]
required: scala.collection.GenSet[Object]
Note: String <: Object, but trait GenSet is invariant in type A.
You may wish to investigate a wildcard type such as `_ <: Object`. (SLS 3.2.10)
a & b
But the below works:
scala> Set(new Object) & Set("hi")
res1: scala.collection.immutable.Set[Object] = Set()
Above as I see it, the scala compiler converts Set("hi") to Set[Object] type and hence works.
What is the type-inference doing here? Can someone please link to specification explaining the behavior and when does it happen in general? Shouldn't it be throwing a compile time error for such cases? As 2 different output for the same operation type.
Not sure, but I think what you're looking for is described in the language spec under "Local Type Inference" (at this time of writing, section 6.26.4 on page 100).
Local type inference infers type arguments to be passed to expressions of polymorphic type. Say e is of type [ a1 >: L1 <: U1, ..., an >: Ln <: Un ] T and no explicit type
parameters are given.
Local type inference converts this expression to a type application e [ T1, ..., Tn ]. The choice of the type arguments T1, ..., Tn depends on the context in which the expression appears and on the expected type pt. There are three cases.
[ ... ]
If the expression e appears as a value without being applied to value arguments, the type arguments are inferred by solving a constraint system which relates the expression's type T with the expected type pt. Without loss of generality we can assume that T is a value type; if it is a method type we apply eta-expansion to convert it to a function type. Solving means finding a substitution σ of types Ti for the type parameters ai such that
None of inferred types Ti is a singleton type
All type parameter bounds are respected, i.e. σ Li <: σ ai and σ ai <: σ Ui for i = 1, ..., n.
The expression's type conforms to the expected type, i.e. σ T <: σ pt.
It is a compile time error if no such substitution exists. If several substitutions exist, local-type inference will choose for each type variable ai a minimal or maximal type Ti of the solution space. A maximal type Ti will be chosen if the type parameter ai appears contravariantly in the type T of the expression. A minimal type Ti will be chosen in all other situations, i.e. if the variable appears covariantly, nonvariantly or not at all in the type T. We call such a substitution an optimal solution of the given constraint system for the type T.
In short: Scalac has to choose values for the generic types that you omitted, and it picks the most specific choices possible, under the constraint that the result compiles.
The expression Set("hi") can be either a scala.collection.immutable.Set[String] or a scala.collection.immutable.Set[Object], depending on what the context requires. (A String is a valid Object, of course.) When you write this:
Set(new Object) & Set("hi")
the context requires Set[Object], so that's the type that's inferred; but when you write this:
val b = Set("hi")
the context doesn't specify, so the more-specific type Set[String] is chosen, which (as you expected) then makes a & b be ill-typed.

Why does the type parameter of reduceLeft contain a lower bound?

The signature of reduceLeft on some Seq[A] is
def reduceLeft [B >: A] (f: (B, A) => B): B
The type of A is known, but the lower bound >: tells us that B can be any supertype of A.
Why is it like this? Why not
def reduceLeft (f: (A, A) => A): A
We already know that the head of the sequence is type A and so I can't think of how B could be anything other than equal to A. Can you provide an example where B is some super-type?
Let's say your class B has a method combine(other:B): B. Now you call reduceLeft((b,a) => b.combine(a)) on a list of As. Since the return type of combine is B the type parameter to reduceLeft needs to be B.