///|
/// Type printing functions
fn print_error_type(
  err_type : @syntax.ErrorType,
  logger : StringBuilder,
) -> Unit {
  match err_type {
    ErrorType(ty~) => {
      logger.write_string(" raise ")
      print_type(ty, logger)
    }
    DefaultErrorType(..) => logger.write_string(" raise")
    MaybeError(..) =>
      // TODO: should check
      logger.write_string(" raise?")
    NoErrorType => ()
    Noraise(..) => logger.write_string(" noraise")
  }
}

///|
fn print_type(ty : @syntax.Type, logger : StringBuilder) -> Unit {
  match ty {
    Any(..) => logger.write_string("_")
    Arrow(args~, res~, err~, is_async~, ..) => {
      if is_async {
        logger.write_string("async ")
      }
      logger.write_string("(")
      print_list(args, print_type, ", ", logger)
      logger.write_string(") -> ")
      print_type(res, logger)
      print_error_type(err, logger)
    }
    Tuple(tys~, ..) => {
      logger.write_string("(")
      print_list(tys, print_type, ", ", logger)
      logger.write_string(")")
    }
    Name(constr_id~, tys~, ..) => {
      print_long_ident(constr_id.id, logger)
      if not(tys.is_empty()) {
        logger.write_string("[")
        print_list(tys, print_type, ", ", logger)
        logger.write_string("]")
      }
    }
    Option(ty~, ..) => {
      print_type(ty, logger)
      logger.write_string("?")
    }
    Object(constr_id) => {
      // For object types, we should print them as &TraitName instead of object TraitName
      logger.write_string("&")
      print_long_ident(constr_id.id, logger)
    }
  }
}