7

Widening Union Types have been discussed here but I can't seem to find an answer to the following case

Let's start by looking at the following

val x = List(1, 2, "a")

This heterogeneous list is inferred as List[Any] Just like it would in Scala 2

However the following

val x2 = List(List(1, 2), Vector("a", "b"))

is inferred as List[scala.collection.immutable.AbstractSeq[Int | String]]

This is rather confusing behavior. Why does two disjoint types' LUB get inferred as Any in one case but a union type in another?

If it is just a design decision, are there any such cases that one should be aware of ?

stefanobaghino
  • 11,253
  • 4
  • 35
  • 63
sinanspd
  • 2,589
  • 3
  • 19
  • 37

1 Answers1

9

smarter states

we avoid inferring union types for the same reason we avoid inferring singleton types, because sometimes they're "too precise"

My interpretation of this statement is that it makes more sense to type List(1,2) as List[Int] instead of List[1 | 2], or List(new Cat, new Dog) as List[Animal] instead of List[Cat | Dog].

See also Dmytro's comment from related question (of mine)

Quote from guillaume.martres.me/talks/dotty-tutorial/#/1/13 (slide 15 "Type inference and union types"): "By default, Dotty does not infer union types, they are approximated by a non-union supertype. Union types can be "too precise" and prevent legitimate code from compiling"

Also see mention at 23:38 of the talk "Dotty and types: the story so far".

However widening of union is performed only once to avoid infinite LUB as per smarter:

when we do the widening once, the resulting type might have a union somewhere (like the example in the section Join of a union type in the doc), we won't widen that if we did do the widening recursively, we could get an infinite lub indeed

Mario Galic
  • 47,285
  • 6
  • 56
  • 98
  • 1
    I saw that comment and the video but it didn't quite answer my question to be honest. I think I have some what of an understanding of why they went with the decision of not inferring union types but I am still not clear on why, in your example, `List[Box[Square | Circle]]` inferred as a union? If inferring union types is hard, why wasn't it hard in this case? Did I miss something in the comments or the video? – sinanspd Sep 16 '20 at 21:21
  • @sinanspd It seems widening of union is performed only once to avoid infinite lub as per [smarter](https://gitter.im/lampepfl/dotty?at=5d2dbe9f7dbfff3242ba2dd0): _"if we did do the widening recursively, we could get an infinite lub indeed"_ Also see [Join of a union type](http://dotty.epfl.ch/docs/reference/new-types/union-types-spec.html#join-of-a-union-type) – Mario Galic Sep 16 '20 at 21:26
  • 1
    Thanks Mario! I thought the infinite lub was prevented by intersection types but I guess there is more to that story. Can you Guillaume's comment in the answer so I can accept this? I think that is the most direct answer to this question – sinanspd Sep 16 '20 at 21:34