ronantakizawa/codereview-bench huggingface dataset:
Repository: HigherOrderCO/Bend
File: src/imp/parser.rs (Rust)

Code before:
          while self.try_consume(",") {
            els.push(self.parse_expr(false)?);
          }
          self.consume(")")?;
          Expr::Tup { els }
        } else {
          self.consume(")")?;
          // A parenthesized expression
          head
        }
      }
      // Map or Sup
      '{' => {
        self.advance_one();
        // Empty map
        if self.try_consume("}") {
          return Ok(Expr::MapInit { entries: vec![] });
        }
        let head = self.parse_expr(false)?;
        self.skip_trivia();
        if self.try_consume(",") {
          self.parse_sup(head)?
        } else if self.try_consume(":") {
          self.parse_map_init(head)?
        } else {
          self.expected("Comma or Colon")?
        }
      }
      // List or Comprehension
      '[' => self.list_or_comprehension()?,
      // Symbol
      '`' => Expr::Num { val: Num::U24(self.parse_quoted_symbol()?) },
      // String
      '\"' => {
        let str = self.parse_quoted_string()?;
        let val = STRINGS.get(str);
        Expr::Str { val }
      }
      // Char
      '\'' => {
        let chr = self.parse_quoted_char()?;
        Expr::Num { val: Num::U24(chr as u32 & 0x00ff_ffff) }
      }
      // Unscoped var
      '$' => {
        self.advance_one();
        let nam = self.parse_bend_name()?;
        Expr::Chn { nam }
      }
      // Era
      '*' => {

Review comment (suggestion):
```suggestion
          self.expected("',' or ':")?
```
To be consistent with how we're displaying this in other situations

Code after:
          while self.try_consume(",") {
            els.push(self.parse_expr(false)?);
          }
          self.consume(")")?;
          Expr::Tup { els }
        } else {
          self.consume(")")?;
          // A parenthesized expression
          head
        }
      }
      // Map or Sup
      '{' => {
        self.advance_one();
        // Empty map
        if self.try_consume("}") {
          return Ok(Expr::MapInit { entries: vec![] });
        }
        let head = self.parse_expr(false)?;
        self.skip_trivia();
        if self.try_consume(",") {
          self.parse_sup(head)?
        } else if self.try_consume(":") {
          self.parse_map_init(head)?
        } else {
          self.expected("',' or ':'")?
        }
      }
      // List or Comprehension
      '[' => self.list_or_comprehension()?,
      // Symbol
      '`' => Expr::Num { val: Num::U24(self.parse_quoted_symbol()?) },
      // String
      '\"' => {
        let str = self.parse_quoted_string()?;
        let val = STRINGS.get(str);
        Expr::Str { val }
      }
      // Char
      '\'' => {
        let chr = self.parse_quoted_char()?;
        Expr::Num { val: Num::U24(chr as u32 & 0x00ff_ffff) }
      }
      // Unscoped var
      '$' => {
        self.advance_one();
        let nam = self.parse_bend_name()?;
        Expr::Chn { nam }
      }
      // Era
      '*' => {
Repository: HigherOrderCO/Bend
File: src/fun/parser.rs (Rust)

Code before:
      Op::AND
    } else if self.starts_with("|") {
      Op::OR
    } else if self.starts_with("^") {
      Op::XOR
    } else {
      return None;
    };
    Some(opr)
  }

  fn parse_u32(&mut self) -> ParseResult<u32> {
    let radix = match self.peek_many(2) {
      Some("0x") => {
        self.advance_many(2);
        16
      }
      Some("0b") => {
        self.advance_many(2);
        2
      }
      _ => 10,
    };
    let num_str = self.take_while(move |c| c.is_digit(radix) || c == '_');
    let num_str = num_str.chars().filter(|c| *c != '_').collect::<String>();
    if let Some(c) = self.peek_one() && c.is_numeric() {
      self.expected("valid digit")
    } else if num_str.is_empty() {
      self.expected("numeric digit")
    } else {
      u32::from_str_radix(&num_str, radix).map_err(|e| e.to_string())
    }
  }

  fn parse_number(&mut self) -> ParseResult<Num> {
    let ini_idx = *self.index();

    // Parses sign
    let sgn = if self.try_consume_exactly("+") {
      Some(1)
    } else if self.try_consume_exactly("-") {
      Some(-1)
    } else {
      None
    };

    // Parses main value
    let num = self.parse_u32()?;

    // Parses frac value (Float type)
    // TODO: Will lead to some rounding errors

Review comment (suggestion):
Shouldn't be just `is_numeric`, but rather `is_alphanumeric` since it will include hex numbers and avoid confusions with names that start with numbers (which are not possible since numbers have higher priority).
```suggestion
    if let Some(c) = self.peek_one() && c.is_ascii_alphanumeric() {
```

Code after:
      Op::AND
    } else if self.starts_with("|") {
      Op::OR
    } else if self.starts_with("^") {
      Op::XOR
    } else {
      return None;
    };
    Some(opr)
  }

  fn parse_u32(&mut self) -> ParseResult<u32> {
    let radix = match self.peek_many(2) {
      Some("0x") => {
        self.advance_many(2);
        16
      }
      Some("0b") => {
        self.advance_many(2);
        2
      }
      _ => 10,
    };
    let num_str = self.take_while(move |c| c.is_digit(radix) || c == '_');
    let num_str = num_str.chars().filter(|c| *c != '_').collect::<String>();
    // can't merge the first two blocks because || is invalid in let chains
    if let Some(c) = self.peek_one()
      && c.is_ascii_alphanumeric()
    {
      let base = match radix {
        16 => "hexadecimal",
        10 => "decimal",
        2 => "binary",
        _ => unreachable!(),
      };
      self.expected(format!("valid {base} digit").as_str())
    } else if num_str.is_empty() {
      let base = match radix {
        16 => "hexadecimal",
        10 => "decimal",
        2 => "binary",
        _ => unreachable!(),
      };
      self.expected(format!("valid {base} digit").as_str())
    } else {
      u32::from_str_radix(&num_str, radix).map_err(|e| e.to_string())
    }
  }

  fn parse_number(&mut self) -> ParseResult<Num> {
    let ini_idx = *self.index();
Repository: HigherOrderCO/Bend
File: src/fun/transform/desugar_with_blocks.rs (Rust)

Code before:
use crate::{
  diagnostics::Diagnostics,
  fun::{Ctx, Name, Pattern, Term},
  maybe_grow,
};
use std::collections::HashSet;

impl Ctx<'_> {
  /// Converts `ask` terms inside `do` blocks into calls to a monadic bind operation.
  pub fn desugar_with_blocks(&mut self) -> Result<(), Diagnostics> {
    self.info.start_pass();

    let def_names = self.book.defs.keys().cloned().collect::<HashSet<_>>();

    for def in self.book.defs.values_mut() {
      for rule in def.rules.iter_mut() {
        if let Err(e) = rule.body.desugar_with_blocks(None, &def_names) {
          self.info.add_rule_error(e, def.name.clone());
        }
      }
    }

    self.info.fatal(())
  }
}

