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

Lazy invariant following argument type #640

Open
bvssvni opened this issue Sep 17, 2019 · 4 comments
Open

Lazy invariant following argument type #640

bvssvni opened this issue Sep 17, 2019 · 4 comments

Comments

@bvssvni
Copy link
Member

bvssvni commented Sep 17, 2019

This feature was designed to help with #635.

A lazy invariant is a value that the functions returns immediately when the argument has the value. This makes it unnecessary to evaluate the rest of the arguments. When a lazy invariant is specified, the runtime checks the argument value before evaluating the rest of the arguments and calling the function. If the value matches the lazy variant, the runtime returns the value immediately.

Example (pseudocode):

use std as std

fn and(a: bool => false, b: bool) -> bool {return std::and_also(a, b)}

This makes the and function behave exactly like the && operator.

One can use lazy invariants on other types than bool and on multiple arguments:

Example:

use std as std

fn mul(a: f64 => 0, b: f64 => 0) -> f64 {return std::mul(a, b)}

When either a or b is 0, the runtime returns 0 immediately instead of calling the function and evaluating the function body. However, the argument a is evaluated before b.

Lazy invariants improve performance when evaluating arguments or function body is expensive.

// The intersection of two sets returns `[]` immediately if either set is empty.
fn intersect(a: [] => [], b: [] => []) -> [] { ... }

A list of lazy invariants can be used to handle operations on dynamical types, e.g:

fn concat(a: any => [] | "", b: any => [] | "") -> any { ... }

Normal arguments might therefore be considered lazy invariants with length zero.

The syntax ok(_), err(_), some(_) can be used to unwrap automatically without evaluating the rest of the arguments. The syntax _ is used to return the argument.

For example, the unwrap_or function is be declared such that the default value is lazy evaluated:

fn unwrap_or(var: any => ok(_) | some(_), def: any) -> any { ... }
@bvssvni
Copy link
Member Author

bvssvni commented Sep 17, 2019

One idea is to extend this further, to support conditional evaluation based on previous arguments.

fn if(cond: bool, tr: 'return any => if cond, fa: 'return any => if !cond) -> any {
    if cond {return tr} else {return fa}
}

@bvssvni
Copy link
Member Author

bvssvni commented Sep 17, 2019

More ideas on the use of _:

fn id(a: any => _) -> any { ... }
fn first(a: [] => [_, ...]) -> any { ... }
fn second(a: [] => [.., _, ...]) -> any { ... }
fn third(a: [] => [.., .., _, ...]) -> any { ... }
fn last(a: [] => [..., _]) -> any { ... }
fn second_last(a: [] => [..., _, ..]) -> any { ... }
fn foo(a: {} => {bar: _, ...}) -> any { ... }

@bvssvni
Copy link
Member Author

bvssvni commented Sep 17, 2019

One might also check equality with previous arguments:

fn foo(a: f64, b: => [a, _]) -> any { ... }

This returns the second item in the list of b if the first item is equal to a.

@bvssvni
Copy link
Member Author

bvssvni commented Sep 17, 2019

Here is a version that borrows syntax from path semantics:

// Returns `b` if it is greater than `a`.
fn foo(a: f64, b: any => (> a)) -> any { ... }
// Returns `b` if `f(b) == a`.
fn foo(f: \(any) -> any, a: any, b: any => [f] a) -> any { ... }
// Returns second item of `b` if it is an array of 2 elements
// and the first is equal to `a`.
fn foo(a: f64, b: any => [(= a), _]) -> any { ... }

bvssvni added a commit to bvssvni/dyon that referenced this issue Sep 22, 2019
See PistonDevelopers#640

- Use fake grab expressions to precompute lazy invariant values
- Added lazy invariant fake grap expressions
- Added lazy invariant `ok(_)` pattern
- Added lazy invariant `err(_)` pattern
- Added lazy invariant `some(_)` pattern
- Added test for lazy invariant
- Removed `Expression::BinOp`
- Added `ast::Lazy`
- Added `ast::Arg::lazy`
- Added `FnIndex::ExternalLazy`
- Removed `BinOpExpression::resolve_locals`
- Made `and_also` standard external function
- Made `or_else` standard external function
- Changed external function signature to return
`Result<Option<Variable>, String>`
- Returns value directly from external functions whenever possible
- Made `unwrap_or` lazy
- Use static pointers for external lazy invariants
- Added `LAZY_UNWRAP_OR`
- Added `LAZY_AND`
- Added `LAZY_OR`
- Added `LAZY_NO`
- Rewrite binary operators in type checker
- Delay errors in type checker for external functions
- Treat `Kind::Add`, `Kind::Mul`, `Kind::Pow` as expressions in type
checker
- Updated `dyon_fn!` macro to return value directly when possible
- Removed `Runtime::binop`
- Added ambiguity check for `sec[_] vs any`
- Added `standard_binop` to “write.rs”
@bvssvni bvssvni changed the title Add lazy invariant following argument type Lazy invariant following argument type Apr 7, 2021
@bvssvni bvssvni removed the draft label Dec 11, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant