Skip to content

Commit

Permalink
update language guide
Browse files Browse the repository at this point in the history
  • Loading branch information
johnynek committed Dec 11, 2024
1 parent 869c30b commit 4dc0a00
Showing 1 changed file with 42 additions and 44 deletions.
86 changes: 42 additions & 44 deletions docs/src/main/paradox/language_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ Just as we can build up lists literally, we can also use match to tear them down
more later:
```
top_animal = match new_favorites:
[most_fav, *rest]: most_fav
[]: "no favorites, :("
case [most_fav, *rest]: most_fav
case []: "no favorites, :("
```

## Variables
Expand Down Expand Up @@ -74,45 +74,45 @@ addition to user defined types as discussed later.
Here are some examples:
```
result0 = match "foo":
"baz":
case "baz":
"we matched baz"
"foo":
case "foo":
"we matched foo"
_:
case _:
"hmmm, didn't match"
result1 = match "foo":
"f${rest}":
case "f${rest}":
"we start with f followed by ${rest}"
_:
case _:
"hmmm, didn't match"
result2 = match ["foo", "bar"]:
["f${rest}", "baz"]:
case ["f${rest}", "baz"]:
"this won't match"
["f${rest}", *_]:
case ["f${rest}", *_]:
"we start with f followed by ${rest}"
_:
case _:
"hmmm, didn't match"
result3 = match [False, True, True]:
[*_, True, *_]: "at least one is true"
_: "all items are false, or the list is empty"
case [*_, True, *_]: "at least one is true"
case _: "all items are false, or the list is empty"
result4 = match [False, True, True]:
[*_, True, *tail]:
case [*_, True, *tail]:
match tail:
[]: "True with tail empty"
[True]: "True with tail == [True] (this branch matches)"
_: "True with something else"
_: "all items are false, or the list is empty"
case []: "True with tail empty"
case [True]: "True with tail == [True] (this branch matches)"
case _: "True with something else"
case _: "all items are false, or the list is empty"
```

A common shorthand for checking if something matches is:
```
long = match ["foo", "bar"]:
["foo", *_]: True
_: False
case ["foo", *_]: True
case _: False
short = ["foo", "bar"] matches ["foo", *_]
```
Expand Down Expand Up @@ -140,16 +140,14 @@ def add_with_three(x, y):
```
using the `add_Int` function from the `Bosatsu/Predef`. Bosatsu also has anonymous function syntax similar to Haskell. The above is almost exactly the same as:
```
add_with_three = \x, y -> add_Int(add_Int(x, y), 3)
add_with_three = (x, y) -> add_Int(add_Int(x, y), 3)
```
or even

```
add_with_three = x -> y -> add_Int(add_Int(x, y), 3)
```
Think of `\` ans an ASCII version of `λ`.

All of these functions have type `Int -> Int -> Int`, which is to say, given two integers (the two on the left) we can produce a new integer (always! Remember Bosatsu is total, no exceptions!).
The first of these functions has type `(Int, Int) -> Int`, which is to say, given two integers (the two on the left) we can produce a new integer (always! Remember Bosatsu is total, no exceptions!). The second has type `Int -> Int -> Int`, or with parentheses: `Int -> (Int -> Int)`, which means given an `Int` we return a function from `Int -> Int`.

### Scope difference for defs
Unlike normal bindings, defs ARE in scope in their body. However, in order to prevent unbounded
Expand All @@ -169,7 +167,7 @@ The above to forms are equivalent. `foo.bar` means pass `foo` as the first argum

```
inc10 = add(10)
inc10_again = 10.add
inc10_again = 10.add()
```
both of these two values `inc10` and `inc10_again` have the type `Int -> Int` and do the same thing: they add `10` to an integer.

Expand All @@ -179,8 +177,8 @@ terminate. Here is an example:
```
def len(lst):
recur lst:
[]: 0
[_, *tail]: len(tail).add(1)
case []: 0
case [_, *tail]: len(tail).add(1)
```
In the above, we see the `recur` syntax. This is a normal match with two restrictions: 1. it can only be
on a literal parameter of the nearest enclosing def, 2. a function may have at most one recur in a
Expand All @@ -201,8 +199,8 @@ depth is high. If you can, prefer to use tail recursive loops:
def len(lst):
def loop(acc, lst):
recur lst:
[]: acc
[_, *tail]: loop(acc.add(1), tail)
case []: acc
case [_, *tail]: loop(acc.add(1), tail)
loop(0, lst)
```
Expand Down Expand Up @@ -249,19 +247,19 @@ my_file_rec = File { name, modified_time: 1540951025, size, hash }
There are no methods in bosatsu, only functions. We cannot define methods on `struct`s or `enum`s. The only thing we can do with a struct or enum is match it:
```
nm = match my_file:
File(name, _, _, _): name
case File(name, _, _, _): name
# same as the above but ignoring trailing fields
nm = match my_file:
File(name, ...): name
case File(name, ...): name
# same as the above but using records
nm = match my_file:
File { name: n, ... }: n
case File { name: n, ... }: n
# same, but omit the colon to bind the field name
nm = match my_file:
File { name, ... }: name
case File { name, ... }: name
```

