Conversation
There was a problem hiding this comment.
🟡 Changes recommended
The current function desugaring has correctness issues for argument tuple typing/projection and includes desugaring-time crashes (error) on user input.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds Python-style indented function definitions (def ...: blocks) and introduces standalone variable annotations, extending the lexer/parser and lowering functions into existing core constructs (lambda + let) to preserve operational semantics.
Changes:
- Extend AST (
Def,Expr) to represent function defs and standalone annotations; renameGenLettoLetthroughout. - Implement indentation-sensitive lexing (
indent/dedent) and parsing fordefblocks and parameter lists. - Desugar
FunDefinto a single-argument lambda over a tuple/product argument and update an example to use the new syntax.
File summaries
| File | Description |
|---|---|
| src/Lang/Types.hs | Kind-check type annotations in Sig; add Let synthesis and improve typed lambda synthesis. |
| src/Lang/Syntax.hs | Add AnnDef/FunDef; rename MkGenLet to MkLet and update helpers/patterns. |
| src/Lang/Substitution.hs | Update substitution to handle Let instead of GenLet. |
| src/Lang/Semantics.hs | Update big-step evaluator to handle Let instead of GenLet. |
| src/Lang/PrettyPrint.hs | Pretty-print Let instead of GenLet. |
| src/Lang/Parser.y | Add tokens/grammar for def blocks, parameters, and standalone annotations; parse let as MkLet. |
| src/Lang/Lexer.x | Add def token and implement layout-based indent/dedent injection after def headers. |
| src/Lang/Desugar.hs | Desugar FunDef into typed lambda + tuple destructuring; introduce parameter-type resolution via AnnDef. |
| examples/flow-rate.frtl | Update example to define and call a function using the new def + indented block syntax. |
Review details
Suppressed comments (3)
src/Lang/Desugar.hs:68
- Missing parameter annotations currently trigger
error, which will crash the frontend at desugaring time on user input (no source-positioned diagnostic). This should be reported as a proper parse/type error (e.g., viaExceptT TypeError/Either TypeErrorin the desugaring pipeline) so the REPL/frontend can surface a structured error instead of aborting.
case [ty | AnnDef name ty <- body, name == arg] of
ty:_ -> (arg, ty)
[] -> case headerType of
Just ty -> (arg, ty)
Nothing -> error $ "Missing type annotation for function parameter " ++ arg
src/Lang/Desugar.hs:72
functionArgTypeusesfoldr1 ProdTy, which (1) crashes on zero-argument functions and (2) builds a right-nested product type that is inconsistent with the parser/type grammar’s left-associative*products. This mismatch will break typing/projection for functions with 3+ parameters. Handle the empty case and build the product with left association.
functionArgType :: [(Identifier, Type 0)] -> Type 0
functionArgType [( _, ty)] = ty
functionArgType args = foldr1 ProdTy (map snd args)
src/Lang/Desugar.hs:104
pairProjectionscurrently returns projections likeFst arg,Snd arg,Snd (Snd arg)…, which do not extract individual elements from nested pair arguments (e.g., for 3 args, the second projection yields the pair(b,c)instead ofb). With left-nested tuples (the default due to left-associative,), projections should recursively project the prefix tuple and append the last element.
pairProjections count arg =
[ projection index | index <- [0 .. count - 1] ]
where
projection 0 = Fst arg
projection index = Snd (iterate Snd arg !! (index - 1))
- Files reviewed: 9/9 changed files
- Comments generated: 3
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
The current desugaring/projection logic misbinds parameters for 3+ argument functions and the parser’s block grammar does not reliably accept non-return-terminated function bodies given the lexer’s newline emission.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 25/25 changed files
- Comments generated: 4
- Review effort level: Lite
| annotations <- pendingAnnotations <$> get | ||
| bodyExpr <- desugarBody annotations body |
| pairProjections :: Int -> Expr -> [Expr] | ||
| pairProjections count arg = | ||
| [ projection index | index <- [0 .. count - 1] ] | ||
| where | ||
| projection 0 = Fst arg | ||
| projection index = Snd (iterate Snd arg !! (index - 1)) |
| BlockDefs :: { [Option] -> [Def 'Parsed] } | ||
| : nl BlockDefs { $2 } | ||
| | Def nl BlockDefs { \opts -> ($1 opts) : ($3 opts) } | ||
| | return Expr nl { \opts -> [Return ($2 opts)] } | ||
| | Def { \opts -> [$1 opts] } | ||
| | return Expr { \opts -> [Return ($2 opts)] } | ||
|
|
| -- | Resolve the declared types of a function's parameters before lowering its | ||
| -- body to a lambda expression. Parameters can carry an optional legacy header | ||
| -- annotation, while standalone annotations in the body support Python-style | ||
| -- declarations such as @def f(x): x : T@. A body annotation takes precedence, | ||
| -- keeping the parameter name next to its documentation and unit information. | ||
| -- Every parameter must resolve to a type because the generated lambda is | ||
| -- explicitly typed. |
|
The approach here to |
This PR adds indented function definition as in pure-py and also allows variables to given an annotation separately which is useful when defining functions and wanting to keep documentation next to typing. The PR extends the lexer and parser. Functions for now are desugared into lambda with let (to keep the operational semantics simple) but later this may need to change. The let binding expression is retained. Addresses #18