Change 'do' keyword to 'with'

This commit is contained in:
imaqtkatt 2024-05-28 16:49:00 -03:00
parent 046b1d7826
commit ea4a79ca6d
17 changed files with 81 additions and 52 deletions

View File

@ -205,6 +205,7 @@ elif condition3:
else:
return 3
```
The conditions are evaluated in order, one by one, stopping at the first successful case.
### Switch
@ -342,15 +343,15 @@ match p:
...
```
### Do
### With block
```python
do Result:
with Result:
x <- safe_div(2, 0)
return x
```
A monadic do block.
A monadic with block.
Where `x <- ...` performs a monadic operation.
@ -366,16 +367,29 @@ def Result/bind(res, nxt):
return res
```
Other statements are allowed inside the `do` block and it can both return a value at the end and bind a variable, like branching statements do.
Other statements are allowed inside the `with` block and it can both return a value at the end and bind a variable, like branching statements do.
```python
# Also ok:
do Result:
with Result:
x <- safe_div(2, 0);
y = x
return y
```
The name `wrap` is bound inside a `with` block as a shorthand for `Type/wrap`,
the equivalent as a `pure` function in other functional languages:
```python
def Result/wrap(x):
return Result/Ok(x)
with Result:
x <- some_operation(...)
y <- some_operation(...)
return wrap(x * y)
```
## Expressions
### Variables
@ -940,7 +954,7 @@ match x {
}
```
### Monadic bind blocks
### With block
```rust
Result/bind (Result/Ok val) f = (f val)
@ -956,7 +970,7 @@ rem a b = switch b {
_: (Result/Ok (% a b))
}
Main = do Result {
Main = with Result {
ask y = (div 3 2);
ask x = (rem y 0);
x
@ -966,7 +980,7 @@ Main = do Result {
Receives a type defined with `type` and expects `Result/bind` to be defined as a monadic bind function.
It should be of type `(Result a) -> (a -> Result b) -> Result b`, like in the example above.
Inside a `do` block, you can use `ask`, to access the continuation value of the monadic operation.
Inside a `with` block, you can use `ask`, to access the continuation value of the monadic operation.
```rust
ask y = (div 3 2)
@ -979,6 +993,19 @@ x
It can be used to force a sequence of operations. Since the continuation receives the result through a lambda, it is only fully evaluated after something is applied to it.
The name `wrap` is bound inside a `with` block as a shorthand for `Type/wrap`,
the equivalent as a `pure` function in other functional languages:
```rust
Result/wrap x = (Result/Ok x)
with Result {
ask x = (some_operation ...)
ask y = (some_operation ...)
wrap(x * y)
}
```
### Numbers and operations
Currently, bend supports 3 types of numbers: floats, integers and unsigned integers. All of then are 24 bit sized.

View File

