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

Code before:
  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 mut arg_use = old_args.clone().chain(new_args.clone());

  // Add the applications to call the rule body
  let term = match_path.iter().zip(&rule.pats).fold(term, |term, (matched, pat)| {
    match (matched, pat) {
      (Pattern::Var(_), Pattern::Var(_)) => Term::arg_call(term, arg_use.next().unwrap()),
      (Pattern::Ctr(_, vars), Pattern::Ctr(_, _)) => {
        vars.iter().fold(term, |term, _| Term::arg_call(term, arg_use.next().unwrap()))
      }
      // This particular rule was not matching on this arg but due to the other rules we had to match on a constructor.
      // So, to call the rule body we have to recreate the constructor.
      // (On scott encoding, if one of the cases is matched we must also match on all the other constructors for this arg)
      (Pattern::Ctr(ctr_nam, vars), Pattern::Var(_)) => {
        let ctr_ref_id = book.def_names.def_id(ctr_nam).unwrap();
        let ctr_args = vars.iter().map(|_| arg_use.next().unwrap());
        let ctr_term = ctr_args.fold(Term::Ref { def_id: ctr_ref_id }, Term::arg_call);
        Term::App { fun: Box::new(term), arg: Box::new(ctr_term) }
      }
      (Pattern::Var(_), Pattern::Ctr(_, _)) => unreachable!(),
      (_, Pattern::Num(..)) => todo!(),
      (Pattern::Num(..), _) => todo!(),
      (_, Pattern::Tup(_fst, _snd)) => arg_use.clone().fold(term, |acc, arg| Term::App {
        fun: Box::new(acc),
        arg: Box::new(Term::Var { nam: arg.clone() }),
      }),
      (Pattern::Tup(..), _) => todo!(),
    }
  });

  // Add the lambdas to get the matched variables
  let arg_decl = new_args.chain(old_args);
  let term = arg_decl.rev().fold(term, |term, arg| Term::named_lam(arg, term));

  add_case_to_book(book, crnt_name.clone(), term);
}

/// Builds a function for one of the pattern matches of the original one, as well as the next subfunctions recursively.
/// `(Rule ... (CtrA a0 ... an) ...) = ...`
/// to
/// `(Case) = λy1 .. λyn λx1 ... λxm λx (x Case$CtrA ... Case$CtrN x1 ... xm y1 ... yn)`
fn make_branch_pattern_matching_case(
  book: &mut Book,
  def_type: &[Type],
  def_id: DefId,
  crnt_name: &Name,
  crnt_rules: Vec<usize>,
  match_path: Vec<Pattern>,

Review comment (refactor):
I made a helper function `Term::arg_call` for adding this specific kind of case. Look at ` vars.iter().fold(term, |term, _| Term::arg_call(term, arg_use.next().unwrap()))` for example

Code after:
  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 mut arg_use = old_args.clone().chain(new_args.clone());

  // Add the applications to call the rule body
  let term = match_path.iter().zip(&rule.pats).fold(term, |term, (matched, pat)| {
    match (matched, pat) {
      (Pattern::Var(_), Pattern::Var(_)) => Term::arg_call(term, arg_use.next().unwrap()),
      (Pattern::Ctr(_, vars), Pattern::Ctr(_, _)) => {
        vars.iter().fold(term, |term, _| Term::arg_call(term, arg_use.next().unwrap()))
      }
      // This particular rule was not matching on this arg but due to the other rules we had to match on a constructor.
      // So, to call the rule body we have to recreate the constructor.
      // (On scott encoding, if one of the cases is matched we must also match on all the other constructors for this arg)
      (Pattern::Ctr(ctr_nam, vars), Pattern::Var(_)) => {
        let ctr_ref_id = book.def_names.def_id(ctr_nam).unwrap();
        let ctr_args = vars.iter().map(|_| arg_use.next().unwrap());
        let ctr_term = ctr_args.fold(Term::Ref { def_id: ctr_ref_id }, Term::arg_call);
        Term::App { fun: Box::new(term), arg: Box::new(ctr_term) }
      }
      (_, Pattern::Tup(..)) => arg_use.clone().fold(term, Term::arg_call),
      (Pattern::Tup(fst, snd), Pattern::Var(..)) => {
        let fst = if let Some(nam) = fst { Term::Var { nam: nam.clone() } } else { Term::Era };
        let snd = if let Some(nam) = snd { Term::Var { nam: nam.clone() } } else { Term::Era };
        Term::Tup { fst: Box::new(fst), snd: Box::new(snd) }
      }
      (Pattern::Var(_), _) => unreachable!(),
      (_, Pattern::Num(..)) => todo!(),
      (Pattern::Num(..), _) => todo!(),
      (Pattern::Tup(..), _) => todo!(),
    }
  });

  // Add the lambdas to get the matched variables
  let arg_decl = new_args.chain(old_args);
  let term = arg_decl.rev().fold(term, |term, arg| Term::named_lam(arg, term));

  add_case_to_book(book, crnt_name.clone(), term);
}

/// Builds a function for one of the pattern matches of the original one, as well as the next subfunctions recursively.
/// `(Rule ... (CtrA a0 ... an) ...) = ...`
/// to
/// `(Case) = λy1 .. λyn λx1 ... λxm λx (x Case$CtrA ... Case$CtrN x1 ... xm y1 ... yn)`
fn make_branch_pattern_matching_case(
  book: &mut Book,
  def_type: &[Type],
  def_id: DefId,
  crnt_name: &Name,