Skip to content

Commit

Permalink
document enums
Browse files Browse the repository at this point in the history
  • Loading branch information
Akuli committed Jan 10, 2025
1 parent baa063b commit 59fc3fb
Showing 1 changed file with 180 additions and 0 deletions.
180 changes: 180 additions & 0 deletions doc/enums.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
# Enums

TL;DR:

```python
import "stdlib/io.jou"

enum Foo:
Bar
Baz

def main() -> int:
thing = Foo::Bar

if thing == Foo::Bar:
printf("It's bar\n") # Output: It's bar
elif thing == Foo::Baz:
printf("It's baz\n")
else:
assert False # never happens

printf("%d\n", Foo::Bar as int) # Output: 0
printf("%d\n", Foo::Baz as int) # Output: 1

return 0
```


## Introduction

Let's say you are working on a calculator program,
and you have something like this:

```python
import "stdlib/io.jou"

def calculate(a: double, b: double, op: byte) -> double:
if op == '+':
return a + b
elif op == '-':
return a - b
elif op == '*':
return a * b
else:
assert False # unsupported operation

def main() -> int:
printf("%f\n", calculate(1, 2, '+')) # Output: 3.000000
return 0
```

Here `assert False` aborts the execution of the program
with an error message that contains the file name and line number.
This is useful when some part of the code should never run.
Here, if you leave it out, you will get a compiler warning,
because then the `calculate()` function doesn't return a value in all cases.

Now, suppose you need to add a division feature to the calculator.
The most common ways to do division are:
- "Normal" division: `7 / 3 = 2.33333...`
- Floor division: `7 / 3 = 2` (result rounded down)

It is tempting to use the `'/'` byte for normal division, but which byte to use for floor division?
Maybe `'f'` for floor?
If you ask any experienced programmer, they will probably tell you to use enums.
Here's how it works in Jou:

```python
import "stdlib/io.jou"
import "stdlib/math.jou"

enum Operation:
Add
Subtract
Multiply
Divide # 7 / 3 produces 2.3333...
FloorDivide # 7 / 3 produces 2

def calculate(a: double, b: double, op: Operation) -> double:
if op == Operation::Add:
return a + b
if op == Operation::Subtract:
return a - b
if op == Operation::Multiply:
return a * b
if op == Operation::Divide:
return a / b
if op == Operation::FloorDivide:
return floor(a / b)
else:
assert False # not possible

def main() -> int:
printf("%f\n", calculate(7, 3, Operation::Divide)) # Output: 2.333333
printf("%f\n", calculate(7, 3, Operation::FloorDivide)) # Output: 2.000000
return 0
```

Here `enum Operation` defines a new enum, which has 5 possible values.
You can then use `if` statements to check which value an instance of `Operation` is.

Enums members are accessed with `::` instead of `.`,
because it was easier to implement in the Jou compiler.
Please create an issue on GitHub if you would prefer `.` syntax instead.


## Integer conversions

Jou enums are actually just fancy integers.
You can use `as` to convert them to integers and back:

```python
import "stdlib/io.jou"

enum Operation:
Add
Subtract
Multiply

def main() -> int:
printf("%d\n", Operation::Add as int) # Output: 0
printf("%d\n", Operation::Subtract as int) # Output: 1
printf("%d\n", Operation::Multiply as int) # Output: 2
return 0
```

This is sometimes used to assign a string to each enum member:

```python
import "stdlib/io.jou"

enum Operation:
Add
Subtract
Multiply

def main() -> int:
descriptions = ["Add numbers", "Subtract numbers", "Multiply numbers"]
printf("%s\n", descriptions[Operation::Subtract as int]) # Output: Subtract numbers
return 0
```

You can also convert integers to enums,
but note that the result might not correspond with any member of the enum:

```python
import "stdlib/io.jou"

enum Operation:
Add
Subtract
Multiply

def main() -> int:
wat = 7 as Operation

# Output: something else 7
if wat == Operation::Add:
printf("Add\n")
elif wat == Operation::Subtract:
printf("Subtract\n")
elif wat == Operation::Multiply:
printf("Multiply\n")
else:
printf("something else %d\n", wat)

return 0
```

Note that the `printf()` function sees the enum as if it was an integer,
so you don't need to do `wat as int` when printing the integer value with `%d`.


## Debugging

Unfortunately, it is not possible to print the name of an enum member at runtime.
You can however print its numeric value with printf's `%d` specifier (see above).
Please create an issue on GitHub to discuss this if this annoys you.


0 comments on commit 59fc3fb

Please sign in to comment.