arliss_obsidian/fp/Language/Functions/Defining/Guard Clause.md
Orion Kindel 3701b1e2f8
update
2024-09-23 18:56:55 -05:00

56 lines
2.0 KiB
Markdown

Single [[Functions|Functions]] [[Defining/Pattern Matching|implementations]] may have 1 or more guard clauses.
> [!info]
> [[case .. of]] expressions can also use guard clauses.
These allow functions to switch on boolean expressions based on the inputs, and can often take up less space than explicit [[Expressions/if .. then .. else ..]] expressions.
Guard patterns are placed after the function arguments and take the form `| <Boolean expr> = <body>`. There can be any number of guard patterns, as long as all cases are exhaustively covered.
### Example
Strings can't be structurally pattern matched, so in order to ask if a string starts with a substring we need to call a function like `Data.String.Utils.startsWith`.
We could do this with `if then else`:
```haskell
ensureLeadingSlash :: String -> String
ensureLeadingSlash str =
if String.Util.startsWith "/" then
str
else
"/" <> str
```
Alternatively, we could implement this with guard patterns:
```haskell
ensureLeadingSlash :: String -> String
ensureLeadingSlash str
| String.Util.startsWith "/" str = str
| otherwise = "/" <> str
```
When the first pattern `String.Util.startsWith "/" str` returns `true`, the function will use `... = str`. Otherwise, it will prepend `/` to `str`.
> [!tip]
> `otherwise` is simply an alias for `true`, specifically for better-reading fallthrough guard patterns.
Much less often, but occasionally useful is the ability to structurally pattern match in a guard clause:
```haskell
ensureLeadingSlashM :: Maybe String -> String
ensureLeadingSlashM mstr
| Just s <- mstr, String.Util.startsWith "/" s = s
| otherwise = ""
```
Here the bit `Just s <- mstr` is saying "when `mstr` is `Just s`..."
then `String.Util.startsWith "/" s` says "and `s` starts with `"/"`..."
`| Just s <- mstr, startsWith "/" s = s`
"when `mstr` is `Just s` and `s` starts with `"/"`, return s"
`| otherwise = ""`
otherwise return `""`
You can have any number of comma-separated guard bindings before the last boolean expression.