Repository: HigherOrderCO/Bend
File: src/term/transform/encode_pattern_matching.rs (Rust)

Code before:
impl Term {
  fn named_lam(nam: Name, bod: Term) -> Term {
    Term::Lam { nam: Some(nam), bod: Box::new(bod) }
  }

  fn arg_call(term: Term, arg: Name) -> Term {
    Term::App { fun: Box::new(term), arg: Box::new(Term::Var { nam: arg }) }
  }
}

fn add_case_to_book(book: &mut Book, nam: Name, body: Term) {
  if let Some(def_id) = book.def_names.def_id(&nam) {
    book.defs.get_mut(&def_id).unwrap().rules = vec![Rule { pats: vec![], body }];
  } else {
    book.insert_def(nam, vec![Rule { pats: vec![], body }]);
  }
}

// How many old arguments and how many new arguments in the match path
fn get_pat_arg_count(match_path: &[Pattern]) -> (usize, usize) {
  let pat_arg_count = |pat: &Pattern| match pat {
    Pattern::Var(_) => 1,
    Pattern::Ctr(_, vars) => vars.len(),
    Pattern::Num(_) => todo!(),
    Pattern::Tup(fst, snd) => match (fst, snd) {
      (None, None) => 0,
      (Some(..), Some(..)) => 2,
      (None, Some(..)) | (Some(..), None) => 1,
    }
  };
  if let Some((new_pat, old_pats)) = match_path.split_last() {
    let new_args = pat_arg_count(new_pat);
    let old_args = old_pats.iter().map(pat_arg_count).sum();
    (new_args, old_args)
  } else {
    (0, 0)
  }
}

/// Adds the argument calls to the term, with old args followed by new args.
fn add_arg_calls(term: Term, match_path: &[Pattern]) -> Term {
  let (num_new_args, num_old_args) = get_pat_arg_count(match_path);
  let old_args = (0 .. num_old_args).map(|x| Name(format!("x{x}")));
  let new_args = (0 .. num_new_args).map(|x| Name(format!("y{x}")));
  let args = old_args.chain(new_args);
  args.fold(term, Term::arg_call)
}

// Adds the argument lambdas to the term, with new args followed by old args.
fn add_arg_lams(term: Term, match_path: &[Pattern]) -> Term {
  let (num_new_args, num_old_args) = get_pat_arg_count(match_path);

Review comment (suggestion):
With the way the encoding is implemented right now, we always pass the unused arguments forward, so tuples should always count as 2 variables. We can improve this later to insert some Era terms at the end, but for now fix this block

Code after:
impl Term {
  fn named_lam(nam: Name, bod: Term) -> Term {
    Term::Lam { nam: Some(nam), bod: Box::new(bod) }
  }

  fn arg_call(term: Term, arg: Name) -> Term {
    Term::App { fun: Box::new(term), arg: Box::new(Term::Var { nam: arg }) }
  }
}

fn add_case_to_book(book: &mut Book, nam: Name, body: Term) {
  if let Some(def_id) = book.def_names.def_id(&nam) {
    book.defs.get_mut(&def_id).unwrap().rules = vec![Rule { pats: vec![], body }];
  } else {
    book.insert_def(nam, vec![Rule { pats: vec![], body }]);
  }
}

// How many old arguments and how many new arguments in the match path
fn get_pat_arg_count(match_path: &[Pattern]) -> (usize, usize) {
  let pat_arg_count = |pat: &Pattern| match pat {
    Pattern::Var(_) => 1,
    Pattern::Ctr(_, vars) => vars.len(),
    Pattern::Num(_) => todo!(),
    Pattern::Tup(..) => 2,
  };
  if let Some((new_pat, old_pats)) = match_path.split_last() {
    let new_args = pat_arg_count(new_pat);
    let old_args = old_pats.iter().map(pat_arg_count).sum();
    (new_args, old_args)
  } else {
    (0, 0)
  }
}

/// Adds the argument calls to the term, with old args followed by new args.
fn add_arg_calls(term: Term, match_path: &[Pattern]) -> Term {
  let (num_new_args, num_old_args) = get_pat_arg_count(match_path);
  let old_args = (0 .. num_old_args).map(|x| Name(format!("x{x}")));
  let new_args = (0 .. num_new_args).map(|x| Name(format!("y{x}")));
  let args = old_args.chain(new_args);
  args.fold(term, Term::arg_call)
}

// Adds the argument lambdas to the term, with new args followed by old args.
fn add_arg_lams(term: Term, match_path: &[Pattern]) -> Term {
  let (num_new_args, num_old_args) = get_pat_arg_count(match_path);
  let old_args = (0 .. num_old_args).map(|x| Name(format!("x{x}")));
  let new_args = (0 .. num_new_args).map(|x| Name(format!("y{x}")));
  let args = new_args.chain(old_args);
  args.rev().fold(term, |term, arg| Term::named_lam(arg, term))