diff --git a/docs/syntax.md b/docs/syntax.md index 396f6884..050213b9 100644 --- a/docs/syntax.md +++ b/docs/syntax.md @@ -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. diff --git a/src/fun/display.rs b/src/fun/display.rs index e538272b..29c9b68c 100644 --- a/src/fun/display.rs +++ b/src/fun/display.rs @@ -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$}}}", "") } diff --git a/src/fun/mod.rs b/src/fun/mod.rs index 4ef29d1b..afd29b14 100644 --- a/src/fun/mod.rs +++ b/src/fun/mod.rs @@ -85,7 +85,7 @@ pub enum Term { val: Box, nxt: Box, }, - Do { + With { typ: Name, bod: Box, }, @@ -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 { .. } diff --git a/src/fun/parser.rs b/src/fun/parser.rs index 15b5c996..4de18bcb 100644 --- a/src/fun/parser.rs +++ b/src/fun/parser.rs @@ -25,7 +25,7 @@ use TSPL::Parser; // ::= "(" ")" // ::= "use" "=" ";"? // ::= "let" "=" ";"? -// ::= "do" "{" "}" +// ::= "with" "{" "}" // ::= "ask" "=" ";" | // ::= "let" "(" ("," )+ ")" "=" ";"? // ::= "let" ? "{" (","? )+ "}" "=" ";"? @@ -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 diff --git a/src/fun/term_to_net.rs b/src/fun/term_to_net.rs index 936c8ff2..405a9a55 100644 --- a/src/fun/term_to_net.rs +++ b/src/fun/term_to_net.rs @@ -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 diff --git a/src/fun/transform/desugar_do_blocks.rs b/src/fun/transform/desugar_do_blocks.rs index 46d5036c..fedc1b2f 100644 --- a/src/fun/transform/desugar_do_blocks.rs +++ b/src/fun/transform/desugar_do_blocks.rs @@ -31,21 +31,27 @@ impl Term { def_names: &HashSet, ) -> 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")) -} diff --git a/src/fun/transform/float_combinators.rs b/src/fun/transform/float_combinators.rs index e15f6bba..9a2b062e 100644 --- a/src/fun/transform/float_combinators.rs +++ b/src/fun/transform/float_combinators.rs @@ -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!() } } diff --git a/src/imp/gen_map_get.rs b/src/imp/gen_map_get.rs index 736bb63d..3e34eb0e 100644 --- a/src/imp/gen_map_get.rs +++ b/src/imp/gen_map_get.rs @@ -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); diff --git a/src/imp/mod.rs b/src/imp/mod.rs index 70d7b82c..37021af6 100644 --- a/src/imp/mod.rs +++ b/src/imp/mod.rs @@ -144,10 +144,10 @@ pub enum Stmt { arms: Vec, nxt: Option>, }, - // "do" {fun} ":" + // "with" {fun} ":" // {block} // ? - Do { + With { typ: Name, bod: Box, nxt: Option>, diff --git a/src/imp/order_kwargs.rs b/src/imp/order_kwargs.rs index 2e470040..1b15b532 100644 --- a/src/imp/order_kwargs.rs +++ b/src/imp/order_kwargs.rs @@ -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)?; diff --git a/src/imp/parser.rs b/src/imp/parser.rs index 293e3428..8626800a 100644 --- a/src/imp/parser.rs +++ b/src/imp/parser.rs @@ -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" ":" + /// "with" ":" /// /// ? - 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)) } } diff --git a/src/imp/to_fun.rs b/src/imp/to_fun.rs index 2c9ea519..67d83255 100644 --- a/src/imp/to_fun.rs +++ b/src/imp/to_fun.rs @@ -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 } => { diff --git a/tests/golden_tests/desugar_file/bind_syntax.bend b/tests/golden_tests/desugar_file/bind_syntax.bend index 5f816e36..885a5367 100644 --- a/tests/golden_tests/desugar_file/bind_syntax.bend +++ b/tests/golden_tests/desugar_file/bind_syntax.bend @@ -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 diff --git a/tests/golden_tests/parse_file/imp_program.bend b/tests/golden_tests/parse_file/imp_program.bend index 1013cf76..7c3ea9e1 100644 --- a/tests/golden_tests/parse_file/imp_program.bend +++ b/tests/golden_tests/parse_file/imp_program.bend @@ -75,6 +75,6 @@ def sup(): return x def main(): - do IO: + with IO: x <- IO.read(); return x; diff --git a/tests/golden_tests/run_file/do_block_mixed.bend b/tests/golden_tests/run_file/do_block_mixed.bend index 224bf6c9..1dd9cbfc 100644 --- a/tests/golden_tests/run_file/do_block_mixed.bend +++ b/tests/golden_tests/run_file/do_block_mixed.bend @@ -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 diff --git a/tests/golden_tests/run_file/recursive_bind.bend b/tests/golden_tests/run_file/recursive_bind.bend index ce33ecb6..18e6c7e0 100644 --- a/tests/golden_tests/run_file/recursive_bind.bend +++ b/tests/golden_tests/run_file/recursive_bind.bend @@ -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) } diff --git a/tests/snapshots/parse_file__imp_program.bend.snap b/tests/snapshots/parse_file__imp_program.bend.snap index efa9dfa6..da62166e 100644 --- a/tests/snapshots/parse_file__imp_program.bend.snap +++ b/tests/snapshots/parse_file__imp_program.bend.snap @@ -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)