Pattern Matching on Lists

All the ways to pattern match on lists:

match input {
    [] => EmptyList,

    [_, ..] => NonEmptyList,

    ["Hi", ..] => StartsWithHi,

    [.., 42] => EndsWith42,

    [Foo, Bar, ..] => StartsWithFooBar,

    [Foo, Bar, Baz("Hi")] => FooBarBazStr,

    [Foo, Count(num), ..] if num > 0 => FooCountIf,

    [head, .. as tail] => HeadAndTail(head, tail),

    _ => Other,
}

Note: this specific snippet would not typecheck because it uses lists of different types. This is just meant to be a compact overview. See the code section below for valid Roc.

Code

# Match an empty list
expect {
	match_fn = |input|
		match input {
			[] => EmptyList
			_ => Other
		}

	match_fn([]) == EmptyList and match_fn([A, B, C]) == Other
}

# Match a non-empty list
expect {
	match_fn = |input|
		match input {
			[_, ..] => NonEmptyList
			_ => Other
		}

	match_fn([A, B, C]) == NonEmptyList and match_fn([]) == Other
}

# Match a list whose first element is the string "Hi"
expect {
	match_fn = |input|
		match input {
			["Hi", ..] => StartsWithHi
			_ => Other
		}

	match_fn(["Hi", "Hello", "Yo"]) == StartsWithHi and match_fn(["Hello", "Yo", "Hi"]) == Other
}

# Match a list whose last element is the number 42
expect {
	match_fn = |input|
		match input {
			[.., 42] => EndsWith42
			_ => Other
		}

	match_fn([24, 64, 42]) == EndsWith42 and match_fn([42, 1, 5]) == Other
}

# Match a list that starts with a Foo tag
# followed by a Bar tag
expect {
	match_fn = |input|
		match input {
			[Foo, Bar, ..] => StartsWithFooBar
			_ => Other
		}

	match_fn([Foo, Bar, Bar]) == StartsWithFooBar and match_fn([Bar, Bar, Foo]) == Other
}

# Match a list with these exact elements:
# Foo, Bar, and then Baz("Hi")
expect {
	match_fn = |input|
		match input {
			[Foo, Bar, Baz("Hi")] => FooBarBazStr
			_ => Other
		}

	match_fn([Foo, Bar, Baz("Hi")])
		== FooBarBazStr
		and
		match_fn([Foo, Bar])
			== Other
			and
			match_fn([Foo, Bar, Baz("Hi"), Blah])
				== Other
}

# Match a list with Foo as its first element, and
# Count for its second element. Count holds a number,
# and we only match if that number is greater than 0.
expect {
	match_fn = |input|
		match input {
			[Foo, Count(num), ..] if num > 0 => FooCountIf
			_ => Other
		}

	match_fn([Foo, Count(1)])
		== FooCountIf
		and match_fn([Foo, Count(0)])
			== Other
			and match_fn([Baz, Count(1)])
				== Other
}

# Use `as` to create a variable equal to the part of the list that matches `..`
expect {
	match_fn = |input|
		match input {
			[head, .. as tail] => HeadAndTail(head, tail)
			_ => Other
		}

	match_fn([1, 2, 3])
		== HeadAndTail(1, [2, 3])
		and match_fn([1, 2])
			== HeadAndTail(1, [2])
			and match_fn([1])
				== HeadAndTail(1, [])
				and match_fn([])
					== Other
}

main! = |_| Ok({})

Output

Run this from the directory that has PatternMatching.roc in it:

$ roc test PatternMatching.roc

All (8) tests passed in 11.1 ms.