All matches in Bosatsu are total: one of the branches must match.
Expand Down Expand Up @@ -333,7 +331,7 @@ x = (1, "2", ["three"])
(a, b, c) = x
# or we can use match syntax if that is more appropriate
match x:
(x, y, z): do_something(x, y, z)
case (x, y, z): do_something(x, y, z)
```

### Enums
Expand Down Expand Up @@ -367,16 +365,16 @@ Here are some examples of pattern matches:
enum Either: Left(a), Right(b)
match x:
Left(Left(Left(_)) | Right(_)): 0
Left(Left(Right(_))): 1
Right(_): 2
case Left(Left(Left(_)) | Right(_)): 0
case Left(Left(Right(_))): 1
case Right(_): 2
match y:
Left(Left(x)) | Right(x):
case Left(Left(x)) | Right(x):
# here we require that the bindings in the left and right sides of
# the union are the same and that they have the same type
Some(x)
Left(Right(_)): None
case Left(Right(_)): None
# if we can write a match in a single arm, we can write it as a binding:
Left(x) | Right(x) = y
Expand Down Expand Up @@ -409,8 +407,8 @@ from Animals/Favorites import mammals
export most_fav
most_fav = match mammals:
[head, *tail]: head
[]: "who knows?"
case [head, *tail]: head
case []: "who knows?"
```

Expand All @@ -433,8 +431,8 @@ def someO(a): OSome(a)
def foldO(oo, if_none, if_some):
match oo:
ONone: if_none
oSome(a): if_some(a)
case ONone: if_none
case oSome(a): if_some(a)
```
Here ClearOption exports all its constructors so they can be pattern matched upon. In the OpaqueOption example, we export functions to create the opaque option, and fold function to tear it down, but we can't explicitly pattern match on the types.
Expand All @@ -447,7 +445,7 @@ gives the user a chance to violate totality. Use with caution.

An example function we cannot implement in bosatsu is:
```
def int_loop(int_v: Int, state: a, fn: Int -> a -> (Int, a)) -> a:
def int_loop(int_v: Int, state: a, fn: (Int, a) -> (Int, a)) -> a:
if cmp_Int(int_v, 0) matches GT:
(next_i, next_state) = fn(int_v, state)
if cmp_Int(next_i, int_v) matches LT:
Expand All @@ -465,7 +463,7 @@ of inputs in the same position. This gives a simple proof that the loop will ter
Instead, we implement this function in Predef as an external def that has to be supplied to the
compiler with a promise that it is indeed total.
```
external def int_loop(intValue: Int, state: a, fn: Int -> a -> Tuple2[Int, a]) -> a
external def int_loop(intValue: Int, state: a, fn: (Int, a) -> Tuple2[Int, a]) -> a
```

External values and types work exactly like internally defined types from any other point of view.
Expand Down

0 comments on commit 4dc0a00

Please sign in to comment.