Hello World in Common-lisp: Complete Solution & Deep Dive Guide

Hello World text

The Complete Guide to "Hello, World!" in Common Lisp: Your First Step into a Powerful Language

To create a "Hello, World!" program in Common Lisp, you define a function using (defun hello-world () ...) that returns the string "Hello, World!". The language's core principle is that the last evaluated expression inside a function is its return value, making the implementation elegantly concise.

Stepping into the world of Common Lisp can feel like learning a language from another dimension. You're immediately confronted with a sea of parentheses and a syntax that seems to defy convention. This initial shock is a universal experience, but it guards a secret: Lisp's unique structure is the source of its unparalleled power and elegance. You're not just learning a new language; you're learning a new way to think about code.

This guide is designed to be your trusted companion on this first, crucial step. We will transform that initial confusion into a moment of clarity by building the classic "Hello, World!" program. By the end, you won't just have working code; you'll understand the fundamental philosophy of Lisp—S-expressions, function definitions, and the interactive REPL—setting a solid foundation for your entire journey through the kodikra learning path.


What is the "Hello, World!" Program? A Universal Rite of Passage

The "Hello, World!" program is the traditional first program for anyone learning a new programming language. Its purpose is deceptively simple: to make the computer display the phrase "Hello, World!". While trivial in function, its true value lies in acting as a comprehensive sanity check for your entire development environment.

Successfully running this program confirms several critical things:

  • Compiler/Interpreter: Your Common Lisp implementation (like SBCL, CLISP, or CCL) is installed correctly and is capable of executing code.
  • File System: You can create, save, and edit a source code file (e.g., hello-world.lisp).
  • Execution Flow: You understand the basic commands to load your file into the Lisp environment and run the function you've defined.
  • Core Syntax: You have grasped the most fundamental syntactic element of the language, which in Lisp's case, is the S-expression.

In essence, "Hello, World!" isn't about the output string. It's about building a bridge between your intentions and the machine's execution. It's the first successful communication, proving that your setup works and you're ready to tackle more complex challenges from the exclusive kodikra.com curriculum.


Why is Common Lisp's Syntax So Unique? Understanding S-Expressions

Before writing a single line of code, we must address the elephant in the room: the parentheses. Common Lisp's syntax is built entirely on a concept called S-expressions (Symbolic Expressions). An S-expression is either an atom (like a number 42, a string "hello", or a symbol my-variable) or a list of other S-expressions enclosed in parentheses.

This leads to a simple, consistent rule: prefix notation. Unlike languages where the operator goes between operands (1 + 2), in Lisp, the operator (which is just a function) always comes first: (+ 1 2). This applies to everything, from arithmetic to function definitions.

The Core Idea: Code is Data (Homoiconicity)

The most profound consequence of this design is a property called homoiconicity. This intimidating term means that the code itself is represented using the language's primary data structure—the list. The expression (defun hello-world () "Hello, World!") is not just text; it's a list containing the symbol defun, the symbol hello-world, an empty list (), and the string "Hello, World!".

Because code is just data, you can write Lisp programs that write other Lisp programs. This is the foundation of Lisp's legendary macro system, which allows you to extend the language itself to solve your specific problems. While that's an advanced topic, your "Hello, World!" journey begins by embracing this foundational structure.


How to Write and Execute "Hello, World!" in Common Lisp

Now, let's translate theory into practice. We will create a function that, when called, provides the required string. This is the canonical solution for the kodikra module.

The Solution Code

Create a file named hello-world.lisp and add the following code.

;;; hello-world.lisp
;;; This file contains the solution for the "Hello, World!" module.

