Skip to content

Commit 7f9012d

Browse files
authored
Docs for casing on constants (#7481)
1 parent b559899 commit 7f9012d

File tree

2 files changed

+170
-1
lines changed

2 files changed

+170
-1
lines changed
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
---
2+
sidebar_position: 26
3+
---
4+
5+
# Case-splitting on constants
6+
7+
:::info
8+
9+
This use of `case` in UPLC was introduced with protocol version 11 and requires
10+
[Plutus Core version](../essential-concepts/versions#plutus-core-version)
11+
`1.1.0`.
12+
13+
:::
14+
15+
In UPLC, it is possible to branch on expressions of certain built-in types, like
16+
`Integer` and `Bool`. This can be done with `case` syntax, which is also used
17+
for [sums-of-products](./encoding#sums-of-products). Using `case` on built-in
18+
values may improve script performance and size.
19+
The built-in types that `case` currently supports are:
20+
21+
- `bool`
22+
- `unit`
23+
- `integer`
24+
- `list`
25+
- `pair`
26+
27+
In the future, support for `data` is also planned.
28+
29+
For each type, the allowed branches and their order differs. See [Supported
30+
Types](#supported-types) for more detail.
31+
32+
## Compiling to `case` in Plinth
33+
34+
When compiling Plinth code with the [option](./plinth-compiler-options)
35+
`datatypes=BuiltinCasing` (which in the future be achieved with
36+
`datatypes=SumsOfProducts), many standard library functions will be compiled
37+
into this use of `case`, such as `fstPair`, `ifThenElse` and `caseList`. Note
38+
that Plinth's `case ... of ...` syntax is not necessarily compiled to UPLC, as
39+
it can be more expressive.
40+
41+
## Supported types
42+
43+
### Bool
44+
45+
Booleans can be used in `case` with either one or two branches, where the first
46+
is the `False` branch. Boolean negation can be written for example as:
47+
48+
```uplc
49+
lam b (case b (con bool True) (con bool False))
50+
```
51+
52+
When only a single branch is provided, script execution will fail when the
53+
boolean evaluates to `True`.
54+
55+
Using a single branch is appropriate when the second branch was supposed to fail
56+
already, saving script size.
57+
58+
59+
:::info
60+
61+
When compiling without `datatypes=BuiltinCasing`, Plinth's `ifThenElse` is
62+
compiled into UPLC's `ifThenElse` built-in function, which usually requires more
63+
AST nodes since each branch argument needs to be delayed (function application is
64+
strict), and finally force the chosen branch. This impacts the size and
65+
execution cost.
66+
67+
:::
68+
69+
70+
### Unit
71+
72+
Needs exactly one branch. If the expression being cased on evaluates to a unit
73+
value, evaluation will continue with the expression in that branch. For example,
74+
this expression evaluates to `con integer 5`.
75+
76+
```uplc
77+
case (con unit ()) (con integer 5)
78+
```
79+
80+
### Pair
81+
82+
A built-in pair expects a single branch: a function that takes both components
83+
of the pair.
84+
85+
This example sums the two integer constants in a pair.
86+
87+
```uplc
88+
lam x (case x (lam a (lam b [(builtin addInteger) a b])))
89+
```
90+
91+
:::info
92+
93+
When compiling without `datatypes=BuiltinCasing`, Plinth's `choosePair` is
94+
compiled into multiple built-in function calls to project out the pair's
95+
components, impacting size and execution cost.
96+
97+
:::
98+
99+
### Integer
100+
101+
Casing on integers can be used for non-negative integers only, and a variable
102+
amount of branches may be given. If the expression `e` evaluates to an integer
103+
`i`, the `i`th branch will be evaluated. If there is no branch, `case` will fail.
104+
105+
For example, the following expression evaluates to `con string "c"`
106+
107+
```
108+
case [(builtin addInteger) (con integer 1) (con integer 1)]
109+
(con string "a")
110+
(con string "b")
111+
(con string "c")
112+
```
113+
114+
If the `i`th branch is not given, or `i` is a negative integer, evaluation will
115+
fail:
116+
117+
```
118+
case [(builtin addInteger) (con integer 2) (con integer 2)]
119+
(con string "a")
120+
(con string "b")
121+
(con string "c")
122+
```
123+
124+
Results in
125+
126+
```
127+
An error has occurred:
128+
'case' over a value of a built-in type failed with
129+
'case 4' is out of bounds for the given number of branches: 3
130+
Caused by: 4
131+
```
132+
133+
Note that there is no way to provide a "catch-all" case for integers.
134+
135+
:::info
136+
137+
In Plinth, using `caseInteger` with `datatypes=BuiltinCasing` will be compiled into
138+
the above `case` construct in PIR, provided the second argument is given as a
139+
literal list (otherwise this is a compile error).
140+
141+
When not using `datatypes=BuiltinCasing`, Plinth's `caseInteger` is compiled
142+
into a much less efficient implementation that turns the second argument in a
143+
list (of which the representation depends on the chosen `datatypes=` flag), and
144+
does a recursive lookup in that list.
145+
146+
:::
147+
148+
149+
### List
150+
151+
A `case` on built-in lists may be given one or two branches (similar to
152+
booleans), where the first one deals with the cons case, and the second one with
153+
the empty list. If no second branch is given, execution will fail when the list
154+
turns out to be empty.
155+
156+
This example implements the `head` function, which fails if the list if empty.
157+
158+
```uplc
159+
lam xs (case xs (lam y (lam ys y)))
160+
```
161+
162+
:::info
163+
164+
When compiling without `datatypes=BuiltinCasing`, Plinth's `caseList` is
165+
compiled into a combination of built-in calls such as `chooseList`, `headList`
166+
and `tailList`. Similarly to booleans, the branches are also thunked, impacting
167+
script size and execution cost.
168+
169+
:::

doc/docusaurus/docs/delve-deeper/plinth-compiler-options.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ For each boolean option, you can add a `no-` prefix to switch it off, such as `n
2626
|`coverage-all`|Bool|False|Add all available coverage annotations in the trace output|
2727
|`coverage-boolean`|Bool|False|Add boolean coverage annotations in the trace output|
2828
|`coverage-location`|Bool|False|Add location coverage annotations in the trace output|
29-
|`datatypes`|DatatypeCompilationOpts|SumsOfProducts|Set datatype encoding style|
29+
|`datatypes`|DatatypeCompilationOpts|SumsOfProducts|BuiltinCasing|Set datatype encoding style|
3030
|`defer-errors`|Bool|False|If a compilation error happens and this option is turned on, the compilation error is suppressed and the original Haskell expression is replaced with a runtime-error expression.|
3131
|`dump-compilation-trace`|Bool|False|Dump compilation trace for debugging|
3232
|`dump-pir`|Bool|False|Dump Plutus IR|

0 commit comments

Comments
 (0)