impl Term {
  pub fn desugar_with_blocks(
    &mut self,
    cur_block: Option<&Name>,
    def_names: &HashSet<Name>,
  ) -> Result<(), String> {
    maybe_grow(|| {
      if let Term::With { typ, bod } = self {

Review comment (suggestion):
```suggestion
  /// Converts `ask` terms inside `with` blocks into calls to a monadic bind operation.
```

Code after:
use crate::{
  diagnostics::Diagnostics,
  fun::{Ctx, Name, Pattern, Term},
  maybe_grow,
};
use std::collections::HashSet;

impl Ctx<'_> {
  /// Converts `ask` terms inside `with` blocks into calls to a monadic bind operation.
  pub fn desugar_with_blocks(&mut self) -> Result<(), Diagnostics> {
    self.info.start_pass();

    let def_names = self.book.defs.keys().cloned().collect::<HashSet<_>>();

    for def in self.book.defs.values_mut() {
      for rule in def.rules.iter_mut() {
        if let Err(e) = rule.body.desugar_with_blocks(None, &def_names) {
          self.info.add_rule_error(e, def.name.clone());
        }
      }
    }

    self.info.fatal(())
  }
}

impl Term {
  pub fn desugar_with_blocks(
    &mut self,
    cur_block: Option<&Name>,
    def_names: &HashSet<Name>,
  ) -> Result<(), String> {
    maybe_grow(|| {
      if let Term::With { typ, bod } = self {
Repository: HigherOrderCO/Bend
File: src/fun/transform/expand_generated.rs (Rust)

Code before:
  maybe_grow,
};

/// Dereferences any non recursive generated definitions in the term.
/// Used after readback.
impl Term {
  pub fn expand_generated(&mut self, book: &Book, recursive_defs: &RecursiveDefs) {
    maybe_grow(|| {
      if let Term::Ref { nam } = &*self {
        if nam.is_generated() && !recursive_defs.contains(nam) {
          *self = book.defs.get(nam).unwrap().rule().body.clone();
        }
      }

      for child in self.children_mut() {
        child.expand_generated(book, recursive_defs);
      }
    })
  }
}

type DepGraph = HashMap<Name, HashSet<Name>>;
type Cycles = Vec<Vec<Name>>;
type RecursiveDefs = BTreeSet<Name>;

pub fn find_recursive_defs(book: &Book) -> RecursiveDefs {
  let mut cycle_map = BTreeSet::new();
  let deps = book_def_deps(book);
  let cycles = cycles(&deps);

  for cycle in cycles {
    for name in cycle {
      cycle_map.insert(name);
    }
  }

  cycle_map
}

/// Find all cycles in the dependency graph.
fn cycles(deps: &DepGraph) -> Cycles {
  let mut cycles = vec![];
  let mut stack = vec![];
  let mut visited = HashSet::new();
  for nam in deps.keys() {
    if !visited.contains(nam) {
      find_cycles(deps, nam, &mut visited, &mut stack, &mut cycles);
    }
  }
  cycles
}

Review comment (suggestion):
Can you instead make this 
```
impl Book {
  pub fn recursive_defs(&self) -> RecursiveDefs {
    ...
  }
}
```
To be more in line with how we expose other functions on Book and Term? (like Term::free_vars for example)

Code after:
  maybe_grow,
};

/// Dereferences any non recursive generated definitions in the term.
/// Used after readback.
impl Term {
  pub fn expand_generated(&mut self, book: &Book, recursive_defs: &RecursiveDefs) {
    maybe_grow(|| {
      if let Term::Ref { nam } = &*self {
        if nam.is_generated() && !recursive_defs.contains(nam) {
          *self = book.defs.get(nam).unwrap().rule().body.clone();
        }
      }

      for child in self.children_mut() {
        child.expand_generated(book, recursive_defs);
      }
    })
  }
}

type DepGraph = HashMap<Name, HashSet<Name>>;
type Cycles = Vec<Vec<Name>>;
type RecursiveDefs = BTreeSet<Name>;

impl Book {
  pub fn recursive_defs(&self) -> RecursiveDefs {
    let mut cycle_map = BTreeSet::new();
    let deps = book_def_deps(self);
    let cycles = cycles(&deps);

    for cycle in cycles {
      for name in cycle {
        cycle_map.insert(name);
      }
    }

    cycle_map
  }
}

/// Find all cycles in the dependency graph.
fn cycles(deps: &DepGraph) -> Cycles {
  let mut cycles = vec![];
  let mut visited = HashSet::new();
  // let mut stack = vec![];
  for nam in deps.keys() {
    if !visited.contains(nam) {
      find_cycles(deps, nam, &mut visited, &mut cycles);
    }
  }
Repository: HigherOrderCO/Bend
File: src/lib.rs (Rust)

Code before:
use crate::{
  fun::{book_to_hvm, net_to_term::net_to_term, term_to_net::Labels, Book, Ctx, Term},
  hvm::{
    add_recursive_priority::add_recursive_priority,
    check_net_size::{check_net_sizes, MAX_NET_SIZE},
    eta_reduce::eta_reduce_hvm_net,
    hvm_book_show_pretty,
    inline::inline_hvm_book,
    mutual_recursion,
    prune::prune_hvm_book,
  },
};
use diagnostics::{Diagnostics, DiagnosticsConfig, ERR_INDENT_SIZE};
use net::hvm_to_net::hvm_to_net;

pub mod diagnostics;
#[allow(clippy::mutable_key_type)]
pub mod fun;
pub mod hvm;
pub mod imp;
pub mod imports;
pub mod net;
mod utils;

pub use fun::load_book::{load_file_to_book, load_to_book};

pub const ENTRY_POINT: &str = "main";
pub const HVM1_ENTRY_POINT: &str = "Main";
pub const HVM_OUTPUT_END_MARKER: &str = "Result: ";

pub fn check_book(
  book: &mut Book,
  diagnostics_cfg: DiagnosticsConfig,
  compile_opts: CompileOpts,
) -> Result<Diagnostics, Diagnostics> {
  // TODO: Do the checks without having to do full compilation
  let res = compile_book(book, compile_opts, diagnostics_cfg, None)?;
  Ok(res.diagnostics)
}

pub fn compile_book(
  book: &mut Book,
  opts: CompileOpts,

Review comment (suggestion):
Add a comment explaining why this is necessary.
Eg. "`Name` is implemented with inner mutability so it triggers this warning, but we know it works."

Code after:
use crate::{
  fun::{book_to_hvm, net_to_term::net_to_term, term_to_net::Labels, Book, Ctx, Term},
  hvm::{
    add_recursive_priority::add_recursive_priority,
    check_net_size::{check_net_sizes, MAX_NET_SIZE},
    eta_reduce::eta_reduce_hvm_net,
    hvm_book_show_pretty,
    inline::inline_hvm_book,
    mutual_recursion,
    prune::prune_hvm_book,
  },
};
use diagnostics::{Diagnostics, DiagnosticsConfig, ERR_INDENT_SIZE};
use net::hvm_to_net::hvm_to_net;

pub mod diagnostics;
// `Name` triggers this warning, but it's safe because we're not using its internal mutability.
#[allow(clippy::mutable_key_type)]
pub mod fun;
pub mod hvm;
pub mod imp;
pub mod imports;
pub mod net;
mod utils;

pub use fun::load_book::{load_file_to_book, load_to_book};

pub const ENTRY_POINT: &str = "main";
pub const HVM1_ENTRY_POINT: &str = "Main";
pub const HVM_OUTPUT_END_MARKER: &str = "Result: ";

pub fn check_book(
  book: &mut Book,
  diagnostics_cfg: DiagnosticsConfig,
  compile_opts: CompileOpts,
) -> Result<Diagnostics, Diagnostics> {
  // TODO: Do the checks without having to do full compilation
  let res = compile_book(book, compile_opts, diagnostics_cfg, None)?;
  Ok(res.diagnostics)
}

pub fn compile_book(
  book: &mut Book,
Repository: HigherOrderCO/Bend
File: src/hvm/check_net_size.rs (Rust)

Code before:
use super::tree_children;
use crate::{diagnostics::Diagnostics, fun::Name};
use hvm::ast::{Book, Net, Tree};

pub const MAX_NET_SIZE_C: usize = 4095;
pub const MAX_NET_SIZE_CUDA: usize = 64;

pub fn check_net_sizes(book: &Book, diagnostics: &mut Diagnostics, cmd: &str) -> Result<(), Diagnostics> {
  let net_size_bound = match cmd {
    "run-cu" | "gen-cu" | "gen-hvm" => MAX_NET_SIZE_CUDA,
    _ => MAX_NET_SIZE_C,
  };

  diagnostics.start_pass();

  for (name, net) in &book.defs {
    let nodes = count_nodes(net);
    if nodes > net_size_bound {
      diagnostics.add_rule_error(
        format!("Definition is too large for hvm (size={nodes}, max size={net_size_bound}). Please break it into smaller pieces."),
        Name::new(name),
      );
    }
  }

  diagnostics.fatal(())
}

/// Utility function to count the amount of nodes in an hvm-core AST net
pub fn count_nodes(net: &Net) -> usize {
  let mut visit: Vec<&Tree> = vec![&net.root];
  let mut count = 0usize;
  for (_, l, r) in &net.rbag {

Review comment (suggestion):
The CLI should be decoupled from Bend as a library, so we don't want to pass the command as a string.
Instead this value should be part of the compiler options struct, and set by the CLI in `compile_opts_from_cli`

Code after:
use super::tree_children;
use crate::{diagnostics::Diagnostics, fun::Name, CompilerTarget};
use hvm::ast::{Book, Net, Tree};

pub const MAX_NET_SIZE_C: usize = 4095;
pub const MAX_NET_SIZE_CUDA: usize = 64;

pub fn check_net_sizes(
  book: &Book,
  diagnostics: &mut Diagnostics,
  target: &CompilerTarget,
) -> Result<(), Diagnostics> {
  let (net_size_bound, target_lang) = match target {
    CompilerTarget::Cuda => (MAX_NET_SIZE_CUDA, "Cuda"),
    _ => (MAX_NET_SIZE_C, "C"),
  };

  diagnostics.start_pass();

  for (name, net) in &book.defs {
    let nodes = count_nodes(net);
    if nodes > net_size_bound {
      diagnostics.add_rule_error(
        format!("Definition is too large for HVM {target_lang} (size={nodes}, max size={net_size_bound}). Please break it into smaller pieces."),
        Name::new(name),
      );
    }
  }

  diagnostics.fatal(())
}

/// Utility function to count the amount of nodes in an hvm-core AST net
Repository: HigherOrderCO/Bend
File: src/lib.rs (Rust)

Code before:
    RunOpts { linear_readback: false, pretty: false, hvm_path: "hvm".to_string() }
  }
}

#[derive(Clone, Copy, Debug, Default)]
pub enum OptLevel {
  Disabled,
  #[default]
  Enabled,
  Alt,
}

impl OptLevel {
  pub fn enabled(&self) -> bool {
    !matches!(self, OptLevel::Disabled)
  }

  pub fn is_extra(&self) -> bool {
    matches!(self, OptLevel::Enabled)
  }
}

#[derive(Clone, Debug)]
pub struct CompileOpts {
  /// The Bend command
  pub command: String,

  /// Enables [hvm::eta_reduce].
  pub eta: bool,

  /// Enables [fun::transform::definition_pruning] and [hvm::prune].
  pub prune: bool,

  /// Enables [fun::transform::linearize_matches].
  pub linearize_matches: OptLevel,

  /// Enables [fun::transform::float_combinators].
  pub float_combinators: bool,

  /// Enables [fun::transform::definition_merge]
  pub merge: bool,

  /// Enables [hvm::inline].
  pub inline: bool,

  /// Enables [hvm::check_net_size].
  pub check_net_size: bool,

  /// Determines the encoding of constructors and matches.
  pub adt_encoding: AdtEncoding,
}

Review comment (suggestion):
Instead of storing the command, which is a CLI thing, we should store the target architecture.
For now we can use a structured format, like an enum
```
enum CompileTarget {
  C,
  Cuda,
  Unknown,
}
```

Code after:
    RunOpts { linear_readback: false, pretty: false, hvm_path: "hvm".to_string() }
  }
}

#[derive(Clone, Copy, Debug, Default)]
pub enum OptLevel {
  Disabled,
  #[default]
  Enabled,
  Alt,
}

impl OptLevel {
  pub fn enabled(&self) -> bool {
    !matches!(self, OptLevel::Disabled)
  }

  pub fn is_extra(&self) -> bool {
    matches!(self, OptLevel::Enabled)
  }
}

#[derive(Clone, Debug)]
pub enum CompilerTarget {
  C,
  Cuda,
  Unknown,
}

#[derive(Clone, Debug)]
pub struct CompileOpts {
  /// The Compiler target architecture
  pub target_architecture: CompilerTarget,

  /// Enables [hvm::eta_reduce].
  pub eta: bool,

  /// Enables [fun::transform::definition_pruning] and [hvm::prune].
  pub prune: bool,

  /// Enables [fun::transform::linearize_matches].
  pub linearize_matches: OptLevel,

  /// Enables [fun::transform::float_combinators].
  pub float_combinators: bool,

  /// Enables [fun::transform::definition_merge]
  pub merge: bool,

  /// Enables [hvm::inline].
  pub inline: bool,
Repository: HigherOrderCO/Bend
File: src/hvm/check_net_size.rs (Rust)

Code before:
use super::tree_children;
use crate::{diagnostics::Diagnostics, fun::Name, CompileOpts};
use hvm::ast::{Book, Net, Tree};

pub const MAX_NET_SIZE_C: usize = 4095;
pub const MAX_NET_SIZE_CUDA: usize = 64;

pub fn check_net_sizes(
  book: &Book,
  diagnostics: &mut Diagnostics,
  opts: &CompileOpts,
) -> Result<(), Diagnostics> {
  let net_size_bound = match opts.command.as_str() {
    "run-cu" | "gen-cu" | "gen-hvm" => MAX_NET_SIZE_CUDA,
    _ => MAX_NET_SIZE_C,
  };

  diagnostics.start_pass();

  for (name, net) in &book.defs {
    let nodes = count_nodes(net);
    if nodes > net_size_bound {
      diagnostics.add_rule_error(
        format!("Definition is too large for hvm (size={nodes}, max size={net_size_bound}). Please break it into smaller pieces."),
        Name::new(name),
      );
    }
  }

  diagnostics.fatal(())
}

/// Utility function to count the amount of nodes in an hvm-core AST net
pub fn count_nodes(net: &Net) -> usize {
  let mut visit: Vec<&Tree> = vec![&net.root];
  let mut count = 0usize;
  for (_, l, r) in &net.rbag {
    visit.push(l);
    visit.push(r);
  }
  while let Some(tree) = visit.pop() {
    // If it is not 0-ary, then we'll count it as a node.
    if tree_children(tree).next().is_some() {
      count += 1;
    }
    for subtree in tree_children(tree) {
      visit.push(subtree);
    }
  }

Review comment (suggestion):
If you change the command to be a target instead, it would be nice to print it here. Something like `Definition is too large for HVM target {} ...`

Code after:
use super::tree_children;
use crate::{diagnostics::Diagnostics, fun::Name, CompilerTarget};
use hvm::ast::{Book, Net, Tree};

pub const MAX_NET_SIZE_C: usize = 4095;
pub const MAX_NET_SIZE_CUDA: usize = 64;

pub fn check_net_sizes(
  book: &Book,
  diagnostics: &mut Diagnostics,
  target: &CompilerTarget,
) -> Result<(), Diagnostics> {
  let (net_size_bound, target_lang) = match target {
    CompilerTarget::Cuda => (MAX_NET_SIZE_CUDA, "Cuda"),
    _ => (MAX_NET_SIZE_C, "C"),
  };

  diagnostics.start_pass();

  for (name, net) in &book.defs {
    let nodes = count_nodes(net);
    if nodes > net_size_bound {
      diagnostics.add_rule_error(
        format!("Definition is too large for HVM {target_lang} (size={nodes}, max size={net_size_bound}). Please break it into smaller pieces."),
        Name::new(name),
      );
    }
  }

  diagnostics.fatal(())
}

/// Utility function to count the amount of nodes in an hvm-core AST net
pub fn count_nodes(net: &Net) -> usize {
  let mut visit: Vec<&Tree> = vec![&net.root];
  let mut count = 0usize;
  for (_, l, r) in &net.rbag {
    visit.push(l);
    visit.push(r);
  }
  while let Some(tree) = visit.pop() {
    // If it is not 0-ary, then we'll count it as a node.
    if tree_children(tree).next().is_some() {
      count += 1;
    }
    for subtree in tree_children(tree) {
      visit.push(subtree);
    }
  }
Repository: HigherOrderCO/Bend
File: src/lib.rs (Rust)

Code before:
      inline: false,
      check_net_size: self.check_net_size,
      adt_encoding: self.adt_encoding,
    }
  }

  pub fn check_for_strict(&self) {
    if !self.float_combinators {
      println!(
        "Warning: Running in strict mode without enabling the float_combinators pass can lead to some functions expanding infinitely."
      );
    }
    if !self.linearize_matches.enabled() {
      println!(
        "Warning: Running in strict mode without enabling the linearize_matches pass can lead to some functions expanding infinitely."
      );
    }
  }
}

impl Default for CompileOpts {
  /// Enables eta, linearize_matches, float_combinators.
  /// Uses num-scott ADT encoding.
  fn default() -> Self {
    Self {
      target_architecture: CompilerTarget::Cuda,
      eta: true,
      prune: false,
      linearize_matches: OptLevel::Enabled,
      float_combinators: true,
      merge: false,
      inline: false,
      check_net_size: true,
      adt_encoding: AdtEncoding::NumScott,
    }
  }
}

#[derive(Clone, Copy, Debug)]
pub enum AdtEncoding {
  Scott,
  NumScott,
}

impl std::fmt::Display for AdtEncoding {
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    match self {
      AdtEncoding::Scott => write!(f, "Scott"),
      AdtEncoding::NumScott => write!(f, "NumScott"),
    }
  }

Review comment (suggestion):
```suggestion
      target_architecture: CompilerTarget::Unknown,
```

Code after:
      inline: false,
      check_net_size: self.check_net_size,
      adt_encoding: self.adt_encoding,
    }
  }

  pub fn check_for_strict(&self) {
    if !self.float_combinators {
      println!(
        "Warning: Running in strict mode without enabling the float_combinators pass can lead to some functions expanding infinitely."
      );
    }
    if !self.linearize_matches.enabled() {
      println!(
        "Warning: Running in strict mode without enabling the linearize_matches pass can lead to some functions expanding infinitely."
      );
    }
  }
}

impl Default for CompileOpts {
  /// Enables eta, linearize_matches, float_combinators.
  /// Uses num-scott ADT encoding.
  fn default() -> Self {
    Self {
      target_architecture: CompilerTarget::Unknown,
      eta: true,
      prune: false,
      linearize_matches: OptLevel::Enabled,
      float_combinators: true,
      merge: false,
      inline: false,
      check_net_size: true,
      adt_encoding: AdtEncoding::NumScott,
    }
  }
}

#[derive(Clone, Copy, Debug)]
pub enum AdtEncoding {
  Scott,
  NumScott,
}

impl std::fmt::Display for AdtEncoding {
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    match self {
      AdtEncoding::Scott => write!(f, "Scott"),
      AdtEncoding::NumScott => write!(f, "NumScott"),
    }
  }