Is there a difference in scala type bound notation direction? - scala

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.

Related

Lower bound and upper bound types for scala and dotty

Martin Odersky mentioned that they introduced compound and intersection types in dotty. On his slides there were two pictures representing the difference in upper and lower bounds for old and new type system.
This is the old one:
This is the new one:
Can someone explain what actually upper bound for T <: U means? Why it's infinite for current version of scala? How it would be differ for T >: U for example.
If T <: U (i.e. T is a subtype of U or U is a supertype of T) then U is an upper bound for T and T is a lower bound for U.
In Scala 2
T with U <: T
T with U <: U
U with T <: T
U with T <: U
In Dotty
T & U =:= U & T
T | U =:= U | T
T & U <: T
T & U <: U
T <: T | U
U <: T | U
If T <: U then
T with U =:= T
T & U =:= T
T | U =:= U
If T >: U then U <: T and vice versa
T with U =:= U
T & U =:= U
T | U =:= T
In Scala 2 if T and U are from different inheritance hierarchies (or more precisely, subtyping hierarchies) then the best what can be said is
T <: Any
U <: Any
In this sense upper bound is infinite.

Summing "Large" Nat's

Given:
scala> import shapeless.nat.
_0 _10 _12 _14 _16 _18 _2 _21 _3 _5 _7 _9 natOps
_1 _11 _13 _15 _17 _19 _20 _22 _4 _6 _8 apply toInt
scala> import shapeless.ops.nat._
import shapeless.ops.nat._
After > 3 minutes, the following code has not compiled/run. Why's that?
scala> Sum[_22, _22]
Also, looking at the above REPL auto-completion, does _44 even exist in shapeless?
Why is it so slow?
Let's start with a smaller number. When you ask for Sum[_4, _4], the compiler is going to go looking for an instance, and it'll find these two methods:
implicit def sum1[B <: Nat]: Aux[_0, B, B] = new Sum[_0, B] { type Out = B }
implicit def sum2[A <: Nat, B <: Nat](implicit
sum: Sum[A, Succ[B]]
): Aux[Succ[A], B, sum.Out] = new Sum[Succ[A], B] { type Out = sum.Out }
The first one is clearly out since _4 is not _0. It knows that _4 is the same as Succ[_3] (more on that in a second), so it'll try sum2 with A as _3 and B as _4.
This means we need to find a Sum[_3, _5] instance. sum1 is out for similar reasons as before, so we try sum2 again, this time with A = _2 and B = _5, which means we need a Sum[_2, _6], which gets us back to sum2, with A = _1 and B = _6, which sends us looking for a Sum[_1, _7]. This is the last time we'll use sum2, with A = _0 and B = _7. This time when we go looking for a Sum[_0, _8] we'll hit sum1 and we're done.
So it's clear that for n + n we're going to have to do n + 1 implicit searches, and during each one the compiler is going to be doing type equality checks and other stuff (update: see Miles's answer for an explanation of what the biggest problem here is) that requires traversing the structure of the Nat types, so we're in exponential land. The compiler really, really isn't designed to work efficiently with types like this, which means that even for small numbers, this operation is going to take a long time.
Side note 1: implementation in Shapeless
Off the top of my head I'm not entirely sure why sum2 isn't defined like this:
implicit def sum2[A <: Nat, B <: Nat](implicit
sum: Sum[A, B]
): Aux[Succ[A], B, Succ[sum.Out]] = new Sum[Succ[A], B] { type Out = Succ[sum.Out] }
This is much faster, at least on my machine, where Sum[_18, _18] compiles in four seconds as opposed to seven minutes and counting.
Side note 2: induction heuristics
This doesn't seem to be a case where Typelevel Scala's -Yinduction-heuristics helps—I just tried compiling Shapeless with the #inductive annotation on Sum and it's still seems pretty much exactly as horribly slow as without it.
What about 44?
The _1, _2, _3 type aliases are defined in code produced by this boilerplate generator in Shapeless, which is configured only to produce values up to 22. In this case specifically, this is an entirely arbitrary limit. We can write the following, for example:
type _23 = Succ[_22]
And we've done exactly the same thing the code generator does, but going one step further.
It doesn't really matter much that Shapeless's _N aliases stop at 22, though, since they're just aliases. The important thing about a Nat is its structure, and that's independent of any nice names we might have for it. Even if Shapeless didn't provide any _N aliases at all, we could still write code like this:
import shapeless.Succ, shapeless.nat._0, shapeless.ops.nat.Sum
Sum[Succ[Succ[_0]], Succ[Succ[_0]]]
And it would be exactly the same as writing Sum[_2, _2], except that it's a lot more annoying to type.
So when you write Sum[_22, _22] the compiler isn't going to have any trouble representing the result type (i.e. 44 Succs around a _0), even though it doesn't have a _44 type alias.
Following on from Travis's excellent answer, it appears that it's the use of the member type in the definition of sum2 which is the root of the problem. With the following definition of Sum and its instances,
trait Sum[A <: Nat, B <: Nat] extends Serializable { type Out <: Nat }
object Sum {
def apply[A <: Nat, B <: Nat](implicit sum: Sum[A, B]): Aux[A, B, sum.Out] = sum
type Aux[A <: Nat, B <: Nat, C <: Nat] = Sum[A, B] { type Out = C }
implicit def sum1[B <: Nat]: Aux[_0, B, B] = new Sum[_0, B] { type Out = B }
implicit def sum2[A <: Nat, B <: Nat, C <: Nat]
(implicit sum : Sum.Aux[A, Succ[B], C]): Aux[Succ[A], B, C] =
new Sum[Succ[A], B] { type Out = C }
}
which replaces the use of the member type with an additional type variable, the compile time is 0+noise on my machine both with and without -Yinduction-heurisitics.
I think that the issue we're seeing is a pathological case for subtyping with member types.
Aside from that, the induction is so small that I wouldn't actually expect -Yinduction-heurisitics to make much of an improvement.
Update now fixed in shapeless.

How can I customize Scala ambiguous implicit errors when using shapeless type inequalities

def typeSafeSum[T <: Nat, W <: Nat, R <: Nat](x: T, y: W)
(implicit sum: Sum.Aux[T, W, R], error: R =:!= _7) = x
typeSafeSum(_3, _4) //compilation error, ambiguous implicit found.
I dont think that error message "ambiguous implicit found" is friendly, how can I customize it to say something like "the sum of 2 NAT value should not equal to 7"
Many thanks in advance
shapeless's =:!= (and similar type inequality operators) inherently exploit ambiguous implicits to encode Prolog-style negation as failure. And, as you've observed, Scala doesn't have a mechanism which allows library authors to provide more meaningful error messages when ambiguity is expected. Perhaps it should, or perhaps Scala should provide a more direct representation of the negation of a type making this encoding unnecessary.
Given that you've couched the question in terms of Nats I think it's probably reasonable that you're trying to work with type inequality. If it weren't Nats my recommendation in answer to another question that a type class directly encoding the relation of interest would apply here too. As it is though, I recommend that same solution as a workaround for not being able to provide better error messages.
import shapeless._, nat._, ops.nat._
#annotation.implicitNotFound(msg = "${A} + ${B} = ${N}")
trait SumNotN[A <: Nat, B <: Nat, N <: Nat]
object SumNotN {
implicit def sumNotN[A <: Nat, B <: Nat, R <: Nat, N <: Nat]
(implicit sum: Sum.Aux[A, B, R], error: R =:!= N): SumNotN[A, B, N] =
new SumNotN[A, B, N] {}
}
def typeSafeSum[T <: Nat, W <: Nat](x: T, y: W)
(implicit valid: SumNotN[T, W, _7]) = x
scala> typeSafeSum(_3, _4)
<console>:20: error: shapeless.nat._3 + shapeless.nat._4 = shapeless.nat._7
typeSafeSum(_3, _4)
^
The technique (hiding an expected ambiguous implicit behind an implicit we expect to be not found in the case of underlying ambiguity) is generally applicable, but is obviously fairly heavyweight ... another reason why type inequalities should be avoided if at all possible.

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

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

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.