
Scala’s Types of Types - ddispaltro
http://ktoso.github.io/scala-types-of-types/
======
greydius
> A major difference from Java in Scala is, that container types are not-
> variant by default!

Only arrays are variant "by default" in java (which was a huge mistake).
Variance for generic collections is specified at use, not at the type
definition level.

> This means that if you have a container defined as Box[A], and then use it
> with a Fruit in place of the type parameter A, you will not be able to
> insert an Apple (which IS-A Fruit) into it.

No. What this really means is that you can't assign an instance of Box[Apple]
to a val/var of type Box[Fruit].

~~~
thomasahle
The last part makes sense, no? If you could do

List<Fruit> fruits = apples

Then you could add an orange to the fruits list, and it would appear among the
apples.

It doesn't seem like you can fix this without immutability?

~~~
ohnomrbill
If you really did have a case where your list is a list of apples, wouldn't
you type:

List<Apple> apples = aps

If you're using a general type Fruit, and can't stand any fruit that isn't an
apple, you probably need to use the more specific Apple type.

~~~
thomasahle
I mean, the problem of assigning `fruits = aps` is not that you get apples in
your fruit list, but that now elements added to fruit (not of apple type) also
get added to `aps`.

The assignment sort of goes both ways like that.

~~~
ohnomrbill
Ah, your point makes more sense now. Immutability would help a lot here, it
seems.

------
igravious
The following has always had me a bit puzzled, be interested in responses.

If bottom type corresponds to no(thing) and top to any(thing) where does
every(thing) and some(thing) fit in? Is there any semantic distinction between
any(thing) and every(thing)? There should be, right? For me, some(thing) that
had type any(thing) would be an _individual_ but some(thing) that had type
every(thing) {actually the mind boggles a bit here} would be a _colection_,
the collection of all existing or living things. In fact Ruby has the notion
of Objectspace. So we can say things like ObjectSpace.each_object{|x| p x}

~~~
junke
The any type is an "or" of all your types. Any value in your type system
belongs to the top type. The bottom type is a type for which no existing value
belongs to this type.

What would an "everything" type be? I'd guess the "and" of all your types,
meaning "any value that belongs to all (non-bottom?) types" (e.g. an imaginary
0 symbol meaning zero float, empty list, empty string according to the
context). If you want your "every" type to consider bottom type, then the
intersection is empty.

Likewise, the "some" type would be a type containing values that belong to at
least one type, which IMO looks like the "any" type.

~~~
AnimalMuppet
An "everthing" type is impossible. It would not only have to be a float, a
list, a string, but also a tuple, a monad, and _everything_ else. So 0 _might_
work as an empty string, and 1 might work as a string, but 1 wouldn't work as
a dictionary at all, or as a monad, or...

~~~
junke
In that case the everything type is just bottom.

(edit: to be clear, the bottom type, aka zero/empty type).

~~~
pklausler
Bottom's not a type; it's a value that inhabits all types.

~~~
gclaramunt
If you consider subtyping, Bottom is a type IIRC, In the category with types
as objects and subtyping relationship (a->b if b is subtype of a) as
morphisms, Any is the initial object and Bottom is the final object of the
category

~~~
catnaroek
I think normally one considers the opposite category, where arrows go from
subtypes to supertypes, because it can be embedded in the usual category of
types and functions, by considering each arrow as the upcasting function.

------
junke
> if (false) 23 else null

Why isn't the type of this expression Null?

~~~
harveywi
Because the type "Any" is the least upper bound of Int and Null.
[http://ktoso.github.io/scala-types-of-types/#unified-type-
sy...](http://ktoso.github.io/scala-types-of-types/#unified-type-system-any-
anyref-anyval)

~~~
junke
Well, sure, but the over-approximation is surprising given that false is
constant and "if" is supposedly a well-known construct.

~~~
octo_t
thats a very slippery slope.

    
    
      - (0 < 1) is a constant.
      - ((10 + 10)*15 > 0) is a constant.
    

where do you draw the line _in the type checker_ at working these things out?

~~~
adriaanm
Exactly. The type checker does track constants (`false` internally has type
`Boolean(false)`), but it won't drop `if` branches. That happens later, as you
can see in the (abridged) output of `scalac -Xprint:all -Xprint-types`:

    
    
      [[syntax trees at end of                     typer]] // constif.scala
      class ConstIf extends scala.AnyRef {
        def m: Any = if (false{Boolean(false)})
          23{Int(23)}
        else
          null{Null(null)}{Any}
      }
    
      [[syntax trees at end of                 refchecks]] // constif.scala
      class ConstIf extends scala.AnyRef {
        def m: Any = null{Null(null)}
      }

------
catnaroek
Calling those “types of types” is highly misleading. The term “type of types”
has a very precise technical meaning:
[https://ncatlab.org/nlab/show/type+of+types](https://ncatlab.org/nlab/show/type+of+types)

~~~
gclaramunt
Also, kinds can be viewed as the "types" of types, right?

~~~
catnaroek
Sure. If your hierarchy (values, types, etc.) stops at kinds, I'd rather call
them kinds, but even TAPL informally explains kinds as “the types of types”
(p. 441, quotation marks in the original).

