On this page:
if
cond
match

5 Conditionals and Matching🔗ℹ

A conditional form, where the type of test_expr must be Boolean and the types of then_body and else_body must be the same.

> if 1 == 2

  | "no (correct)"

  | "yes (wrong!)"

- String

"yes (wrong!)"

> if 1 == 2

  | "no (correct)"

  | #'oops

typecheck failed: String vs. Symbol

expression

cond

| test_expr: then_body

| ...

| ~else: else_body

 

expression

cond

| test_expr: then_body

| ...

Multi-arm conditional, where test_exprs are evaluated in order until a true result is found, and then the result of the corresponding then_body is returned. If ~else is reached, then the result of else_body is returned. If no ~else case is provided and all test_exprs produce false, an error is reported.

fun count(n):

  cond

  | n == 0: "none"

  | n == 1: "one"

  | ~else: "some"

> count(0)

- String

"none"

> count(2)

- String

"some"

expression

match target_expr

| variant_id(field_id, ...): result_body

| ...

 

expression

match target_expr

| variant_id(field_id, ...): result_body

| ...

| ~else: else_body

 

expression

match target_expr

| []: empty_body

| cons(first_id, rest_id): cons_body

 

expression

match target_expr

| 'pattern': result_body

| ...

 

expression

match target_expr

| 'pattern': result_body

| ...

| ~else: else_body

 

expression

match target_expr

| integer_case: result_body

| ...

 

expression

match target_expr

| integer_case: result_body

| ...

| ~else: else_body

 

integer_case

 = 

integer

 | 

integer || integer_case

Pattern-matching case dispatch on the result of target_expr, either for variants of a type defined with type, for empty or nonempty lists, for syntax patterns, or for integer cases.

The most common use of match is the variant_id form, with or without ~else. All of the variant_ids must be for variants of the same type, and the type of target_expr must match that type. If ~else is not present, every variant associated with the type must have a case. In the unusual case that only ~else is present, the type of target_expr is unconstrained.

type Shape

| circle(radius :: Int)

| rectangle(width :: Int, height :: Int)

fun area(s :: Shape):

  match s

  | circle(r): 3*r*r

  | rectangle(w, h): w*h

> area(circle(1))

- Int

3

> area(rectangle(3, 4))

- Int

12

In the list form of match, the [] and cons clauses can actually be in either order, and a second one can be replaced with ~else.

fun sum(lst :: Listof(Int)) :: Int:

  match lst

  | []: 0

  | cons(f, r): f + sum(r)

> sum([1, 2, 3])

- Int

6

fun max_element(lst :: Listof(Int)) :: Int:

  match lst

  | cons(f, r):

      match r

      | []: f

      | ~else:

          block:

            def r_max = max_element(r)

            if f > r_max | f | r_max

  | []: error(#'max_element, "empty list")

> max_element([1, 20, 3])

- Int

20

In the syntax-pattern form of match, target_expr must produce a syntax object, and it is compared to the quoted patterns until a match is found. A pattern can include an escape with $ followed by an identifier, in which case it binds the identifier to one part of the input syntax or as a repetition for multiple parts. See Syntax Objects, Patterns, and Templates for more information and for examples.

In the integer-case form of match, each clause has an integer or multiple integers separated by ||.

fun describe_quantity(n :: Int):

  match n

  | 0: "none"

  | 1: "single"

  | 2: "pair"

  | 3 || 4 || 5: "some"

  | ~else: "many"

> describe_quantity(1)

- String

"single"

> describe_quantity(4)

- String

"some"