8.18

7.5 List Annotations🔗ℹ

As explained in Lists, the List annotation matches any list, while List.of matches a list whose elements all satisfy another annotation.

> [1, "two", #'three] is_a List

#true

> [1, "two", #'three] is_a List.of(String)

#false

> ["1", "two", "three"] is_a List.of(String)

#true

The matching form can create an annotation that matches a list with a more specific shape, such as one that has an integer as its first element and a string as is second element. A List.tuple_of annotation is a kind of shorthand for a matching annotation with a list pattern and _ wildcards. Using [] directly for an annotation (i.e., #%brackets) is an even more compact form that it is equivalent to using List.tuple_of.

> [1, "two"] is_a matching([_ :: Int, _ :: String])

#true

> [1, "two"] is_a List.tuple_of[Int, String]

#true

> [1, "two"] is_a [Int, String]

#true

> ["two", 1] is_a [Int, String]

#false

A List.tuple_of or [] annotation can end with ... to indicate zero or more elements that match the preceding annotation (but, unlike a list List pattern in matching, the ... is allowed only at the end of the annotation sequence). This means that [String, ...] is another way to write List.of(String), for example.

> ["1", "two"] is_a List.of(String)

#true

> ["1", "two"] is_a [String, ...]

#true

> [1, "two", "three"] is_a [Int, String, ...]

#true

A List.tuple_of or [] annotation supplies element-specific static information, which is the sense in which it corresponds to a tuple. A tuple-style list annotation can be an alternative to values, for example, that also works in contexts where values is not allowed.

> use_static

> fun enumerated(s :: String, ...) :~ [[Int, String], ...]:

    for List (s in [s, ...],

              i in 0 ..):

      [i, s]

> def strs = enumerated("a", "bb", "cccc")

> strs

[[0, "a"], [1, "bb"], [2, "cccc"]]

> def [[i0, str0], [i1, str1], [i2, str2]] = strs

> str0.length() // static call to String.length

1

> str2.length()

4

A list construction using List or [] propagates element-specific, tuple-style static information to pattern matching, but the List.get or the [] indexing operation (i.e., #%index) is not statically sensitive to a literal index.

> use_static

> class Posn(x, y):

    nonfinal

> class Posn3D(z):

    extends Posn

> def ps && [p0, p1, p2] = [Posn(1, 2),

                            Posn3D(4, 5, 6),

                            Posn(7, 8)]

> p0.x

1

> p1.z

6

> p0.z

z: no such field or method (based on static information)

> ps[1].x // because all elements are Posns

4

> ps[1].z // not all elements are Posn3Ds

z: no such field or method (based on static information)