Cypher tries to be type safe within a query. That means that it tries to stop
you from building nonsensical queries, e.g. a pattern where an identifier is
used first as a pattern path and then a pattern node. MATCH n = n-->()

To do this, it has a class tree hierarchy of types, with Any as the root of
it all.

    String  Double  Long  Node  Rel  Bool
       \         \   /      \   /     /
        \       Number       Map     /
         \            \      /      /
          \------------Scalar------/   Iterable<T>
                            \           /
                             \         /
                              ---Any---

All expressions have a type. Some expressions type depend on other expressions types -
e.g. LAST(x) will have type T, given that x has type Iterable<T>.

An expression can also have an expectation about inner expressions type, LENGTH(X)
expects that X is an Iterable<T>. These expectations are met by any subtype of the
expected type - e.g. if the expectation is a Number, Double and Long are fine, but
not a Scalar expression.

Lastly, an expression has dependencies on what identifiers exists in the symbol table,
and their types. An example is always in place:

+`LENGTH(FILTER(n in nodes(p) : n.prop = 'foo'))`+

In this expression, LENGTH has an expectation on FILTER, and FILTER has one on NODES,
and so one. But the dependency on identifiers on the symbol table is just one - it has
to have an identifier named 'p', and it's type is expected to be an
Iterable<Map> (PathType is a shortcut to this in the code).