@ -49,7 +49,7 @@ impl fmt::Display for Term {
Term::Var { nam } => write!(f, "{nam}"),
Term::Link { nam } => write!(f, "${nam}"),
Term::Let { pat, val, nxt } => write!(f, "let {} = {}; {}", pat, val, nxt),
Term::Do { typ, bod } => write!(f, "do {typ} {{ {bod} }}"),
Term::With { typ, bod } => write!(f, "with {typ} {{ {bod} }}"),
Term::Ask { pat, val, nxt } => write!(f, "ask {pat} = {val}; {nxt}"),
Term::Use { nam, val, nxt } => {
let Some(nam) = nam else { unreachable!() };
@ -295,8 +295,8 @@ impl Term {
Term::Let { pat, val, nxt } => {
write!(f, "let {} = {};\n{:tab$}{}", pat, val.display_pretty(tab), "", nxt.display_pretty(tab))
}
Term::Do { typ, bod } => {
writeln!(f, "do {typ} {{")?;
Term::With { typ, bod } => {
writeln!(f, "with {typ} {{")?;
writeln!(f, "{:tab$}{}", "", bod.display_pretty(tab + 2), tab = tab + 2)?;
write!(f, "{:tab$}}}", "")
}

View File

@ -85,7 +85,7 @@ pub enum Term {
val: Box<Term>,
nxt: Box<Term>,
},
Do {
With {
typ: Name,
bod: Box<Term>,
},
@ -316,7 +316,7 @@ impl Clone for Term {
Self::Var { nam } => Self::Var { nam: nam.clone() },
Self::Link { nam } => Self::Link { nam: nam.clone() },
Self::Let { pat, val, nxt } => Self::Let { pat: pat.clone(), val: val.clone(), nxt: nxt.clone() },
Self::Do { typ, bod } => Self::Do { typ: typ.clone(), bod: bod.clone() },
Self::With { typ, bod } => Self::With { typ: typ.clone(), bod: bod.clone() },
Self::Ask { pat, val, nxt } => Self::Ask { pat: pat.clone(), val: val.clone(), nxt: nxt.clone() },
Self::Use { nam, val, nxt } => Self::Use { nam: nam.clone(), val: val.clone(), nxt: nxt.clone() },
Self::App { tag, fun, arg } => Self::App { tag: tag.clone(), fun: fun.clone(), arg: arg.clone() },
@ -506,7 +506,7 @@ impl Term {
| Term::Use { val: fst, nxt: snd, .. }
| Term::App { fun: fst, arg: snd, .. }
| Term::Oper { fst, snd, .. } => ChildrenIter::Two([fst.as_ref(), snd.as_ref()]),
Term::Lam { bod, .. } | Term::Do { bod, .. } | Term::Open { bod, .. } => {
Term::Lam { bod, .. } | Term::With { bod, .. } | Term::Open { bod, .. } => {
ChildrenIter::One([bod.as_ref()])
}
Term::Var { .. }
@ -541,7 +541,7 @@ impl Term {
| Term::Use { val: fst, nxt: snd, .. }
| Term::App { fun: fst, arg: snd, .. }
| Term::Oper { fst, snd, .. } => ChildrenIter::Two([fst.as_mut(), snd.as_mut()]),
Term::Lam { bod, .. } | Term::Do { bod, .. } | Term::Open { bod, .. } => {
Term::Lam { bod, .. } | Term::With { bod, .. } | Term::Open { bod, .. } => {
ChildrenIter::One([bod.as_mut()])
}
Term::Var { .. }
@ -609,7 +609,7 @@ impl Term {
ChildrenIter::Two([(fst.as_ref(), BindsIter::Zero([])), (snd.as_ref(), BindsIter::Zero([]))])
}
Term::Lam { pat, bod, .. } => ChildrenIter::One([(bod.as_ref(), BindsIter::Pat(pat.binds()))]),
Term::Do { bod, .. } => ChildrenIter::One([(bod.as_ref(), BindsIter::Zero([]))]),
Term::With { bod, .. } => ChildrenIter::One([(bod.as_ref(), BindsIter::Zero([]))]),
Term::Var { .. }
| Term::Link { .. }
| Term::Num { .. }
@ -669,7 +669,7 @@ impl Term {
ChildrenIter::Two([(fst.as_mut(), BindsIter::Zero([])), (snd.as_mut(), BindsIter::Zero([]))])
}
Term::Lam { pat, bod, .. } => ChildrenIter::One([(bod.as_mut(), BindsIter::Pat(pat.binds()))]),
Term::Do { bod, .. } => ChildrenIter::One([(bod.as_mut(), BindsIter::Zero([]))]),
Term::With { bod, .. } => ChildrenIter::One([(bod.as_mut(), BindsIter::Zero([]))]),
Term::Var { .. }
| Term::Link { .. }
| Term::Num { .. }
@ -725,7 +725,7 @@ impl Term {
ChildrenIter::Two([(fst.as_mut(), BindsIter::Zero([])), (snd.as_mut(), BindsIter::Zero([]))])
}
Term::Lam { pat, bod, .. } => ChildrenIter::One([(bod.as_mut(), BindsIter::Pat(pat.binds_mut()))]),
Term::Do { bod, .. } => ChildrenIter::One([(bod.as_mut(), BindsIter::Zero([]))]),
Term::With { bod, .. } => ChildrenIter::One([(bod.as_mut(), BindsIter::Zero([]))]),
Term::Var { .. }
| Term::Link { .. }
| Term::Num { .. }

View File

@ -25,7 +25,7 @@ use TSPL::Parser;
// <Group> ::= "(" <Term> ")"
// <Use> ::= "use" <Name> "=" <Term> ";"? <Term>
// <Let> ::= "let" <NameEra> "=" <Term> ";"? <Term>
// <Bind> ::= "do" <Name> "{" <Ask> "}"
// <Bind> ::= "with" <Name> "{" <Ask> "}"
// <Ask> ::= "ask" <Pattern> "=" <Term> ";" <Term> | <Term>
// <LetTup> ::= "let" "(" <NameEra> ("," <NameEra>)+ ")" "=" <Term> ";"? <Term>
// <Dup> ::= "let" <Tag>? "{" <NameEra> (","? <NameEra>)+ "}" "=" <Term> ";"? <Term>
@ -523,14 +523,14 @@ impl<'a> TermParser<'a> {
return Ok(Term::Swt { arg: Box::new(arg), bnd, with, pred, arms });
}
// Do (monadic block)
if self.try_parse_keyword("do") {
// With (monadic block)
if self.try_parse_keyword("with") {
unexpected_tag(self)?;
let typ = self.parse_name()?;
self.consume("{")?;
let bod = self.parse_term()?;
self.consume("}")?;
return Ok(Term::Do { typ: Name::new(typ), bod: Box::new(bod) });
return Ok(Term::With { typ: Name::new(typ), bod: Box::new(bod) });
}
// Fold

View File

@ -218,7 +218,7 @@ impl<'t, 'l> EncodeTermState<'t, 'l> {
}
}
Term::Use { .. } // Removed in earlier pass
| Term::Do { .. } // Removed in earlier pass
| Term::With { .. } // Removed in earlier pass
| Term::Ask { .. } // Removed in earlier pass
| Term::Mat { .. } // Removed in earlier pass
| Term::Bend { .. } // Removed in desugar_bend

View File

@ -31,21 +31,27 @@ impl Term {
def_names: &HashSet<Name>,
) -> Result<(), String> {
maybe_grow(|| {
if let Term::Do { typ, bod } = self {
if let Term::With { typ, bod } = self {
bod.desugar_do_blocks(Some(typ), def_names)?;
*self = std::mem::take(bod);
let wrap_ref = Term::r#ref(&format!("{typ}/wrap"));
// let wrap_ref = if def_names.contains(&wrap_nam) {
// Term::r#ref(&wrap_nam)
// } else {
// return Err(format!("Could not find definition {wrap_nam} for type {typ}"));
// };
*self = Term::Use { nam: Some(Name::new("wrap")), val: Box::new(wrap_ref), nxt: std::mem::take(bod) };
}
if let Term::Ask { pat, val, nxt } = self {
if let Some(typ) = cur_block {
let fun = make_fun_name(typ);
let bind_nam = Name::new(format!("{typ}/bind"));
if def_names.contains(&fun) {
if def_names.contains(&bind_nam) {
// TODO: come up with a strategy for forwarding free vars to prevent infinite recursion.
let nxt = Term::lam(*pat.clone(), std::mem::take(nxt));
*self = Term::call(Term::Ref { nam: fun.clone() }, [*val.clone(), nxt]);
*self = Term::call(Term::Ref { nam: bind_nam }, [*val.clone(), nxt]);
} else {
return Err(format!("Could not find definition {} for type {}.", fun, typ));
return Err(format!("Could not find definition {bind_nam} for type {typ}."));
}
} else {
return Err(format!("Monadic bind operation '{pat} <- ...' used outside of a `do` block."));
@ -60,7 +66,3 @@ impl Term {
})
}
}
fn make_fun_name(typ: &Name) -> Name {
Name::new(format!("{typ}/bind"))
}

View File

@ -207,7 +207,7 @@ impl Term {
| Term::Nat { .. }
| Term::Str { .. }
| Term::List { .. }
| Term::Do { .. }
| Term::With { .. }
| Term::Ask { .. }
| Term::Open { .. }
| Term::Err => unreachable!(),
@ -255,7 +255,7 @@ impl Term {
| Term::Ref { .. }
| Term::Era
| Term::Err => FloatIter::Zero([]),
Term::Do { .. } | Term::Ask { .. } | Term::Bend { .. } | Term::Fold { .. } | Term::Open { .. } => {
Term::With { .. } | Term::Ask { .. } | Term::Bend { .. } | Term::Fold { .. } | Term::Open { .. } => {
unreachable!()
}
}

View File

@ -95,7 +95,7 @@ impl Stmt {
*self = gen_get(self, substitutions);
}
}
Stmt::Do { typ: _, bod, nxt } => {
Stmt::With { typ: _, bod, nxt } => {
bod.gen_map_get(id);
if let Some(nxt) = nxt {
nxt.gen_map_get(id);

View File

@ -144,10 +144,10 @@ pub enum Stmt {
arms: Vec<MatchArm>,
nxt: Option<Box<Stmt>>,
},
// "do" {fun} ":"
// "with" {fun} ":"
// {block}
// <nxt>?
Do {
With {
typ: Name,
bod: Box<Stmt>,
nxt: Option<Box<Stmt>>,

View File

@ -75,7 +75,7 @@ impl Stmt {
nxt.order_kwargs(book)?;
}
}
Stmt::Do { typ: _, bod, nxt } => {
Stmt::With { typ: _, bod, nxt } => {
bod.order_kwargs(book)?;
if let Some(nxt) = nxt {
nxt.order_kwargs(book)?;

View File

@ -411,8 +411,8 @@ impl<'a> PyParser<'a> {
self.parse_fold(indent)
} else if self.try_parse_keyword("bend") {
self.parse_bend(indent)
} else if self.try_parse_keyword("do") {
self.parse_do(indent)
} else if self.try_parse_keyword("with") {
self.parse_with(indent)
} else if self.try_parse_keyword("open") {
self.parse_open(indent)
} else if self.try_parse_keyword("use") {
@ -821,10 +821,10 @@ impl<'a> PyParser<'a> {
}
}
/// "do" <typ> ":"
/// "with" <typ> ":"
/// <bod>
/// <nxt>?
fn parse_do(&mut self, indent: &mut Indent) -> ParseResult<(Stmt, Indent)> {
fn parse_with(&mut self, indent: &mut Indent) -> ParseResult<(Stmt, Indent)> {
self.skip_trivia_inline();
let typ = self.parse_bend_name()?;
self.skip_trivia_inline();
@ -838,10 +838,10 @@ impl<'a> PyParser<'a> {
if nxt_indent == *indent {
let (nxt, nxt_indent) = self.parse_statement(indent)?;
let stmt = Stmt::Do { typ, bod: Box::new(bod), nxt: Some(Box::new(nxt)) };
let stmt = Stmt::With { typ, bod: Box::new(bod), nxt: Some(Box::new(nxt)) };
Ok((stmt, nxt_indent))
} else {
let stmt = Stmt::Do { typ, bod: Box::new(bod), nxt: None };
let stmt = Stmt::With { typ, bod: Box::new(bod), nxt: None };
Ok((stmt, nxt_indent))
}
}

View File

@ -262,12 +262,12 @@ impl Stmt {
fun::Term::Bend { bind, init, cond: Box::new(cond), step: Box::new(step), base: Box::new(base) };
wrap_nxt_assign_stmt(term, nxt, pat)?
}
Stmt::Do { typ, bod, nxt } => {
Stmt::With { typ, bod, nxt } => {
let (pat, bod) = match bod.into_fun()? {
StmtToFun::Return(term) => (None, term),
StmtToFun::Assign(pat, term) => (Some(pat), term),
};
let term = fun::Term::Do { typ, bod: Box::new(bod) };
let term = fun::Term::With { typ, bod: Box::new(bod) };
wrap_nxt_assign_stmt(term, nxt, pat)?
}
Stmt::Ask { pat, val, nxt } => {

View File

@ -13,7 +13,7 @@ safe_rem a b = switch b {
_: (Result/Ok (% a b))
}
Main = do Result {
Main = with Result {
ask y = (safe_div 3 2)
ask x = (safe_rem y 0);
x

View File

@ -75,6 +75,6 @@ def sup():
return x
def main():
do IO:
with IO:
x <- IO.read();
return x;

View File

@ -6,7 +6,7 @@ Result/bind r nxt = match r {
Result/Err: r
}
main = do Result {
main = with Result {
let x = 1
let y = (Result/Ok x)
ask y = y

View File

@ -5,7 +5,7 @@ Result/bind err _ = err
Bar x = (Result/Err 0)
Foo x y = do Result {
Foo x y = with Result {
ask x = (Bar x);
(Foo x y)
}

View File

@ -36,7 +36,7 @@ input_file: tests/golden_tests/parse_file/imp_program.bend
(sup) = let x = {(List/Cons 1 (List/Cons 2 List/Nil)) (List/Cons 3 (List/Cons 4 (List/Cons 5 (List/Cons 6 List/Nil))))}; x
(main) = do IO { ask x = IO.read; x }
(main) = with IO { ask x = IO.read; x }
(List/Nil) = λ%x (%x List/Nil/tag)