diff --git a/interactive/README.md b/interactive/README.md
index a47ee9f07..fe8589950 100644
--- a/interactive/README.md
+++ b/interactive/README.md
@@ -37,6 +37,8 @@ You can write this as (it's one file, but broken up for exposition):
Although at the beginning, this can technically go anywhere in the root scope.
Here we just define some types, and the number and shapes of input collections.
The language is structurally typed, with collections containing non-recursive algebraic data types (sums, products, lists).
+Enums are structural: variant names are for reading and erase, so two enums with the same lane shapes are the same type.
+`type Name = shape;` declares an erased alias, usable in collection shapes and enum payloads, but not yet inside lambdas.
@@ -46,8 +48,8 @@ The language is structurally typed, with collections containing non-recursive al
-- groups contain users and other groups, and a block on a member
-- reaches everyone inside it.
-type Access = Owner | Edit | View; -- decreasing levels of access
-type Member = User int | Group int; -- a user, or group of members
+enum Access = Owner | Edit | View; -- decreasing levels of access
+enum Member = User int | Group int; -- a user, or group of members
let grants = input 0 : (int, (Member, Access)); -- doc -> (member, access)
let groups = input 1 : (int, Member); -- group id -> member
diff --git a/interactive/examples/programs/adt.ddp b/interactive/examples/programs/adt.ddp
index 7bfdc91e8..594bfed40 100644
--- a/interactive/examples/programs/adt.ddp
+++ b/interactive/examples/programs/adt.ddp
@@ -2,7 +2,7 @@
-- carrying the field-sum as payload, then matched back out to a (bucket, sum)
-- and counted per bucket. (Contrived — exercises sum intro/outro + fold.)
-type Dir = Fwd int -- src < dst
+enum Dir = Fwd int -- src < dst
| Bwd int; -- otherwise
let edges = input 0 : ((int, int), ()) | map(((a, b), ()) -> (a, b));
diff --git a/interactive/examples/programs/ast.ddp b/interactive/examples/programs/ast.ddp
index dc8cd16a2..823e6d173 100644
--- a/interactive/examples/programs/ast.ddp
+++ b/interactive/examples/programs/ast.ddp
@@ -8,7 +8,7 @@
--
-- Inputs may be any integers: corgi orders integers by their signed values, as DDIR does.
-type Dir = Fwd int | Bwd int;
+enum Dir = Fwd int | Bwd int;
let rows = input 0 : ((int, int), ()) | map(((a, b), ()) -> (a, b));
diff --git a/interactive/examples/programs/binders.ddp b/interactive/examples/programs/binders.ddp
index ffdbef7cd..e573aed2f 100644
--- a/interactive/examples/programs/binders.ddp
+++ b/interactive/examples/programs/binders.ddp
@@ -1,7 +1,7 @@
-- Depth-tracking test: a pattern binder `xs` used INSIDE a fold step. Correct
-- de Bruijn resolution => xs is the case payload at Bound(2) inside the fold;
-- a wrong index would hit the fold's Int element and panic on Proj.
-type Node = Node List(int);
+enum Node = Node List(int);
let rows = input 0 : ((int, int), ()) | map(((a, b), ()) -> (a, (a enlist, b enlist) append inject Node));
diff --git a/interactive/examples/programs/tour.ddp b/interactive/examples/programs/tour.ddp
index 3ed20c696..4a75b92a0 100644
--- a/interactive/examples/programs/tour.ddp
+++ b/interactive/examples/programs/tour.ddp
@@ -3,7 +3,7 @@
-- fixtures under tests/programs/ pin individual lowerings and their fallback
-- routing; this program pins that the constructs work TOGETHER.
-type Dir = Fwd int | Bwd int;
+enum Dir = Fwd int | Bwd int;
let edges = input 0 : ((int, int), ()) | map(((a, b), ()) -> (a, b));
let roots = input 1 : ((int), ()) | map(((n), ()) -> (n, ()));
diff --git a/interactive/examples/server/programs/access.ddp b/interactive/examples/server/programs/access.ddp
index 8e828a58d..cfdfc6fb4 100644
--- a/interactive/examples/server/programs/access.ddp
+++ b/interactive/examples/server/programs/access.ddp
@@ -2,8 +2,8 @@
-- groups contain users and other groups, and a block on a member
-- reaches everyone inside it.
-type Access = Owner | Edit | View; -- decreasing levels of access
-type Member = User int | Group int; -- a user, or group of members
+enum Access = Owner | Edit | View; -- decreasing levels of access
+enum Member = User int | Group int; -- a user, or group of members
let grants = input 0 : (int, (Member, Access)); -- doc -> (member, access)
let groups = input 1 : (int, Member); -- group id -> member
diff --git a/interactive/examples/server/programs/echo.ddp b/interactive/examples/server/programs/echo.ddp
index 34e71e54c..e17ebc5da 100644
--- a/interactive/examples/server/programs/echo.ddp
+++ b/interactive/examples/server/programs/echo.ddp
@@ -1,5 +1,5 @@
-- Echoes input 0 to its output, so `inspect` shows whatever is fed — used to
-- demonstrate structured (ADT) values and time-scheduled input. Its rows are
-- values of a declared sum type.
-type Node = Leaf int | Pair (int, int);
+enum Node = Leaf int | Pair (int, int);
output "out" = input 0 : (Node, ()) | inspect(echo);
diff --git a/interactive/server/src/cmd.rs b/interactive/server/src/cmd.rs
index 757f38e71..63721c515 100644
--- a/interactive/server/src/cmd.rs
+++ b/interactive/server/src/cmd.rs
@@ -876,7 +876,7 @@ mod tests {
/// A parser that has prepared a load of `world`, whose inputs are fed below.
fn world() -> LineParser {
let program = r#"
- type T = A int | B int | C (int, int);
+ enum T = A int | B int | C (int, int);
output "pairs" = input 0 : ((int, int), ());
output "both" = input 1 : ((int, int), (int, int));
output "tagged" = input 2 : ((), T);
diff --git a/interactive/src/ir.rs b/interactive/src/ir.rs
index 122b1ae57..e641a0cb7 100644
--- a/interactive/src/ir.rs
+++ b/interactive/src/ir.rs
@@ -12,7 +12,7 @@ pub use crate::lambda::Lambda;
/// registered function may not take one as its name (`crate::lambda::register`). The lexer's own
/// table (`parse::pipe`) must agree, which its tests check.
pub(crate) const KEYWORDS: &[&str] = &[
- "let", "var", "set", "output", "type", "input", "import", "map", "join", "min", "distinct", "count",
+ "let", "var", "set", "output", "type", "enum", "input", "import", "map", "join", "min", "distinct", "count",
"collect", "flatmap", "arrange", "negate", "filter", "inspect", "lift_iter",
];
diff --git a/interactive/src/lambda.rs b/interactive/src/lambda.rs
index 4e1aaca35..69e8f3bfd 100644
--- a/interactive/src/lambda.rs
+++ b/interactive/src/lambda.rs
@@ -38,7 +38,7 @@ pub(crate) mod normalize;
/// input is the tuple of the slot's rows (`(key, val)` for a linear op, `(key, left val, right
/// val)` for a join). The parser binds it where it is written, in the scope of its DDIR program:
/// the registered functions and the base words `call` reaches, and the program's
-/// `type` declarations as enums. A parse error is kept, and reported where the program is compiled
+/// `enum` declarations. A parse error is kept, and reported where the program is compiled
/// or run. A composed lambda's graph is authoritative: its readable `src` is
/// reconstructed from the component sources and may reparse to a different
/// graph, especially after masking. Serialization stores only `src` and
diff --git a/interactive/src/parse/pipe.rs b/interactive/src/parse/pipe.rs
index fc7540180..190ba38fa 100644
--- a/interactive/src/parse/pipe.rs
+++ b/interactive/src/parse/pipe.rs
@@ -67,8 +67,9 @@
//! program output, root scope only). An optional `var x;` or
//! `var x : (key_shape, val_shape);` declares a feedback variable; the latter
//! checks the shape inferred for its `set` value. An `output` may also declare
-//! its shape. `type Name = V0 shape? | V1 shape? …;` declares a sum type, an
-//! enum of the program's lambdas (below); it emits no IR.
+//! its shape. `enum Name = V0 shape? | V1 shape? …;` declares a sum type, an
+//! enum of the program's lambdas (below); `type Name = shape;` declares an erased alias.
+//! Neither declaration emits IR.
//!
//! A shape is `int`, `float`, `()`, `(a, b, …)`, `List(a)`, `Option(a)`, `Result(a, b)`, or a
//! declared type's name. A variant's payload shape is `()` when omitted, and a type's payloads
@@ -105,8 +106,9 @@
//! `0.0`, where corgi's own `eq lt …` use the total order.
//! - `n call bit_length` is Int -> Int: `64 - (n as u64).leading_zeros()`, so zero gives 0
//! and a negative integer gives 64.
-//! - The program's `type` declarations are corgi enums of the same
+//! - The program's `enum` declarations are corgi enums of the same
//! names and variants. A variant two types share is written `Type::Variant`.
+//! Aliases are not yet available inside lambdas.
//! - `Option` (`None`, `Some`) and `Result` (`Ok`, `Err`) are enums whose payload lanes are open,
//! `None` aside: `x inject Some` takes its lane's shape from `x`, and `branch`, `map_variant` and
//! `match` read only their tags, as in `(x, c) branch Option map_variant None (_ -> ())` for
@@ -117,7 +119,7 @@ use crate::ir::Lambda;
#[derive(Debug, Clone, PartialEq)]
enum Token {
- Let, Var, Set, Output, Type,
+ Let, Var, Set, Output, Type, Enum,
Input, Import, Map, Join, Min, Distinct, Count, Collect, Arrange, Negate, Filter, LiftIter, FlatMap, Inspect,
Ident(String), Int(i64), Str(String),
/// A slot written as a corgi lambda, `pat -> body`, kept as corgi source.
@@ -149,7 +151,12 @@ impl TypeShape {
struct TypeDecl {
name: String,
- variants: Vec<(String, TypeShape)>,
+ body: TypeBody,
+}
+
+enum TypeBody {
+ Enum(Vec<(String, TypeShape)>),
+ Alias(TypeShape),
}
/// The source as a stream of chars, with the byte offset of the next one.
@@ -296,7 +303,7 @@ fn lex(input: &str) -> Vec {
fn keyword(ident: &str) -> Option {
Some(match ident {
"let" => Token::Let, "var" => Token::Var, "set" => Token::Set, "output" => Token::Output,
- "type" => Token::Type,
+ "type" => Token::Type, "enum" => Token::Enum,
"input" => Token::Input, "import" => Token::Import,
"map" => Token::Map,
"join" => Token::Join, "min" => Token::Min, "distinct" => Token::Distinct,
@@ -315,6 +322,8 @@ struct Parser {
/// Declared sum types: name -> the variants in tag order, each with its payload shape.
/// Populated before statements are parsed; tags are positions, scoped to their type.
types: std::collections::HashMap>,
+ /// Erased aliases, resolved in the same dependency order as enums.
+ aliases: std::collections::HashMap,
/// The declared types, in dependency order.
type_order: Vec,
/// The corgi scope lambdas are parsed in, built at the first lambda.
@@ -330,11 +339,11 @@ struct Parser {
impl Parser {
fn new(tokens: Vec) -> Self {
- Parser { tokens, pos: 0, types: Default::default(), type_order: Vec::new(), scope: None, unnumbered_inputs: 0, numbered_inputs: false, input_names: Default::default() }
+ Parser { tokens, pos: 0, types: Default::default(), aliases: Default::default(), type_order: Vec::new(), scope: None, unnumbered_inputs: 0, numbered_inputs: false, input_names: Default::default() }
}
/// The corgi scope of this program's lambdas: the registered functions and the base words
- /// as words, the built-in `Option` and `Result`, and all declared types as enums.
+ /// as words, the built-in `Option` and `Result`, and all declared enums.
fn lambda_scope(&mut self) -> &corgi::Scope {
if self.scope.is_none() {
let mut scope = crate::lambda::base_scope();
@@ -342,7 +351,9 @@ impl Parser {
scope.word(f.name(), crate::lambda::function_word(&f)).unwrap_or_else(|e| panic!("{e}"));
}
for name in &self.type_order {
- declare_enum(&mut scope, name, &self.types[name], &self.types);
+ if let Some(variants) = self.types.get(name) {
+ declare_enum(&mut scope, name, variants, &self.types);
+ }
}
self.scope = Some(scope);
}
@@ -358,25 +369,35 @@ impl Parser {
let mut indices = std::collections::HashMap::new();
let mut ranges = Vec::new();
while *self.peek() != Token::Eof {
- if *self.peek() != Token::Type { self.next(); continue; }
+ if !matches!(self.peek(), Token::Type | Token::Enum) { self.next(); continue; }
let start = self.pos;
- self.next();
+ let kind = self.next();
let name = self.parse_ident();
+ assert!(!matches!(name.as_str(), "int" | "float" | "List" | "Option" | "Result"),
+ "`{name}` is a built-in shape and cannot be declared");
assert!(!indices.contains_key(&name), "type `{name}` declared twice");
self.expect(&Token::Eq);
- let mut variants = vec![self.parse_variant_decl()];
- while *self.peek() == Token::Pipe {
- self.next();
- variants.push(self.parse_variant_decl());
- }
+ let body = if kind == Token::Enum {
+ let mut variants = vec![self.parse_variant_decl()];
+ while *self.peek() == Token::Pipe {
+ self.next();
+ variants.push(self.parse_variant_decl());
+ }
+ for (tag, (variant, _)) in variants.iter().enumerate() {
+ assert!(!variants[..tag].iter().any(|(other, _)| other == variant),
+ "enum `{name}` declares `{variant}` twice");
+ }
+ TypeBody::Enum(variants)
+ } else {
+ let shape = self.parse_type_shape();
+ // Anything but `;` after the shape, such as a `|` or a variant's payload, was meant as a sum.
+ assert!(*self.peek() == Token::Semi, "type `{name}` must be one shape followed by `;`; sums are declared with `enum`, not `type`");
+ TypeBody::Alias(shape)
+ };
self.expect(&Token::Semi);
- for (tag, (variant, _)) in variants.iter().enumerate() {
- assert!(!variants[..tag].iter().any(|(other, _)| other == variant),
- "type `{name}` declares `{variant}` twice");
- }
indices.insert(name.clone(), decls.len());
ranges.push((start, self.pos));
- decls.push(TypeDecl { name, variants });
+ decls.push(TypeDecl { name, body });
}
let mut states = vec![0; decls.len()];
let mut stack = Vec::new();
@@ -395,7 +416,7 @@ impl Parser {
stmts
}
- /// One `Name shape?` of a `type` declaration.
+ /// One `Name shape?` of an `enum` declaration.
fn parse_variant_decl(&mut self) -> (String, TypeShape) {
let name = self.parse_ident();
let shape = match self.peek() {
@@ -468,7 +489,8 @@ impl Parser {
TypeShape::List(inner) => Shape::List(Box::new(self.resolve_shape(inner))),
TypeShape::Option(inner) => Shape::Sum(vec![Shape::Unit, self.resolve_shape(inner)]),
TypeShape::Result(ok, err) => Shape::Sum(vec![self.resolve_shape(ok), self.resolve_shape(err)]),
- TypeShape::Named(name) => Shape::Sum(self.type_lanes(name)),
+ TypeShape::Named(name) => self.aliases.get(name).cloned()
+ .unwrap_or_else(|| Shape::Sum(self.type_lanes(name))),
}
}
@@ -678,7 +700,15 @@ impl Parser {
/// A corgi lambda, `pat -> body`: it runs `let pat = input in body`, in the program's scope.
fn lambda(&mut self) -> Lambda {
match self.next() {
- Token::Lambda { pat, body } => Lambda::new(format!("let {pat} = input in {body}"), self.lambda_scope()),
+ Token::Lambda { pat, body } => {
+ let mut lambda = Lambda::new(format!("let {pat} = input in {body}"), self.lambda_scope());
+ if let Err(error) = &mut lambda.graph {
+ if let Some(name) = self.aliases.keys().find(|name| error.ends_with(&format!("unknown enum '{name}'"))) {
+ *error += &format!("; alias `{name}`: aliases are not yet available inside lambdas");
+ }
+ }
+ lambda
+ },
other => panic!("expected a lambda `pat -> body`, got {other:?}"),
}
}
@@ -704,15 +734,21 @@ fn declare_type(
stack.push(index);
let decl = &decls[index];
let mut dependencies = Vec::new();
- for (_, shape) in &decl.variants { shape.dependencies(&mut dependencies); }
+ match &decl.body {
+ TypeBody::Enum(variants) => for (_, shape) in variants { shape.dependencies(&mut dependencies); },
+ TypeBody::Alias(shape) => shape.dependencies(&mut dependencies),
+ }
for name in dependencies {
let dependency = *indices.get(&name).unwrap_or_else(|| panic!("unknown type `{name}`"));
declare_type(dependency, decls, indices, states, stack, parser);
}
- let variants = decl.variants.iter()
- .map(|(name, shape)| (name.clone(), parser.resolve_shape(shape)))
- .collect();
- parser.types.insert(decl.name.clone(), variants);
+ match &decl.body {
+ TypeBody::Enum(variants) => {
+ let variants = variants.iter().map(|(name, shape)| (name.clone(), parser.resolve_shape(shape))).collect();
+ parser.types.insert(decl.name.clone(), variants);
+ }
+ TypeBody::Alias(shape) => { parser.aliases.insert(decl.name.clone(), parser.resolve_shape(shape)); }
+ }
parser.type_order.push(decl.name.clone());
stack.pop();
states[index] = 2;
@@ -747,7 +783,7 @@ fn declare_enum(scope: &mut corgi::Scope, name: &str, variants: &[(String, corgi
}
}
let body: Vec = variants.iter().map(|(v, s)| format!("{v} {}", render(s, scope, types))).collect();
- scope.declare(&format!("enum {name} = {}", body.join(" | "))).unwrap_or_else(|e| panic!("type `{name}`: {e}"));
+ scope.declare(&format!("enum {name} = {}", body.join(" | "))).unwrap_or_else(|e| panic!("enum `{name}`: {e}"));
}
pub fn parse(input: &str) -> Vec { let tokens = tokenize(input); let mut p = Parser::new(tokens); p.collect_types(); p.parse_program() }
@@ -757,7 +793,7 @@ pub(crate) fn print_context(input: &str) -> (Vec<(String, Vec<(String, corgi::Sh
let mut p = Parser::new(tokenize(input));
p.collect_types();
let scope = p.lambda_scope().clone();
- let types = p.type_order.iter().map(|name| (name.clone(), p.types[name].clone())).collect();
+ let types = p.type_order.iter().filter_map(|name| p.types.get(name).map(|vs| (name.clone(), vs.clone()))).collect();
let sources = p.tokens.iter().filter_map(|token| match token {
Token::Lambda { pat, body } => Some(format!("let {pat} = input in {body}")),
_ => None,
@@ -771,7 +807,8 @@ mod tests {
fn panic_text(src: &str) -> String {
let err = std::panic::catch_unwind(|| parse(src)).unwrap_err();
- *err.downcast::().expect("string panic")
+ err.downcast_ref::().cloned()
+ .or_else(|| err.downcast_ref::<&str>().map(|s| s.to_string())).expect("string panic")
}
#[test]
@@ -779,9 +816,9 @@ mod tests {
let stmts = parse(r#"
output "out" = input 0 : (int, Inner)
| map((k, v) -> (k, v inject Outer::Wrap));
- type Outer = Wrap Inner | Empty;
- type Nested = Lane List(Option(Inner));
- type Inner = Leaf int;
+ enum Outer = Wrap Inner | Empty;
+ enum Nested = Lane List(Option(Inner));
+ enum Inner = Leaf int;
"#);
assert_eq!(stmts.len(), 1);
assert!(matches!(&stmts[0], Stmt::Output(name, _, _) if name == "out"));
@@ -792,8 +829,8 @@ mod tests {
let stmts = parse(r#"
output "out" = input 0 : (int, Member)
| map((k, v) -> (k, v inject Group));
- inner: { type Member = User int; }
- type Group = Group Member;
+ inner: { enum Member = User int; }
+ enum Group = Group Member;
"#);
assert_eq!(stmts.len(), 2);
assert!(matches!(&stmts[1], Stmt::Scope(_, body) if body.is_empty()));
@@ -801,22 +838,54 @@ mod tests {
#[test]
fn recursive_types_name_the_cycle() {
- let message = panic_text("type A = Wrap B; type B = Wrap A;");
+ let message = panic_text("enum A = Wrap B; enum B = Wrap A;");
assert!(message.contains("A -> B -> A"), "{message}");
}
+ #[test]
+ fn recursive_aliases_name_the_cycle() {
+ for src in ["type A = B; type B = A;", "type A = B; enum B = Wrap A;"] {
+ let message = panic_text(src);
+ assert!(message.contains("recursive types: A -> B -> A"), "{message}");
+ }
+ }
+
+ #[test]
+ fn aliases_and_enums_share_a_namespace() {
+ for src in ["type A = int; enum A = One;", "enum A = One; type A = int;", "type A = int; type A = float;"] {
+ let message = panic_text(src);
+ assert!(message.contains("type `A` declared twice"), "{message}");
+ }
+ }
+
+ #[test]
+ fn sums_require_enum() {
+ for src in ["type X = A | B;", "type Node = Node List(int);"] {
+ let message = panic_text(src);
+ assert!(message.contains("sums are declared with `enum`"), "{message}");
+ }
+ }
+
+ #[test]
+ fn built_in_shapes_cannot_be_declared() {
+ for src in ["type int = float;", "enum float = F;", "type List = int;", "enum Option = O;", "type Result = (int, int);"] {
+ let message = panic_text(src);
+ assert!(message.contains("is a built-in shape and cannot be declared"), "{message}");
+ }
+ }
+
#[test]
fn unknown_types_keep_their_error() {
- let message = panic_text("type A = Wrap Missing;");
+ let message = panic_text("enum A = Wrap Missing;");
assert!(message.contains("unknown type `Missing`"), "{message}");
}
#[test]
fn duplicate_types_and_variants_keep_their_errors() {
- let name = panic_text("type A = One; s: { type A = Two; }");
+ let name = panic_text("enum A = One; s: { enum A = Two; }");
assert!(name.contains("type `A` declared twice"), "{name}");
- let variant = panic_text("type A = One | One;");
- assert!(variant.contains("type `A` declares `One` twice"), "{variant}");
+ let variant = panic_text("enum A = One | One;");
+ assert!(variant.contains("enum `A` declares `One` twice"), "{variant}");
}
#[test]
diff --git a/interactive/src/plan/ddp.rs b/interactive/src/plan/ddp.rs
index 789cb6f2b..d3c3ddb08 100644
--- a/interactive/src/plan/ddp.rs
+++ b/interactive/src/plan/ddp.rs
@@ -251,7 +251,7 @@ impl Printer<'_> {
let (name, variants) = self.types[i].clone();
let variants = variants.iter().map(|(v, s)| Ok(if *s == Shape::Unit { v.clone() } else { format!("{v} {}", self.shape(s)?) })).collect::, String>>()?;
for j in named..self.types.len() { self.declare_type(j, text)?; }
- writeln!(text, "type {name} = {};", variants.join(" | ")).unwrap();
+ writeln!(text, "enum {name} = {};", variants.join(" | ")).unwrap();
Ok(())
}
@@ -573,7 +573,7 @@ mod tests {
let source = |_: &str| Some((Shape::Sum(vec![Shape::Int, Shape::Unit]), Shape::Unit));
let before = super::super::report("output \"out\" = import \"sum\";", &source, &crate::passes::Config::all()).unwrap();
let text = before.emit_ddp().unwrap().text;
- assert!(text.contains("type Sum0 = V0 int | V1;"));
+ assert!(text.contains("enum Sum0 = V0 int | V1;"));
let after = super::super::report(&text, &source, &crate::passes::Config::all()).unwrap();
assert!(before.compare_final(&after).is_empty());
assert_eq!(text, after.emit_ddp().unwrap().text);
@@ -608,7 +608,7 @@ mod tests {
#[test]
fn graph_wins_over_stale_source_and_uses_declared_sums() {
- let report = plan("type Member = User int | Group int; let x = input 0 : (int, Member); output \"out\" = x | map((k, v) -> (k inject Group, v));");
+ let report = plan("enum Member = User int | Group int; let x = input 0 : (int, Member); output \"out\" = x | map((k, v) -> (k inject Group, v));");
let mut printer = Printer { as_of: report.as_of, context: &report.context, types: report.context.types.clone(), counts: LambdaCounts::default() };
let mut lambda = Lambda::new("let (k, v) = input in (k inject Group, v)".into(), &report.context.scope);
lambda.src = "let (k, v) = input in (k, v)".into();
diff --git a/interactive/tests/corgi_lambdas.rs b/interactive/tests/corgi_lambdas.rs
index bc1a11616..0b775deb2 100644
--- a/interactive/tests/corgi_lambdas.rs
+++ b/interactive/tests/corgi_lambdas.rs
@@ -4,6 +4,25 @@
use interactive::{lower, parse};
+#[test]
+fn aliases_inside_lambdas_explain_the_limit() {
+ timely::execute_directly(|worker| {
+ let src = r#"type Text = List(int); output "out" = input 0 : (int, Text) | map((k, x : Text) -> (k, x));"#;
+ let program = lower::lower_tree(parse::pipe::parse(src));
+ let error = interactive::server::Server::new().install(worker, "p", &program).unwrap_err();
+ assert!(error.contains("alias `Text`: aliases are not yet available inside lambdas"), "{error}");
+ });
+}
+
+#[test]
+fn inline_enums_inside_lambdas_still_work() {
+ timely::execute_directly(|worker| {
+ let src = r#"output "out" = input 0 : (int, int) | map((k, v) -> enum Local = One int in (k, v inject One));"#;
+ let program = lower::lower_tree(parse::pipe::parse(src));
+ interactive::server::Server::new().install(worker, "p", &program).unwrap();
+ });
+}
+
#[test]
fn mismatched_lambdas_are_refused_at_install() {
timely::execute_directly(|worker| {
diff --git a/interactive/tests/difftest.rs b/interactive/tests/difftest.rs
index fd81f16ce..7b4bc3b37 100644
--- a/interactive/tests/difftest.rs
+++ b/interactive/tests/difftest.rs
@@ -91,7 +91,7 @@ enum Sh { Int, Unit, Tup(Vec), List(Box), Sum, Float }
const LATE_INSTALLS: bool = true;
/// The one declared sum, `S`: its lanes' payload shapes are `int`, `(int, int)` and `()`.
-const SUM_DECL: &str = "type S = A int | B (int, int) | C;";
+const SUM_DECL: &str = "enum S = A int | B (int, int) | C;";
impl Sh {
fn text(&self) -> String {
@@ -140,7 +140,7 @@ enum Op {
#[derive(Clone, Debug)]
enum Stmt {
- /// A line written as is, such as a type declaration.
+ /// A line written as is, such as an enum declaration.
Raw(String),
Input { name: String, input: usize, key: Sh, val: Sh },
Let(Let),
diff --git a/interactive/tests/plan_pairs/cases/access_shared.ddp b/interactive/tests/plan_pairs/cases/access_shared.ddp
index 8fd52677d..9187bba61 100644
--- a/interactive/tests/plan_pairs/cases/access_shared.ddp
+++ b/interactive/tests/plan_pairs/cases/access_shared.ddp
@@ -3,8 +3,8 @@
-- groups contain users and other groups, and a block on a member
-- reaches everyone inside it.
-type Access = Owner | Edit | View; -- decreasing levels of access
-type Member = User int | Group int; -- a user, or group of members
+enum Access = Owner | Edit | View; -- decreasing levels of access
+enum Member = User int | Group int; -- a user, or group of members
let grants = input 0 : (int, (Member, Access)); -- doc -> (member, access)
let groups = input 1 : (int, Member); -- group id -> member
diff --git a/interactive/tests/plan_pairs/cases/enums.ddp b/interactive/tests/plan_pairs/cases/enums.ddp
index ebb252152..479337a56 100644
--- a/interactive/tests/plan_pairs/cases/enums.ddp
+++ b/interactive/tests/plan_pairs/cases/enums.ddp
@@ -1,4 +1,4 @@
-type Entry = Empty | Number int | Pair (int, int);
+enum Entry = Empty | Number int | Pair (int, int);
let rows = input 0 : (int, Entry);
let values = rows | map((k, v) -> (k, v match (Empty (_ -> 0), Number (n -> n), Pair ((a, b) -> (a, b) add))));
output "out" = values | map((k, n) -> (k, n inject Number));
diff --git a/interactive/tests/plan_pairs/expected/access_shared.ddp b/interactive/tests/plan_pairs/expected/access_shared.ddp
index 7f13b3b5d..2849db449 100644
--- a/interactive/tests/plan_pairs/expected/access_shared.ddp
+++ b/interactive/tests/plan_pairs/expected/access_shared.ddp
@@ -1,5 +1,5 @@
-type Access = Owner | Edit | View;
-type Member = User int | Group int;
+enum Access = Owner | Edit | View;
+enum Member = User int | Group int;
-- Time lanes: epoch.
let grants = input 0 : (int, (Member, Access));
let groups = input 1 : (int, Member);
diff --git a/interactive/tests/plan_pairs/expected/access_shared.parsed--lowered.diff b/interactive/tests/plan_pairs/expected/access_shared.parsed--lowered.diff
index ecce6a3d2..c07210acd 100644
--- a/interactive/tests/plan_pairs/expected/access_shared.parsed--lowered.diff
+++ b/interactive/tests/plan_pairs/expected/access_shared.parsed--lowered.diff
@@ -1,8 +1,8 @@
--- parsed
+++ lowered
@@ -1,19 +1,43 @@
- type Access = Owner | Edit | View;
- type Member = User int | Group int;
+ enum Access = Owner | Edit | View;
+ enum Member = User int | Group int;
+-- Time lanes: epoch.
let grants = input 0 : (int, (Member, Access));
let groups = input 1 : (int, Member);
diff --git a/interactive/tests/plan_pairs/expected/access_shared.parsed.ddp b/interactive/tests/plan_pairs/expected/access_shared.parsed.ddp
index e21770699..df600708d 100644
--- a/interactive/tests/plan_pairs/expected/access_shared.parsed.ddp
+++ b/interactive/tests/plan_pairs/expected/access_shared.parsed.ddp
@@ -1,5 +1,5 @@
-type Access = Owner | Edit | View;
-type Member = User int | Group int;
+enum Access = Owner | Edit | View;
+enum Member = User int | Group int;
let grants = input 0 : (int, (Member, Access));
let groups = input 1 : (int, Member);
let blocks = input 2 : (int, Member);
diff --git a/interactive/tests/plan_pairs/expected/enums.ddp b/interactive/tests/plan_pairs/expected/enums.ddp
index 7d9e6c609..d453f7608 100644
--- a/interactive/tests/plan_pairs/expected/enums.ddp
+++ b/interactive/tests/plan_pairs/expected/enums.ddp
@@ -1,4 +1,4 @@
-type Entry = Empty | Number int | Pair (int, int);
+enum Entry = Empty | Number int | Pair (int, int);
-- Time lanes: epoch.
let rows = input 0 : (int, Entry);
let out_map = rows | map((a, b) -> enum S0 = V0 () | V1 int | V2 (int, int) in (a, b match (0 (_ -> 0), 1 (a -> a), 2 ((a, b) -> (a, b) add)) inject S0::V1));
diff --git a/interactive/tests/plan_pairs/expected/enums.lowered--optimized.diff b/interactive/tests/plan_pairs/expected/enums.lowered--optimized.diff
index a5d1bbb11..0985c160e 100644
--- a/interactive/tests/plan_pairs/expected/enums.lowered--optimized.diff
+++ b/interactive/tests/plan_pairs/expected/enums.lowered--optimized.diff
@@ -1,7 +1,7 @@
--- lowered
+++ optimized
@@ -1,7 +1,6 @@
- type Entry = Empty | Number int | Pair (int, int);
+ enum Entry = Empty | Number int | Pair (int, int);
-- Time lanes: epoch.
let rows = input 0 : (int, Entry);
-let values = rows | map((k, v) -> (k, v match (Empty (_ -> 0), Number (n -> n), Pair ((a, b) -> (a, b) add))));
diff --git a/interactive/tests/plan_pairs/expected/enums.parsed--lowered.diff b/interactive/tests/plan_pairs/expected/enums.parsed--lowered.diff
index 1c0ecd075..ebae1d0c3 100644
--- a/interactive/tests/plan_pairs/expected/enums.parsed--lowered.diff
+++ b/interactive/tests/plan_pairs/expected/enums.parsed--lowered.diff
@@ -1,7 +1,7 @@
--- parsed
+++ lowered
@@ -1,4 +1,7 @@
- type Entry = Empty | Number int | Pair (int, int);
+ enum Entry = Empty | Number int | Pair (int, int);
+-- Time lanes: epoch.
let rows = input 0 : (int, Entry);
let values = rows | map((k, v) -> (k, v match (Empty (_ -> 0), Number (n -> n), Pair ((a, b) -> (a, b) add))));
diff --git a/interactive/tests/plan_pairs/expected/enums.parsed.ddp b/interactive/tests/plan_pairs/expected/enums.parsed.ddp
index fd5ae6758..ec6576c94 100644
--- a/interactive/tests/plan_pairs/expected/enums.parsed.ddp
+++ b/interactive/tests/plan_pairs/expected/enums.parsed.ddp
@@ -1,4 +1,4 @@
-type Entry = Empty | Number int | Pair (int, int);
+enum Entry = Empty | Number int | Pair (int, int);
let rows = input 0 : (int, Entry);
let values = rows | map((k, v) -> (k, v match (Empty (_ -> 0), Number (n -> n), Pair ((a, b) -> (a, b) add))));
output "out" = values | map((k, n) -> (k, n inject Entry::Number));
diff --git a/interactive/tests/plan_pairs/expected/server_access.ddp b/interactive/tests/plan_pairs/expected/server_access.ddp
index 005eddd31..364c5ed87 100644
--- a/interactive/tests/plan_pairs/expected/server_access.ddp
+++ b/interactive/tests/plan_pairs/expected/server_access.ddp
@@ -1,5 +1,5 @@
-type Access = Owner | Edit | View;
-type Member = User int | Group int;
+enum Access = Owner | Edit | View;
+enum Member = User int | Group int;
-- Time lanes: epoch.
let grants = input 0 : (int, (Member, Access));
let groups = input 1 : (int, Member);
diff --git a/interactive/tests/plan_pairs/expected/server_access.parsed--lowered.diff b/interactive/tests/plan_pairs/expected/server_access.parsed--lowered.diff
index 483889769..f64d65b2c 100644
--- a/interactive/tests/plan_pairs/expected/server_access.parsed--lowered.diff
+++ b/interactive/tests/plan_pairs/expected/server_access.parsed--lowered.diff
@@ -1,8 +1,8 @@
--- parsed
+++ lowered
@@ -1,19 +1,42 @@
- type Access = Owner | Edit | View;
- type Member = User int | Group int;
+ enum Access = Owner | Edit | View;
+ enum Member = User int | Group int;
+-- Time lanes: epoch.
let grants = input 0 : (int, (Member, Access));
let groups = input 1 : (int, Member);
diff --git a/interactive/tests/plan_pairs/expected/server_access.parsed.ddp b/interactive/tests/plan_pairs/expected/server_access.parsed.ddp
index 36ff9e2db..4fb45749f 100644
--- a/interactive/tests/plan_pairs/expected/server_access.parsed.ddp
+++ b/interactive/tests/plan_pairs/expected/server_access.parsed.ddp
@@ -1,5 +1,5 @@
-type Access = Owner | Edit | View;
-type Member = User int | Group int;
+enum Access = Owner | Edit | View;
+enum Member = User int | Group int;
let grants = input 0 : (int, (Member, Access));
let groups = input 1 : (int, Member);
let blocks = input 2 : (int, Member);
diff --git a/interactive/tests/plan_pairs/expected/server_echo.ddp b/interactive/tests/plan_pairs/expected/server_echo.ddp
index 976b96f12..2e8b659e5 100644
--- a/interactive/tests/plan_pairs/expected/server_echo.ddp
+++ b/interactive/tests/plan_pairs/expected/server_echo.ddp
@@ -1,4 +1,4 @@
-type Node = Leaf int | Pair (int, int);
+enum Node = Leaf int | Pair (int, int);
-- Time lanes: epoch.
let input0 = input 0 : (Node, ());
let out_inspect = input0 | inspect(echo);
diff --git a/interactive/tests/plan_pairs/expected/server_echo.parsed--lowered.diff b/interactive/tests/plan_pairs/expected/server_echo.parsed--lowered.diff
index be1da63a7..07766b1cf 100644
--- a/interactive/tests/plan_pairs/expected/server_echo.parsed--lowered.diff
+++ b/interactive/tests/plan_pairs/expected/server_echo.parsed--lowered.diff
@@ -1,7 +1,7 @@
--- parsed
+++ lowered
@@ -1,2 +1,6 @@
- type Node = Leaf int | Pair (int, int);
+ enum Node = Leaf int | Pair (int, int);
-output "out" = input 0 : (Node, ()) | inspect(echo);
+-- Time lanes: epoch.
+let input0 = input 0 : (Node, ());
diff --git a/interactive/tests/plan_pairs/expected/server_echo.parsed.ddp b/interactive/tests/plan_pairs/expected/server_echo.parsed.ddp
index 16443f5d6..283555a85 100644
--- a/interactive/tests/plan_pairs/expected/server_echo.parsed.ddp
+++ b/interactive/tests/plan_pairs/expected/server_echo.parsed.ddp
@@ -1,2 +1,2 @@
-type Node = Leaf int | Pair (int, int);
+enum Node = Leaf int | Pair (int, int);
output "out" = input 0 : (Node, ()) | inspect(echo);
diff --git a/interactive/tests/programs/case_ops.ddp b/interactive/tests/programs/case_ops.ddp
index 9ab3a05aa..46724946f 100644
--- a/interactive/tests/programs/case_ops.ddp
+++ b/interactive/tests/programs/case_ops.ddp
@@ -3,7 +3,7 @@
-- whose arms are both predicates, and the built-in `Option`: `Some`/`None` in the two branches of
-- an `if`.
-type Size = Small int | Big int;
+enum Size = Small int | Big int;
let pairs = input 0 : ((int, int), ()) | map(((a, b), ()) -> (a, b));
let tagged = pairs | map((a, b) -> (a, ((b, 15) lt, b inject Small, b inject Big) select));
diff --git a/interactive/tests/programs/join_fallback.ddp b/interactive/tests/programs/join_fallback.ddp
index c4fe50d3d..650a90cfe 100644
--- a/interactive/tests/programs/join_fallback.ddp
+++ b/interactive/tests/programs/join_fallback.ddp
@@ -1,7 +1,7 @@
-- A join whose projection depends on the data's shape: a `case`, whose arm homogeneity is a
-- fact about the data. The join compiles its projection against each block's actual column
-- shapes, so such terms need no separate path. The gate is its recorded answer.
-type L = L int;
+enum L = L int;
let left = input 0 : ((int, int), ()) | map(((a, b), ()) -> (a, b));
let right = input 1 : ((int, int), ()) | map(((a, b), ()) -> (a, b));
diff --git a/interactive/tests/programs/lambda_slots.ddp b/interactive/tests/programs/lambda_slots.ddp
index 23ac7854c..ef6c4b2c4 100644
--- a/interactive/tests/programs/lambda_slots.ddp
+++ b/interactive/tests/programs/lambda_slots.ddp
@@ -4,9 +4,9 @@
-- may sit inside a lambda written over several lines. Declared types and the built-in `Option`
-- are enums a lambda builds and takes apart, also when a payload is itself a sum.
-type Size = Small int | Big (int, int);
-type Inner = A int | B;
-type Outer = Wrap Inner | Opt Option(int) | Nums List(int);
+enum Size = Small int | Big (int, int);
+enum Inner = A int | B;
+enum Outer = Wrap Inner | Opt Option(int) | Nums List(int);
let pairs = input 0 : ((int, int), ()) | map(((a, b), ()) -> ((a), (b)));
diff --git a/interactive/tests/programs/sum_ops.ddp b/interactive/tests/programs/sum_ops.ddp
index 3ead1ffd6..522ca4036 100644
--- a/interactive/tests/programs/sum_ops.ddp
+++ b/interactive/tests/programs/sum_ops.ddp
@@ -1,7 +1,7 @@
-- Sum intro and tag tests on the corgi compiled path, checked against vec: a data-driven tag,
-- `(payload, tag) branch Three` (Op::Branch over a homogeneous declared type), and tag tests by
-- `match` on a sum (MapSum + Unwrap, incl. a tag no row carries).
-type Three = A int | B int | C int;
+enum Three = A int | B int | C int;
let pairs = input 0 : ((int, int), ()) | map(((a, b), ()) -> (a, b));
let tagged = pairs
diff --git a/interactive/tests/programs/sum_skew.ddp b/interactive/tests/programs/sum_skew.ddp
index 0571a4820..4d7bd9efe 100644
--- a/interactive/tests/programs/sum_skew.ddp
+++ b/interactive/tests/programs/sum_skew.ddp
@@ -3,7 +3,7 @@
-- (the arm a side never builds is an empty lane of its declared shape), so the two batches
-- have one shape and merge without reconciliation. This program used to need corgi's
-- uncommitted (⊥) lanes to typecheck (DDIR #817); now it is an ordinary program.
-type Kind = Rare int | Common int;
+enum Kind = Rare int | Common int;
let pairs = input 0 : ((int, int), ()) | map(((a, b), ()) -> (a, b));
diff --git a/interactive/tests/server_batch.rs b/interactive/tests/server_batch.rs
index 8d9b9bea9..ff5f87070 100644
--- a/interactive/tests/server_batch.rs
+++ b/interactive/tests/server_batch.rs
@@ -133,7 +133,7 @@ fn fed_sums_arrive_at_their_time() {
timely::execute_directly(|worker| {
let mut server = Server::new();
install(&mut server, worker, r#"
- type Node = Leaf int | Pair (int, int);
+ enum Node = Leaf int | Pair (int, int);
output "rows" = input 0 : (Node, ());
"#);
let node = corgi::Shape::Sum(vec![corgi::Shape::Int, corgi::Shape::Prod(vec![corgi::Shape::Int, corgi::Shape::Int])]);
diff --git a/interactive/tests/server_change_batches.rs b/interactive/tests/server_change_batches.rs
index 5b8666984..d4055a1bb 100644
--- a/interactive/tests/server_change_batches.rs
+++ b/interactive/tests/server_change_batches.rs
@@ -9,7 +9,7 @@ use interactive::{lower, parse};
fn tapped_changes_sum_to_the_export_and_are_taken_once() {
timely::execute_directly(|worker| {
let mut program = lower::lower_tree(parse::pipe::parse(r#"
- type Three = A int | B int | C int;
+ enum Three = A int | B int | C int;
let pairs = input 0 : ((int, int), ()) | map(((a, b), ()) -> (a, b));
output "plain" = pairs;
output "lists" = pairs | collect;
diff --git a/interactive/tests/server_shapes.rs b/interactive/tests/server_shapes.rs
index 0be9e4a7c..f0e293b4d 100644
--- a/interactive/tests/server_shapes.rs
+++ b/interactive/tests/server_shapes.rs
@@ -16,15 +16,47 @@ fn ints(n: usize) -> Shape { Shape::Prod(vec![Shape::Int; n]) }
const PRODUCER: &str = r#"output "edges" = input 0 : ((int, int), ()) | map(((src, dst), ()) -> ((src), (dst)));"#;
+#[test]
+fn aliases_erase_in_collection_shapes_and_enum_payloads() {
+ timely::execute_directly(|worker| {
+ let mut server = Server::new();
+ let src = r#"
+ let rows = input 0 : (Key, Named);
+ s: { var v : (Key, Named); set v = rows; }
+ output "out" : (Key, Named) = s::v;
+ type Named = Entry;
+ enum Entry = Value Payload | Empty;
+ type Payload = (Scalar, float, (), List(Scalar), Option(Scalar), Result(Scalar, float));
+ type Key = Scalar;
+ types: { type Scalar = int; }
+ "#;
+ server.install(worker, "aliases", &program(src)).unwrap();
+ let payload = Shape::Prod(vec![Shape::Int, Shape::Float, Shape::Unit,
+ Shape::List(Box::new(Shape::Int)), Shape::Sum(vec![Shape::Unit, Shape::Int]),
+ Shape::Sum(vec![Shape::Int, Shape::Float])]);
+ let expected = (Shape::Int, Shape::Sum(vec![payload, Shape::Unit]));
+ assert_eq!(server.trace_shape("aliases::out"), Some(expected.clone()));
+ let consumer = r#"
+ type Key = int;
+ type Payload = (int, float, (), List(int), Option(int), Result(int, float));
+ enum Same = Other Payload | Nothing;
+ type Named = Same;
+ output "out" = import "aliases::out" : (Key, Named);
+ "#;
+ server.install(worker, "consumer", &program(consumer)).unwrap();
+ assert_eq!(server.trace_shape("consumer::out"), Some(expected));
+ });
+}
+
#[test]
fn later_types_are_available_to_shapes_and_qualified_variants() {
timely::execute_directly(|worker| {
let src = r#"
output "out" = input 0 : (int, Inner)
| map((k, v) -> (k, v inject Outer::Wrap));
- type Outer = Wrap Inner;
- type Other = Wrap int;
- type Inner = Leaf int;
+ enum Outer = Wrap Inner;
+ enum Other = Wrap int;
+ enum Inner = Leaf int;
"#;
Server::new().install(worker, "later", &program(src)).unwrap();
});