List patterns

A list pattern matches a List t for some type t and has one of four forms:

  1. head List.+: tail matches a list with at least one element. The pattern head is matched against the first element of the list and tail is matched against the suffix of the list with the first element removed.
  2. init List.:+ last matches a list with at least one element. The pattern init is matched against the prefix of the list with the last element removed, and last is matched against the last element of the list.
  3. A literal list pattern has the form [p1, p2, ā€¦ pn] where p1 through pn are patterns. The patterns p1 through pn are matched against the elements of the list. This pattern only matches if the length of the scrutinee is the same as the number of elements in the pattern. The pattern [] matches the empty list.
  4. part1 List.++ part2 matches a list which composed of the concatenation of part1 and part2. At least one of part1 or part2 must be a pattern with a known list length, otherwise it's unclear where the list is being split. For instance, [x, y] List.++ rest is okay as is start List.++ [x, y], but just a ++ b is not allowed.

Examples:

first : [a] -> Optional a
first = cases
  h +: _ -> Some h
  []     -> None
last : [a] -> Optional a
last = cases
  _ :+ l -> Some l
  []     -> None
exactlyOne : [a] -> Boolean
exactlyOne = cases
  [_] -> true
  _   -> false
lastTwo : [a] -> Optional (a, a)
lastTwo = cases
  start ++ [a, a2] -> Some (a, a2)
  _                -> None
firstTwo : [a] -> Optional (a, a)
firstTwo = cases
  [a, a2] ++ rest -> Some (a, a2)
  _               -> None