{
"cells": [
{
"cell_type": "markdown",
"id": "e1cf5db3",
"metadata": {},
"source": [
"# Advanced Pattern Matching\n",
"\n",
"Here are some additional pattern forms that are useful:\n",
"\n",
"* `p1 | ... | pn`: an \"or\" pattern; matching against it succeeds if a match\n",
" succeeds against any of the individual patterns `pi`, which are tried in order\n",
" from left to right. All the patterns must bind the same variables.\n",
"\n",
"* `(p : t)`: a pattern with an explicit type annotation.\n",
"\n",
"* `c`: here, `c` means any constant, such as integer literals, string literals,\n",
" and booleans.\n",
"\n",
"* `'ch1'..'ch2'`: here, `ch` means a character literal. For example, `'A'..'Z'`\n",
" matches any uppercase letter.\n",
"\n",
"* `p when e`: matches `p` but only if `e` evaluates to `true`.\n",
"\n",
"You can read about [all the pattern forms][patterns] in the manual.\n",
"\n",
"[patterns]: https://ocaml.org/manual/patterns.html\n",
"\n",
"## Pattern Matching with Let\n",
"\n",
"The syntax we've been using so far for let expressions is, in fact, a special\n",
"case of the full syntax that OCaml permits. That syntax is:\n",
"```ocaml\n",
"let p = e1 in e2\n",
"```\n",
"That is, the left-hand side of the binding may in fact be a pattern, not just an\n",
"identifier. Of course, variable identifiers are on our list of valid patterns,\n",
"so that's why the syntax we've studied so far is just a special case.\n",
"\n",
"Given this syntax, we revisit the semantics of let expressions.\n",
"\n",
"**Dynamic semantics.**\n",
"\n",
"To evaluate `let p = e1 in e2`:\n",
"\n",
"1. Evaluate `e1` to a value `v1`.\n",
"\n",
"2. Match `v1` against pattern `p`. If it doesn't match, raise the exception\n",
" `Match_failure`. Otherwise, if it does match, it produces a set $b$ of\n",
" bindings.\n",
"\n",
"3. Substitute those bindings $b$ in `e2`, yielding a new expression `e2'`.\n",
"\n",
"4. Evaluate `e2'` to a value `v2`.\n",
"\n",
"5. The result of evaluating the let expression is `v2`.\n",
"\n",
"**Static semantics.**\n",
"\n",
"* If all the following hold then `(let p = e1 in e2) : t2`:\n",
"\n",
" - `e1 : t1`\n",
"\n",
" - the pattern variables in `p` are `x1..xn`\n",
"\n",
" - `e2 : t2` under the assumption that for all `i` in `1..n` it holds that\n",
" `xi : ti`,\n",
"\n",
"**Let definitions.**\n",
"\n",
"As before, a let definition can be understood as a let expression whose body has\n",
"not yet been given. So their syntax can be generalized to\n",
"```ocaml\n",
"let p = e\n",
"```\n",
"and their semantics follow from the semantics of let expressions, as before.\n",
"\n",
"## Pattern Matching with Functions\n",
"\n",
"The syntax we've been using so far for functions is also a special case of the\n",
"full syntax that OCaml permits. That syntax is:\n",
"```ocaml\n",
"let f p1 ... pn = e1 in e2 (* function as part of let expression *)\n",
"let f p1 ... pn = e (* function definition at toplevel *)\n",
"fun p1 ... pn -> e (* anonymous function *)\n",
"```\n",
"\n",
"The truly primitive syntactic form we need to care about is `fun p -> e`. Let's\n",
"revisit the semantics of anonymous functions and their application with that\n",
"form; the changes to the other forms follow from those below:\n",
"\n",
"**Static semantics.**\n",
"\n",
"* Let `x1..xn` be the pattern variables appearing in `p`. If by assuming that\n",
" `x1 : t1` and `x2 : t2` and ... and `xn : tn`, we can conclude that `p : t`\n",
" and ` e :u`, then `fun p -> e : t -> u`.\n",
"\n",
"* The type checking rule for application is unchanged.\n",
"\n",
"**Dynamic semantics.**\n",
"\n",
"* The evaluation rule for anonymous functions is unchanged.\n",
"\n",
"* To evaluate `e0 e1`:\n",
"\n",
" 1. Evaluate `e0` to an anonymous function `fun p -> e`, and\n",
" evaluate `e1` to value `v1`.\n",
"\n",
" 2. Match `v1` against pattern `p`. If it doesn't match, raise the exception\n",
" `Match_failure`. Otherwise, if it does match, it produces a set $b$ of\n",
" bindings.\n",
"\n",
" 3. Substitute those bindings $b$ in `e`, yielding a new expression `e'`.\n",
"\n",
" 4. Evaluate `e'` to a value `v`, which is the result of evaluating `e0 e1`.\n",
"\n",
"## Pattern Matching Examples\n",
"\n",
"{{ video_embed | replace(\"%%VID%%\", \"3ExRHHqfWm4\")}}\n",
"\n",
"Here are several ways to get a Pokémon's hit points:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5761e406",
"metadata": {},
"outputs": [],
"source": [
"(* Pokemon types *)\n",
"type ptype = TNormal | TFire | TWater\n",
"\n",
"(* A record to represent Pokemon *)\n",
"type mon = { name : string; hp : int; ptype : ptype }\n",
"\n",
"(* OK *)\n",
"let get_hp m = match m with { name = n; hp = h; ptype = t } -> h\n",
"\n",
"(* better *)\n",
"let get_hp m = match m with { name = _; hp = h; ptype = _ } -> h\n",
"\n",
"(* better *)\n",
"let get_hp m = match m with { name; hp; ptype } -> hp\n",
"\n",
"(* better *)\n",
"let get_hp m = match m with { hp } -> hp\n",
"\n",
"(* best *)\n",
"let get_hp m = m.hp"
]
},
{
"cell_type": "markdown",
"id": "ac451e15",
"metadata": {},
"source": [
"Here's how to get the first and second components of a pair:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2e2c1e5c",
"metadata": {},
"outputs": [],
"source": [
"let fst (x, _) = x\n",
"\n",
"let snd (_, y) = y"
]
},
{
"cell_type": "markdown",
"id": "d888c824",
"metadata": {},
"source": [
"Both `fst` and `snd` are actually already defined for you in the standard\n",
"library.\n",
"\n",
"Finally, here are several ways to get the 3rd component of a triple:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "53ad4e64",
"metadata": {},
"outputs": [],
"source": [
"(* OK *)\n",
"let thrd t = match t with x, y, z -> z\n",
"\n",
"(* good *)\n",
"let thrd t =\n",
" let x, y, z = t in\n",
" z\n",
"\n",
"(* better *)\n",
"let thrd t =\n",
" let _, _, z = t in\n",
" z\n",
"\n",
"(* best *)\n",
"let thrd (_, _, z) = z"
]
},
{
"cell_type": "markdown",
"id": "87be01bd",
"metadata": {},
"source": [
"The standard library does not define any functions for triples, quadruples, etc."
]
}
],
"metadata": {
"jupytext": {
"cell_metadata_filter": "-all",
"formats": "md:myst",
"text_representation": {
"extension": ".md",
"format_name": "myst",
"format_version": 0.13,
"jupytext_version": "1.10.3"
}
},
"kernelspec": {
"display_name": "OCaml",
"language": "OCaml",
"name": "ocaml-jupyter"
},
"source_map": [
14,
131,
152,
155,
159,
164,
180
]
},
"nbformat": 4,
"nbformat_minor": 5
}