Typed effects · Services
Effect
Silk started with Effect. It showed how far typed errors, services and effects can carry a codebase as it grows. Silk exists because that kind of scalability shouldn’t be limited to high-level languages.
Explicit by design
Working with agents is all about clear contracts. Silk exists to give you that. Every effect function states what it returns, how it can fail and what it touches, down to the allocator. Every variable has a lifetime, so nothing is left to luck.
import silk.core.effect { Effect }
import silk.io.logger { LogError }
import silk.io.logger_native { StdoutLogger }
pub effect fn main() -> ()
! LogError {
let mut logger = StdoutLogger.make()
run Effect.log("Hello, world!\n", &.{})
|> Effect.provideMut(&mut logger)
}$ curl -fsSL https://raw.githubusercontent.com/silk-lang/silk/main/install.sh | sh01 — How it reads
01 · Service
Declare the need
A service names a capability without saying how it’s provided.
03 · Run
Every effect is marked
An effect is a value until run executes it, so you can see where things happen.
import silk.core.effect { Effect }import silk.io.logger { Logger, LogError }import silk.io.logger_native { StdoutLogger }pub service Random {effect fn next(self: &mut Self) -> u32}effect fn program() -> ()! LogError? &mut Random | &mut Logger {run Effect.log("Random number: {number}", &.{number: run Random.next()})}pub struct Always42Random {}impl Random for Always42Random {effect fn next(self: &mut Self) -> u32 {return 42}}pub effect fn main() -> ()! LogError {let mut loggerProvider = StdoutLogger.make()let mut randomProvider = Always42Random {}run program()|> Effect.provideMut(&mut loggerProvider)|> Effect.provideMut(&mut randomProvider)}
02 · Signature
Failures and needs
! says how it can fail, ? what it needs. Leave a need unprovided and it won’t compile.
04 · Implementation
Many providers
Always 42 in tests, a real generator in production. program doesn’t change.
05 · Provide
Provide at the edge
main picks the logger and the random source. Nothing deeper decides.
01 · Service
A service names a capability without saying how it’s provided.
pub service Random {effect fn next(self: &mut Self) -> u32}
02 · Signature
! says how it can fail, ? what it needs. Leave a need unprovided and it won’t compile.
effect fn program() -> ()! LogError? &mut Random | &mut Logger {
03 · Run
An effect is a value until run executes it, so you can see where things happen.
run Effect.log("Random number: {number}", &.{number: run Random.next()})
04 · Implementation
Always 42 in tests, a real generator in production. program doesn’t change.
impl Random for Always42Random {effect fn next(self: &mut Self) -> u32 {return 42}}
05 · Provide
main picks the logger and the random source. Nothing deeper decides.
run program()|> Effect.provideMut(&mut loggerProvider)|> Effect.provideMut(&mut randomProvider)
02 — Acknowledgments
Typed effects · Services
Silk started with Effect. It showed how far typed errors, services and effects can carry a codebase as it grows. Silk exists because that kind of scalability shouldn’t be limited to high-level languages.
Ownership · Borrowing
Rust designed the memory model today’s software needs, years before anyone expected agents to write code in our lifetime. Every value has an owner and every borrow has a lifetime.
Explicit resources · Fast builds
Zig showed how valuable it is to be explicit about system resources. It also showed that incremental compilation and a fast compiler are essential to any loop, especially one measured in tokens per second.
03 — In the box
TLS 1.3, HTTP/2, zstd and SIMD vectors are written in Silk, under the same allocator, failure and service rules as your code. So is memcpy. An HTTPS client needs one dependency: the toolchain.
261
Modules in the standard library
Standards implemented in Silk
Every module · 22 areas
http32
simd30
platform24
tls16
io14
system14
core13
concurrency11
04 — Cross-compilation
Silk compiles to macOS, Linux with or without libc, Windows and WebAssembly, and any host can build for all of them.
| Host → target | macOS | Linux | Linux, no libc | Windows | WASI |
|---|---|---|---|---|---|
| macOS | macOS builds for macOS | macOS builds for Linux | macOS builds for Linux, no libc | macOS builds for Windows | macOS builds for WASI |
| Linux | Linux builds for macOS | Linux builds for Linux | Linux builds for Linux, no libc | Linux builds for Windows | Linux builds for WASI |
| Windows | Windows builds for macOS | Windows builds for Linux | Windows builds for Linux, no libc | Windows builds for Windows | Windows builds for WASI |
Incremental by design
<1 s
to rebuild after a typical edit to the 365k‑line Silk compiler*
Compile speed is a core value, not an optimization pass. Every new feature is weighed by what it costs the rebuild, because a slow loop is a slow loop for people and agents alike.
* Measured on a 16-inch MacBook Pro (2021) with an Apple M1 Max and 32 GB of memory.
05 — Before you start
Status: alpha — the compiler already builds itself
The language, the standard library and the tools are moving fast. Try it today, pin your toolchain, and help shape what Silk becomes.