mirror of
https://github.com/HigherOrderCO/Bend.git
synced 2024-08-15 14:50:42 +03:00
Change 'do' keyword to 'with'
This commit is contained in:
parent
046b1d7826
commit
ea4a79ca6d
@ -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.
|
||||
|
@ -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$}}}", "")
|
||||
}
|
||||
|
@ -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 { .. }
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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"))
|
||||
}
|
||||
|
@ -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!()
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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>>,
|
||||
|
@ -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)?;
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
@ -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 } => {
|
||||
|
@ -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
|
||||
|
@ -75,6 +75,6 @@ def sup():
|
||||
return x
|
||||
|
||||
def main():
|
||||
do IO:
|
||||
with IO:
|
||||
x <- IO.read();
|
||||
return x;
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user