Using Gleam’s @derive Macro to Auto‑Generate Protocol Implementations
Learn how Gleam’s @derive macro auto‑generates Inspect and Encoder implementations for public structs, why it’s a practical choice, and the limits to keep in mind.
11 Jan 2026, 04:15 UTC

Why Use @derive?
When you define a public struct, you often need to implement several protocols such as Inspect for debugging or Encoder for JSON serialization. Writing each impl manually is tedious, error‑prone, and hard to maintain. The @derive macro inserts the required boilerplate at compile time, guaranteeing type safety and reducing duplication.
How the Macro Works
The macro lives in the core Gleam library and accepts a list of protocol names:
import gleam/inspect.{Inspect}
import gleam/json.{Encoder}
@derive [Inspect, Encoder]
pub struct User {
pub name: Text,
pub age: Int,
}
During compilation, Gleam expands this into two impl blocks:
impl Inspect for Userthat prints the struct as"User { name: "Alice", age: 30 }"impl Encoder for Userthat serializes the struct to JSON using field names as keys.
Because the macro inspects the struct’s public fields, the generated code respects encapsulation and will fail to compile if you attempt to derive on a private or opaque type.
Practical Usage and Verification
1. Create a module user.gleam with the code above.
2. Run gleam build to compile. If the macro expands correctly, the build will succeed.
3. In a test module or REPL, call:
import gleam/io.{print}
import gleam/json.{Encoder, encode}
pub fn main() {
let user = User { name: "Alice", age: 30 }
print(inspect(user))
print(encode(user))
}
Expected output (simplified):
User { name: "Alice", age: 30 }
{"name":"Alice","age":30}
These calls compile because the macro has injected the necessary implementations. Adding a new protocol is a single line change:
@derive [Inspect, Encoder, Hash]
Rebuild and the compiler will generate an impl Hash for User automatically.
Limits, Common Mistakes, and When to Write Manually
- Public structs only:
@derivecannot operate on private or opaque structs. Attempting to derive will result in a compile‑time error. - No custom logic: The macro uses field names verbatim. If you need to rename a key or transform a value before encoding, you must implement
Encoderyourself. - Compile‑time cost: Each derived protocol adds generated code, which can increase compilation time for large structs or numerous protocols.
- Testing the output: While the macro guarantees type correctness, you should still write unit tests to confirm that the generated JSON or string representation matches your expectations.
Use @derive when you need straightforward, default behavior on public structs. If you require custom serialization, field renaming, or special formatting, implement the protocol manually or combine manual code with @derive for the remaining protocols.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.