We can nest patterns as deep as we like. Anywhere we put a variable in our pattern, we can put another pattern.
exception BadTriple fun zip3 list_triple = case list_triple of (, , ) =>  | (hd1::tl1, hd2::tl2, hd3::tl3) => (hd1, hd2, hd3)::zip3(tl1, tl2, tl3) | _ => raise BadTriple fun unzip3 lst = case lst of  => (, , ) | (a, b, c)::tl => let val (l1, l2, l3) = unzip3 tl in (a::l1, b::l2, c::l3) end fun is_sorted intlist = case intlist of  => true | _:: => true | head::(neck::rest) => (head <= neck andalso nondecreasing (neck::rest)) a::(b::(c::d)) (* Match lists with 4 or more elements *) a::(b::(c::)) (* Match lists with exactly 3 elements *) (a, b, c)::d (* Match a list of triplets. *)
Note that _ is a wildcard: It matches anything, but does not bind it to anything.
If you ever have a nested case, consider if nested patterns will work instead.