Historical Context & Motivation
The R programming language grew out of the S language developed at Bell Laboratories in the 1970s, a time when statisticians needed a way to interactively explore data without the rigid compile-link-run cycle of Fortran or C. Ross Ihaka and Robert Gentleman created R in 1993 at the University of Auckland, deliberately designing it around the concept of an interactive console — a read-evaluate-print loop (REPL) that lets users type an expression, see its result immediately, and iterate rapidly. This philosophy of exploratory, conversational computing was a direct inheritance from S and set R apart from the batch-processing paradigm that dominated scientific computing at the time.
The central question that this lesson addresses is deceptively simple: when you write R code, how does it actually get executed? R provides two distinct but complementary modes — the interactive console for rapid experimentation and the .R script file for reproducible, versionable programs. Understanding when and how to use each mode is essential for any serious data science or statistical computing work.
Core Principles & Definitions
Before diving into mechanics, it is important to establish the foundational concepts that underpin R's execution model. Whether you are typing directly into a console or running a saved script, R processes your code through the same interpreter — the difference lies in the source of input and the granularity of execution. The four core principles below define the framework you should use to reason about running R code.
The REPL (Read-Eval-Print Loop)
Script as Persistent Code
Shared Global Environment
source() as the Bridge
source() function reads a .R file and evaluates every expression sequentially in the current session. It is the programmatic equivalent of typing each line into the console.Visual Explanation — The R Execution Pipeline
The following diagram illustrates how R code flows from the two input sources — the interactive console and a .R script file — through the R interpreter and into the global environment. Notice that both paths converge at the R interpreter, which is the unified engine that parses, evaluates, and produces output for every expression regardless of its origin.
As the diagram makes clear, the interpreter does not distinguish between code entered interactively and code fed in from a file. The critical distinction is one of workflow: the console processes one expression per iteration of the REPL loop, whereas source() feeds the interpreter a batch of expressions from the file, evaluating them top to bottom. Both paths ultimately write bindings into the same global environment, which is why you can define a variable in a script and then inspect it from the console prompt within the same session.
How It Works — Console vs. Script Execution in Detail
The Interactive Console (REPL)
When you launch R — whether through a terminal, RGui, or the console pane in RStudio — the interpreter enters its REPL cycle. The prompt character > signals that R is in the Read phase, waiting for input. You type an expression such as sqrt(144) and press Enter. The interpreter evaluates the expression, prints the result ([1] 12), and loops back to the prompt. If your expression is incomplete — for instance, you typed 1 + and pressed Enter — R displays the continuation prompt +, indicating it is still in the Read phase and waiting for the rest of the expression.
invisible() or assign the result to a variable. For example, typing x <- 5 produces no visible output because the assignment operator returns the value invisibly.R Scripts (.R Files)
An R script is simply a plain-text file with a .R extension containing one or more R expressions. You create it in any text editor or the source pane of RStudio. There are three primary ways to execute the code within a script. First, from within an R session, you call source("my_script.R"), which reads the file and evaluates every expression sequentially. Second, in RStudio, you can place your cursor on a line (or select a block) and press Ctrl+Enter (Cmd+Enter on macOS) to send that code to the console for evaluation — a hybrid approach that combines the convenience of a script with the immediacy of the console. Third, from a system shell, you can invoke Rscript my_script.R to run the file in a non-interactive, batch mode, which is critical for automated pipelines and cron jobs.
source(), bare expressions do not auto-print by default. If your script contains just 42 on a line, nothing appears unless you wrap it in print(42) or call source("file.R", echo = TRUE). This is one of the most common surprises for beginners transitioning from console to script.The source() Function in Depth
The source() function accepts several useful parameters. The echo argument, when set to TRUE, causes each expression to be printed to the console before evaluation, mimicking interactive behavior. The local argument, when set to TRUE, evaluates the script in a local environment rather than the global one — analogous to function-scoped execution, which prevents the script from polluting the caller's namespace. The encoding argument lets you specify character encoding (commonly "UTF-8"), ensuring cross-platform compatibility.
Detailed Breakdown — Workflow Patterns and Execution Modes
In practice, R programmers do not exclusively use the console or exclusively use scripts — they follow structured workflow patterns that leverage both execution modes at different stages of a project. The diagram below maps these patterns along the lifecycle of a typical data analysis.
The most productive R workflows treat the console and script as complementary tools rather than alternatives. A typical session begins with exploratory data analysis (EDA) at the console: loading a dataset, inspecting its structure with str(), generating quick plots, and testing transformations. Once a sequence of commands proves useful, the analyst migrates it into a .R script to ensure reproducibility. The script can then be run end-to-end via source(), or individual sections can be sent to the console for targeted debugging. Finally, for deployment on servers or CI/CD pipelines, the script is invoked via Rscript in a fully non-interactive batch mode.
- Ctrl+Enter (RStudio) — sends the current line or selection from the script editor to the console for immediate evaluation
- Ctrl+Shift+Enter (RStudio) — runs the entire script from top to bottom in the console, equivalent to source()
- Ctrl+Shift+S (RStudio) — sources the current script file (same as typing source("file.R") in the console)
Worked Example — From Console Exploration to .R Script
This worked example walks through a realistic scenario: computing the body mass index (BMI) for a small dataset, starting with interactive exploration and finishing with a reproducible script.
> height <- c(1.75, 1.60, 1.82, 1.68)
> weight <- c(70, 55, 90, 65)
These assignment statements return invisibly, so no output appears. Verify by typing > height, which auto-prints the vector.[1] 1.75 1.60 1.82 1.68> bmi <- weight / height^2
Inspect the result by typing > bmi at the prompt.[1] 22.857 21.484 27.170 23.030> round(mean(bmi), 2) to compute the group average BMI. The console auto-prints the scalar result immediately.[1] 23.64bmi_analysis.R. Write the following content:
# bmi_analysis.R — Compute and report BMI
height <- c(1.75, 1.60, 1.82, 1.68)
weight <- c(70, 55, 90, 65)
bmi <- weight / height^2
cat("Individual BMIs:", round(bmi, 2), "\n")
cat("Mean BMI:", round(mean(bmi), 2), "\n")
Note the use of cat() instead of relying on auto-printing, since source() does not auto-print bare expressions.> source("bmi_analysis.R")
All five lines are evaluated sequentially. The cat() calls produce console output. Alternatively, from a system terminal:
$ Rscript bmi_analysis.R
This runs R non-interactively and exits when the script completes.Individual BMIs: 22.86 21.48 27.17 23.03
Mean BMI: 23.64Console vs. Script — Strengths and Limitations
Choosing between the console and a script is not a binary decision but a matter of context. The table below summarizes the trade-offs across several important dimensions that affect everyday R programming.
| Dimension | Console (REPL) | .R Script |
|---|---|---|
| Reproducibility | Low — history is session-bound and hard to reconstruct | High — the entire analysis is captured in a file |
| Iteration Speed | Very fast — immediate feedback per expression | Moderate — requires re-sourcing or selective execution |
| Version Control | Not practical — console input is ephemeral | Excellent — Git tracks every change to the .R file |
| Auto-Printing | Yes — bare expressions auto-print results | No — must use print() or cat() explicitly |
| Collaboration | Difficult — no shareable artifact | Easy — scripts can be shared, emailed, or committed to a repository |
| Automation | Not possible — requires human at the keyboard | Fully supported — Rscript enables cron jobs and CI/CD |
| Best For | EDA, debugging, learning, quick calculations | Production code, reports, automated pipelines, team projects |
Connection to Advanced Execution Paradigms
The console and .R script form the foundation of R's execution model, but modern R extends these primitives into richer paradigms. Understanding the base layer is essential before moving to these more powerful tools, each of which builds directly on the concepts covered in this lesson.
| Paradigm | Based On | Key Enhancement |
|---|---|---|
| R Markdown (.Rmd) | R code chunks embedded in Markdown — each chunk is effectively a mini-script | Literate programming: code, output, and narrative woven into a single document (HTML, PDF, Word) |
| Quarto (.qmd) | Next-generation R Markdown supporting R, Python, Julia, and Observable JS | Cross-language execution with standardized YAML metadata and built-in publishing |
| Shiny Apps | R scripts organized into ui.R, server.R, or a single app.R | Reactive execution: R code re-runs automatically when user inputs change in a web interface |
| R Packages | Collections of .R files in the R/ directory, loaded via library() | Namespaced environments, documentation, testing, and distribution via CRAN |
| targets / make pipelines | DAG-based orchestration of R scripts and functions | Dependency-aware caching: only re-runs steps whose inputs have changed |
Each of these paradigms can be understood as a structured way to organize and trigger the same fundamental operation you have been learning: sending R expressions to the interpreter for evaluation. R Markdown, for instance, extracts code chunks from a document, source()-like evaluates them in order, captures the output, and knits it back into the document. Shiny merely adds a reactive event loop that re-evaluates selected expressions when the user interface signals a change. Mastering the console and script workflow now gives you the mental model needed to reason about all of these tools confidently.
Practice Problems
42 at the R console and having the line 42 inside a .R script that is executed with source("file.R"). Why does this difference exist, and how would you force the script to produce output?greet.R that: (a) assigns your name to a variable name, (b) creates a greeting string using paste0(), and (c) prints it to the console. Then show the exact command to run this script from within an R session.helpers.R that defines a function zscore <- function(x) (x - mean(x)) / sd(x). You want to use this function in another script analysis.R. Explain how to set this up, discuss what happens if you call source("helpers.R", local = TRUE) inside analysis.R, and describe the scoping implications.etl_pipeline.R reads from a database, transforms data, and writes a CSV. Write the cron job entry that runs this script every day at 2:00 AM, and explain the differences between using Rscript versus R CMD BATCH for this use case.Lesson Summary
R provides two fundamental code execution modes. The interactive console implements a REPL (read-eval-print loop) that processes one expression at a time, automatically prints results, and provides an immediate feedback loop ideal for exploration, debugging, and learning. The .R script file stores a sequence of expressions in a plain-text file that can be executed via source() within a session, sent line-by-line with Ctrl+Enter in RStudio, or run non-interactively via Rscript for batch automation.
Both modes feed expressions to the same R interpreter and share the same global environment. The key behavioral difference is auto-printing: the console prints results automatically while scripts require explicit print() or cat() calls. The optimal workflow iterates between console-based exploration and script-based codification — explore, codify, test, and automate — forming the foundation upon which advanced paradigms like R Markdown, Quarto, Shiny, and package development are built.