doc: update sum type docs (#5702)

pull/5710/head
Nick Treleaven 2020-07-06 17:09:38 +01:00 committed by GitHub
parent 9a4d989188
commit 9e7ba5f138
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 121 additions and 47 deletions

View File

@ -1196,71 +1196,85 @@ println(color) // "1" TODO: print "green"?
### Sum types
A sum type instance can hold a value of several different types. Use the `type`
keyword to declare a sum type:
```v
type Expr = BinaryExpr | UnaryExpr | IfExpr
struct Moon {}
struct Mars {}
struct Venus {}
struct BinaryExpr{ ... }
struct UnaryExpr{ ... }
struct IfExpr{ ... }
type World = Moon | Mars | Venus
fn (mut p Parser) expr(precedence int) Expr {
match p.tok {
.key_if { return IfExpr{} }
...
else { return BinaryExpr{} }
}
}
sum := World(Moon{})
```
fn gen(expr Expr) {
match expr {
IfExpr { gen_if(expr) } // `expr` is cast to the matched type automatically
...
}
}
To check whether a sum type instance holds a certain type, use `sum is Type`.
To cast a sum type to one of its variants you use `sum as Type`:
fn gen_if(expr IfExpr) {
...
```v
fn (m Mars) dust_storm() bool
fn main() {
mut w := World(Moon{})
assert w is Moon
w = Mars{}
// use `as` to access the Mars instance
mars := w as Mars
if mars.dust_storm() {
println('bad weather!')
}
}
```
To check whether a sum type is a certain type, use `is`:
You can also use `match` to determine the variant:
```v
println(expr is IfExpr)
fn open_parachutes(n int)
fn land(w World) {
match w {
Moon {} // no atmosphere
Mars {
// light atmosphere
open_parachutes(3)
}
Venus {
// heavy atmosphere
open_parachutes(1)
}
}
}
```
To cast a sum type to one of its variants you use `as`:
`match` must have a pattern for each variant or have an `else` branch.
```v
bin_expr := expr as BinaryExpr
```
You can also use match to determine the variant and cast to it at the same time.
There are 3 ways to access the cast variant inside a match branch:
- the `it` variable
There are 2 ways to access the cast variant inside a match branch:
- the shadowed match variable
- using `as` to specify a variable name
```v
fn binary_expr(bx BinaryExpr) {...}
fn unary_expr(ux UnaryExpr) {...}
fn if_expr(ix IfExpr) {...}
fn (m Moon) moon_walk()
fn (m Mars) shiver()
fn (v Venus) sweat()
// using `it`
match expr {
BinaryExpr { binary_expr(it) }
...
}
// using the shadowed variable, in this case `expr`
match expr {
UnaryExpr { unary_expr(expr) }
...
}
// using `as` to specify a variable
match expr as actual_expr {
IfExpr { if_expr(actual_expr) }
...
}
fn pass_time(w World) {
match w {
// using the shadowed match variable, in this case `w`
Moon { w.moon_walk() }
Mars { w.shiver() }
else {}
}
// using `as` to specify a variable name
match w as expr {
Venus { expr.sweat() }
else {
// w is of type World
assert w !is Venus
}
}
}
```
Note: shadowing only works when the match expression is a variable. It will not work on struct fields, arrays indexing, or map key lookup.

60
examples/lander.v 100644
View File

@ -0,0 +1,60 @@
// Example of sum types
// Models a landing craft leaving orbit and landing on a world
import rand
import time
struct Moon {
}
struct Mars {
}
fn (m Mars) dust_storm() bool {
return rand.int() >= 0
}
struct Venus {
}
type World = Moon | Mars | Venus
struct Lander {
}
fn (l Lander) deorbit() {
println('leaving orbit')
}
fn (l Lander) open_parachutes(n int) {
println('opening $n parachutes')
}
fn wait() {
println('waiting...')
time.sleep(1)
}
fn (l Lander) land(w World) {
if w is Mars {
m := w as Mars
for m.dust_storm() {
wait()
}
}
l.deorbit()
match w {
Moon {} // no atmosphere
Mars {
// light atmosphere
l.open_parachutes(3)
}
Venus {
// heavy atmosphere
l.open_parachutes(1)
}
}
println('landed')
}
fn main() {
l := Lander {}
l.land(Venus{})
l.land(Mars{})
}