Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Voi-92] Basic type casting via match statements #32

Merged
merged 14 commits into from
Sep 4, 2024
23 changes: 13 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,17 +240,20 @@ for item in iterable

### Match Statements

Match statements are used for type narrowing

```rust
let x = 3
match x
1 => print "One"
2 => print "Two"
3 => print "Three"
_ =>
// Match statements are exhaustive, they must cover every possible
// case. When not every case is covered, a default handler must be
// provided.
write "A number"
obj Animal
obj Cat extends Animal
obj Dog extends Animal

let dog = Dog {}

match(dog)
Dog: print "Woof"
Cat: print "Meow"
else:
print "Blurb"
```

## Closures
Expand Down
46 changes: 46 additions & 0 deletions reference/control-flow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Control Flow

## Match

Used to narrow types. Can operate on object to narrow
child objects or on unions to narrow union members

Signature(s):
```
fn match<T extends Object, U>(val: T, body: MatchBlock) -> U
fn match<T extends Object, U>(val: T, bind_identifier: Identifier, body: MatchBlock) -> U
```

Example:
```void
obj Optional

obj None extends Optional

obj Some extends Optional {
value: i32
}

fn divide(a: i32, b: i32) -> Optional
if b == 0
None {}
else:
Some { value: a / b }

fn main(a: i32, b: i32) -> String
let x = a.divide(b)
match(x)
Some: "The value is ${x}"
None: "Error: divide by zero"
else: "Bleh"
```

The second signature of match is useful when the value being matched against
is not already bound to an identifier (i.e. dot pipelines):
```void
fn main(a: i32, b: i32) -> String
a.divide(b)
.match(x) // Here, match binds the result of the previous expression to x
Some: "The value is ${x}"
None: "Error: divide by zero"
```
26 changes: 26 additions & 0 deletions reference/types/objects.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,32 @@ obj Cat extends Animal {
}
```

## Object Type Narrowing

```void
obj Optional

obj None extends Optional

obj Some extends Optional {
value: i32
}

fn divide(a: i32, b: i32) -> Optional
if b == 0
None { }
else:
Some { value: a / b }

fn main(a: i32, b: i32)
a.divide(b)
.match(x)
Some:
log "The value is ${x}"
None:
log "Error: divide by zero"
```

# Traits

Traits are first class types that define the behavior of a nominal object.
Expand Down
6 changes: 6 additions & 0 deletions src/__tests__/compiler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,19 @@ describe("E2E Compiler Pipeline", () => {
const test2 = getWasmFn("test2", instance);
const test3 = getWasmFn("test3", instance);
const test4 = getWasmFn("test4", instance);
const test5 = getWasmFn("test5", instance);
const test6 = getWasmFn("test6", instance);
assert(test1, "Test1 exists");
assert(test2, "Test2 exists");
assert(test3, "Test3 exists");
assert(test4, "Test4 exists");
assert(test5, "Test3 exists");
assert(test6, "Test4 exists");
t.expect(test1(), "test 1 returns correct value").toEqual(13);
t.expect(test2(), "test 2 returns correct value").toEqual(1);
t.expect(test3(), "test 3 returns correct value").toEqual(2);
t.expect(test4(), "test 4 returns correct value").toEqual(52);
t.expect(test5(), "test 5 returns correct value").toEqual(21);
t.expect(test6(), "test 6 returns correct value").toEqual(-1);
});
});
22 changes: 21 additions & 1 deletion src/__tests__/fixtures/e2e-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ obj Pointy extends Vec {
z: i32
}

obj Bitly extends Vec {
x: i32,
y: i32,
z: i32
}

fn get_x(vec: Vec)
vec.x

Expand All @@ -44,6 +50,12 @@ fn get_member(vec: Point)
fn get_member(vec: Pointy)
vec.x

fn get_num_from_vec_sub_obj(vec: Vec)
match(vec)
Pointy: get_member(vec)
Point: get_member(vec)
else: -1

// Should return 13
pub fn test1()
let vec = Point { x: 1, y: 2, z: 13 }
Expand All @@ -59,10 +71,18 @@ pub fn test3()
let vec = Vec { x: 1, y: 2 }
vec.get_member()


// Should return 52
pub fn test4()
let vec = Point { x: 52, y: 2, z: 21 }
vec.get_x()

// Test match type guard (Point case), should return 21
pub fn test5()
let vec = Point { x: 52, y: 2, z: 21 }
get_num_from_vec_sub_obj(vec)

// Test match type guard (else case), should return -1
pub fn test6()
let vec = Bitly { x: 52, y: 2, z: 21 }
get_num_from_vec_sub_obj(vec)
`;
Loading