diff --git a/doc/docs.md b/doc/docs.md index 7060906014..2ac0a79e6e 100644 --- a/doc/docs.md +++ b/doc/docs.md @@ -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. diff --git a/examples/lander.v b/examples/lander.v new file mode 100644 index 0000000000..9fb0d829cc --- /dev/null +++ b/examples/lander.v @@ -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{}) +}