;; We define a package for our solution to avoid symbol clashes.
;; This is good practice in larger Common Lisp projects.
(defpackage #:hello-world
  (:use #:cl)
  (:export #:hello))

;; We switch to our newly defined package.
(in-package #:hello-world)

;;; The core function definition.
(defun hello ()
  "This function takes no arguments and returns the string \"Hello, World!\"."
  "Hello, World!")

Detailed Code Walkthrough

Let's dissect this code piece by piece to understand what's happening.

  1. (defpackage #:hello-world ...): This defines a "package". Think of a package like a namespace in other languages (e.g., in Python or Java). It helps prevent naming conflicts. We're creating a package named hello-world and exporting the symbol hello so it can be called from outside this package.
  2. (in-package #:hello-world): This command tells the Lisp compiler that all subsequent definitions belong to the hello-world package.
  3. (defun hello () ...): This is the main event. It's an S-expression that defines a function.
    • defun: A special operator (a macro, technically) that stands for "Define Function". It's the standard way to create a named function.
    • hello: This is the name of our function. In Common Lisp, the convention is to use kebab-case for multi-word names.
    • (): This is the parameter list. Since our function doesn't need any input, the list is empty.
  4. "This function takes no arguments...": This is an optional documentation string, or "docstring". It's excellent practice to include one, as it allows other developers (and your future self) to understand the function's purpose instantly.
  5. "Hello, World!": This is the entire body of the function. In Common Lisp, functions have an implicit return. The value of the last expression evaluated in the function's body is automatically returned. Here, the only expression is the string "Hello, World!", so that string becomes the function's return value.

ASCII Art: Anatomy of a defun

This diagram illustrates how the Lisp system interprets the defun form to create a new function in memory.

    ● Lisp Reader encounters S-expression
    │
    ▼
  ┌───────────────────────────────┐
  │ `(defun hello () ...)`        │
  └──────────────┬────────────────┘
                 │
                 ▼
    ◆ Is the first element `defun`?
   ╱           ╲
 Yes            No
  │              │
  ▼              ▼
[Process as Function Definition] [Evaluate as normal function call]
  │
  ├─ 1. Identify Name: `hello`
  │
  ├─ 2. Identify Parameters: `()` (empty list)
  │
  ├─ 3. Identify Body: `"Hello, World!"`
  │
  └─ 4. Store in Environment:
     The symbol `hello` is now
     bound to the compiled code.
                 │
                 ▼
    ● Function `hello` is now callable.

Where and How Do You Run Lisp Code? The Power of the REPL

Common Lisp development is highly interactive, revolving around the REPL: Read-Eval-Print Loop. This is a command-line environment where you can type Lisp expressions, have the system evaluate them, print the result, and wait for your next command.

Setting Up Your Environment

First, you need a Common Lisp implementation. A popular, high-performance, open-source choice is Steel Bank Common Lisp (SBCL). You can install it via your system's package manager.

On macOS (using Homebrew):

brew install sbcl

On Debian/Ubuntu:

sudo apt-get update
sudo apt-get install sbcl

Running the Code

Once SBCL is installed, follow these steps:

  1. Open your terminal and start the SBCL REPL by typing:

    sbcl

    You'll be greeted by a prompt, which is usually an asterisk (*).

  2. Load your file into the running Lisp image. Assuming your hello-world.lisp file is in the same directory where you started SBCL, type:

    * (load "hello-world.lisp")

    SBCL will compile and load your file, and you should see a response like T (which means "true" or success).

  3. Now, call your function. Since it's in a package, you need to use the package name to access it:

    * (hello-world:hello)
  4. The REPL will execute the function, and because the function returns a string, the REPL will print that return value to the screen:

    "Hello, World!"
    * 

    Congratulations! You have successfully executed your first Common Lisp program.

ASCII Art: The REPL Cycle

This diagram shows the interactive flow of the Read-Eval-Print Loop.

    ● User at Prompt `*`
    │
    │  Types: `(hello-world:hello)`
    │
    ▼
  ┌───────────┐
  │   READ    │  Reads the text as a Lisp list data structure.
  └─────┬─────┘
        │
        ▼
  ┌───────────┐
  │   EVAL    │  Evaluates the list: looks up `hello-world:hello`,
  └─────┬─────┘  finds it's a function, and executes its code.
        │        The function's code returns the string "Hello, World!".
        ▼
  ┌───────────┐
  │   PRINT   │  Prints the result of the evaluation to the screen.
  └─────┬─────┘
        │        Output: "Hello, World!"
        │
        ▼
    ● Loop back to Prompt `*` for next command.

When to Use Different Approaches: Returning vs. Printing

Our solution returns a string value, which is considered a "pure" approach. The function's only job is to compute and provide data. However, you might also see solutions that directly print to the console. This is an important distinction related to side effects.

Alternative Approach: Using format for Direct Output

You could write the function to actively print the string instead of returning it. The primary tool for this is the format function.

(defun hello-with-printing ()
  "This function prints \"Hello, World!\" to standard output and returns NIL."
  (format t "Hello, World!"))

Let's break down (format t "Hello, World!"):

  • format: The versatile Common Lisp function for producing formatted output.
  • t: The destination for the output. t is a special symbol representing the standard terminal output stream.
  • "Hello, World!": The control string to be printed.

When you run (hello-with-printing) in the REPL, you'll see "Hello, World!" printed, but the return value will be NIL (Lisp's version of "null" or nothing). The format function's job is to cause a side effect (printing), and it returns NIL by convention.

Pros and Cons: Purity vs. Side Effects

Choosing between these two approaches depends entirely on your goal. Here’s a table to clarify the trade-offs:

Aspect Implicit Return (Pure) format (Side Effect)
Core Purpose To compute and return a value. To perform an action (like printing to the screen).
Use Case Ideal for functions that are part of a larger computation. The result can be passed to other functions. Useful for top-level functions that need to display information to the user.
Testability High. It's easy to write a test that checks if (hello) equals "Hello, World!". Lower. Testing requires capturing standard output, which is more complex than checking a return value.
Composability Excellent. You can easily combine it, e.g., (string-upcase (hello)) to get "HELLO, WORLD!". Poor. The function returns NIL, so it cannot be chained into other data-processing functions.
Return Value The string "Hello, World!". NIL.

For the challenges in the kodikra curriculum, the testing framework almost always expects a return value. Therefore, the first, pure-function approach is the correct one. Understanding this difference between returning data and performing an action is a fundamental concept in functional programming and a key takeaway from this first module.


Frequently Asked Questions (FAQ)

Why are there so many parentheses in Lisp?
The parentheses define the structure of the code itself. They create lists (S-expressions) which makes the syntax extremely consistent and simple. Once you get used to it, this regularity makes the code easy to parse, both for the computer and for the human reader, and enables powerful metaprogramming through macros.
What does defun stand for?
defun is a contraction of "DEfine FUNction". It is the standard macro used in Common Lisp to create a global, named function.
Is Common Lisp case-sensitive?
By default, the Common Lisp reader converts all unquoted symbols to uppercase. This means (defun hello ...) and (DEFUN HELLO ...) are treated as identical. So, for practical purposes, it's case-insensitive, but the underlying reality is case-conversion. This is why Lisp code is often written in lowercase, for readability.
What is the difference between returning a string and printing it?
Returning a value makes it available for other parts of your program to use. Printing a value displays it on the screen for a human to see but does not make it programmatically available (the function typically returns NIL). Pure functions that return values are easier to test and compose.
Why is the naming convention kebab-case?
The hyphen (-) is an allowed character in Lisp symbols, unlike in many other languages where it's a subtraction operator. This historical convention improves readability for multi-word names, as opposed to camelCase or snake_case.
What are t and NIL?
t is the canonical symbol for "true". In boolean contexts, anything that is not NIL is considered true. NIL is the symbol for "false" and also represents the empty list (). They are fundamental atoms in the Lisp world.
Can I write the "Hello, World!" function on a single line?
Absolutely. Lisp is not sensitive to whitespace in the same way languages like Python are. The expression (defun hello () "Hello, World!") is perfectly valid. Multi-line formatting is purely for human readability.

Conclusion: Your Journey Has Begun

You've done more than just print a string to the screen. You have successfully navigated the unique syntax of Common Lisp, defined a function, understood the critical difference between returning a value and producing a side effect, and interacted with the powerful REPL. These concepts—S-expressions, defun, implicit returns, and interactive development—are not just quirks; they are the pillars upon which the entire language is built.

The initial hurdle of parentheses is now behind you, replaced with a foundational understanding of Lisp's structure and philosophy. You've proven your environment is ready and that you can translate logic into valid Lisp code. This small victory is the launchpad for exploring the more advanced and powerful features of the language.

Ready to apply these newfound skills? Continue your adventure and tackle the next challenge in the Kodikra Common Lisp Learning Path. To learn more about the language's features and history, explore our comprehensive Common Lisp guide.

This guide uses syntax and concepts compatible with modern ANSI Common Lisp implementations like SBCL 2.4+. The Common Lisp standard is exceptionally stable, so these fundamentals are timeless, but always refer to the documentation for your specific Lisp environment for implementation-specific details.


Published by Kodikra — Your trusted Common-lisp learning resource.