Introduction:
Welcome to this beginner-friendly tutorial on Gleam, a functional programming language designed to create fast, safe, and concurrent systems. Gleam is a statically typed language that compiles to both Erlang and JavaScript, making it ideal for building scalable, fault-tolerant applications. This tutorial will guide you through the basics of Gleam, helping you write your first Gleam program and understand its core features. In this blog post, we'll explore the fundamental concepts of Gleam and get you started on your journey with this exciting language.
What is Gleam?
Gleam is a statically typed functional programming language that compiles to both Erlang bytecode (for running on the BEAM) and JavaScript. This means you can use it for building robust backends and interactive frontends. Key characteristics include:
- đ Functional: Emphasizes immutability and pure functions.
- đ Statically Typed: Catches errors at compile time.
- đȘ Concurrent: Runs on the BEAM, inheriting its concurrency model.
- đ Interoperable: Works seamlessly with Erlang and Elixir code.
- đ§ Modern Syntax: Clean and expressive, inspired by languages like Elm and OCaml.
Why Learn Gleam?
Before diving into Gleam, let's look at why itâs worth learning:
Safety: Gleamâs strong, static type system catches errors at compile-time, ensuring your code is reliable.
Performance: Gleam leverages the Erlang virtual machine (BEAM), renowned for its low-latency and fault-tolerant properties.
Simplicity: Its concise syntax and focus on practicality make it easy for beginners and experts alike.
Setting Up Your Gleam Environment
Prerequisites
A code editor: Visual Studio Code is recommended.
Erlang/OTP: Install it from Erlang.org.
Gleam compiler: Install via your terminal, refer here:
brew install gleam
Verifying Installation
Run the following command to check if Gleam is installed:
gleam --version
You should see the version number if Gleam is installed correctly. Refer the image below
Writing Your First Gleam Program
Step 1: Create a New Project
Open your terminal and create a new Gleam project:
gleam new hello_gleam
cd hello_gleam
This generates a project structure with a src
directory for your code.
Step 2: Write a Simple Program
Navigate to the src
directory and open main.gleam
. Replace its content with:
import gleam/io
pub fn main() {
let greeting = "Hello, Gleam!"
io.println(greeting)
}
This program defines a function main
that prints a greeting to the console.
Step 3: Run Your Program
Compile and run your program:
gleam build
gleam run
You should see Hello, Gleam!
printed to the console.
Basic Syntax
1. Variables and Data Types
-
Variables: In Gleam, variables are immutable (bindings). Once assigned, their value cannot be changed. Gleam uses
let
for variable binding.
import gleam/io
import gleam/int
import gleam/float
import gleam/bool
pub fn main() {
let name = "Alice"
let age = 30
let height = 5.8
let is_student = False
io.println("Name: " <> name)
io.println("Age: " <> int.to_string(age))
io.println("Height: " <> float.to_string(height))
io.println("Is student? " <> bool.to_string(is_student))
}
Refer the below image for the output
-
Data Types: Gleam has a rich type system. Some basic types:
- String: Text (e.g., "Hello").
- Int: Integers (e.g., 10, -5).
- Float: Decimal numbers (e.g., 3.14).
- Bool: True or False.
Gleam uses type inference, so you often don't need to explicitly declare types.
2. Functions
Functions are essential in Gleam. Here's how you define them
import gleam/io
pub fn greet(name: String) -> String {
"Hello, " <> name <> "!"
}
pub fn main() {
io.println(greet("Bob"))
}
- pub: Makes the function publicly accessible.
- fn: Keyword for defining a function.
- ->: Specifies the return type.
- <>: String concatenation operator.
- main: The entry point of your program.
3. Control Flow: Conditional Expressions
Gleam uses conditional expressions (which return a value) rather than statements:
import gleam/io
pub fn is_adult(age: Int) -> String {
case age >= 18 {
True -> "You are an adult."
False -> "You are not an adult yet."
}
}
pub fn main() {
io.println(is_adult(20)) // Output: You are an adult.
io.println(is_adult(10)) // Output: You are not an adult yet.
}
The case
expression checks a condition and returns a value based on the result. Refer the below image for the output.
4. Data Structures: Lists and Tuples
- Lists: Ordered collections of elements of the same type:
let fruits = ["apple", "banana", "cherry"]
let numbers = [1, 2, 3, 4, 5]
- Tuples: Fixed-size collections of elements of potentially different types:
let person = #("Alice", 30)
let name = person.0 // Access the first element ("Alice")
let age = person.1 // Access the second element (30)
Refer the below image for the example
Core Features of Gleam
Pattern Matching
Pattern matching is a powerful feature in Gleam for working with data structures. It simplifies conditional logic:
import gleam/io
pub fn describe_fruit(fruit: String) -> String {
case fruit {
"apple" -> "It's an apple!"
"banana" -> "It's a banana!"
other -> "It's something else: " <> other
}
}
pub fn main() {
io.println(describe_fruit("apple")) // Output: It's an apple!
io.println(describe_fruit("orange")) // Output: It's something else: orange
}
This concisely handles different cases based on the value of fruit
. Refer the below image for output.
Modules
Gleam uses modules to organize code into logical units. Every Gleam file is a module.
// my_module.gleam
pub fn my_function() -> Int {
10
}
// main.gleam
import my_module
import gleam/io
pub fn main() {
io.println(my_module.my_function())
}
Refer the below image for the output.
Concurrency
Gleam leverages Erlangâs concurrency model to build fault-tolerant systems.
import gleam/io
pub fn spawn_task() {
io.println("Starting task")
}
pub fn main() {
let _ = spawn(spawn_task)
io.println("Task spawned")
}
Practical Example: Building a Calculator
Let's build a simple calculator to demonstrate these concepts:
import gleam/io
import gleam/int
import gleam/string
pub type Operation {
Add
Subtract
Multiply
Divide
}
pub fn calculate(operation: Operation, a: Int, b: Int) -> Result(Int, String) {
case operation {
Add -> Ok(a + b)
Subtract -> Ok(a - b)
Multiply -> Ok(a * b)
Divide -> case b {
0 -> Error("Division by zero!")
_ -> Ok(a / b)
}
}
}
pub fn main() {
case calculate(Add, 5, 3) {
Ok(result) -> io.println("5 + 3 = " <> int.to_string(result))
Error(msg) -> io.println("Error: " <> msg)
}
}
Refer the below image for the output.
Best Practices & Tips
-
Type First Development
- Design your types before implementing functionality
- Let the compiler guide your implementation
-
Use Pattern Matching
- Prefer pattern matching over if/else statements
- Make your code more readable and maintainable
-
Leverage the Type System
- Use custom types to model your domain
- Let the compiler catch errors early
-
Testing
- Write tests using Gleam's built-in test framework
- Run tests with
gleam test
Community Resources
- Official Gleam Documentation
- Gleam Discord Community
- Gleam GitHub Repository
- Code Examples: All the code from this post is available on
Conclusion
Gleam offers a modern, type-safe approach to programming while leveraging the robust Erlang ecosystem. Its friendly compiler messages and clean syntax make it an excellent choice for both beginners and experienced developers. By following this tutorial, youâve taken the first step in mastering Gleam. You've learned about variables, functions, data types, control flow, and pattern matching. This is a great foundation for exploring more advanced concepts.
Top comments (0)