A curated standard-library companion for Lean 4 — external concepts ported in, with everything Lean already provides stripped out.
721 modules · 379 compile-time theorems · 9616 #guard checks
linen is a small, opinionated extension of the Lean 4 standard library. When a
useful concept exists in another ecosystem (a Haskell package, another Lean
project, a single module), it is ported in — but every concept that already
has a Lean standard-library equivalent is replaced with that equivalent, and
the result is reshaped to follow Lean's own module hierarchy and naming. What
ships is only what core genuinely lacks, written as idiomatic Lean on top of the
standard library.
Three rules hold across the whole library:
- Stdlib-first — no bespoke re-implementation of anything core provides
(e.g.
Idover a hand-rolled identity functor,· >=> ·over a custom Kleisli combinator,List.foldlMoverfoldM). - No
partial, nosorry— all recursion is structural or has a proven termination argument, and proofs are complete. - Everything is tested — each module has a
Tests/counterpart whose#guardexamples run on every build.
linen covers the following areas — see docs/MODULES.md
for the full per-module feature list and module table.
Data.Functor/Control— functor, applicative & monad constructions missing from core (Compose/Product/FunctorSum,Bifunctor,Foldable/Traversable,mtl-styleReader/State/Except, STM, green-thread concurrency, …).Control.Lens— alens-style profunctor-optics library (plus itsprofunctors/indexed-traversableprerequisites):Lens/Prism/Iso/Traversal/Fold/Getter/Setter/Reviewand indexed variants, withIxed/At/Each/Cons/Snoc/Wrappedinstances over most existing containers;makeLenses-generated accessors are hand-written per field instead, since Lean has no Template Haskell.Data.ByteString*/Data.String/Data.Word8— byte strings (strict, lazy, short, builder), Base64, case-insensitive text, and ASCII byte classification.Data.Json— a tiny JSON library withToJSON/FromJSONand proven encode→decode round trips.Data.Map/Data.Set/Data.IntMap/Data.List'/Data.List.NonEmpty/ … — Haskell-style container and list APIs over coreStd/Listtypes.Time— atime-style calendar/clock layer over Lean's ownStd.Time: Julian-calendar conversion, Gregorian/Orthodox Easter dates, absoluteMonth/Quartercounters,(months, days)/(months, Duration)calendar periods, UT1 mean-solar-time conversion, and TAI↔UTC leap-second conversion — the piecesStd.Timeitself doesn't cover;Data.Time.*'s day/UTC-instant/time-of-day types areStd.Time.Date.PlainDate/Std.Time.Duration/Std.Time.Zonedunderneath.Network.HTTP/HTTP2/HTTP3/Socket/TLS/QUIC/WebSockets— a full network stack: HTTP/1.1 client & wire types, HTTP/2 framing + HPACK, HTTP/3 over QUIC + QPACK, POSIX sockets with a green-thread event dispatcher, TLS 1.2/1.3 over OpenSSL, and WebSockets.Network.WebApp/Network.WebApp.Server— a WAI-style application interface plus an HTTP server implementing it.Web.Html/Web.Css— typed HTML5/CSS construction where illegal nesting and property/value mismatches are compile-time errors, withelem!andrule!macro sugar.DataFrame— typed tabular data with a proven rectangular invariant, CSV I/O, joins, sorting, grouping/aggregation, and statistics.Database.PostgreSQL/Database.SQL— libpq FFI bindings and a hasql-style typed client (encoders/decoders, sessions, pooling).Database.SQLite3—direct-sqlite-style FFI bindings to a vendored SQLite amalgamation (no pkg-config), with anExcept Error-returningDirectlayer and a publicIO-throwing API.Database.SQLite.Simple— asqlite-simple-style mid-level client:Query/Only/row-cons types, the error-accumulatingOkapplicative, SQLite date/time text parsing & rendering,Connection/Statement/Fieldconnection plumbing, and theToField/FromField/ToRow/FromRowparameter/row conversion classes (tuple instances up to arity 7); a public facade (withConnection,query/query_/execute/execute_, streamingfold/fold_,withTransaction/withSavepoint,lastInsertRowId/changes); thesql "…"syntax/macro_rulesquasiquoter substitute; and user-defined scalar SQL function registration (createFunction0–createFunction3/deleteFunction) via a new Lean-closure-called-from-Csqlite3_create_function_v2bridge.Database.DuckDB.FFI— low-levelduckdb-ffi-style FFI bindings tolibduckdb: connection/query lifecycle, prepared statements & the appender,DataChunk/Vector/validity-mask access to result data, the logical-type system (primitive/LIST/ARRAY/MAP/STRUCT/UNION/ENUM/DECIMAL), catalog/config/error/logging helpers, and user-defined scalar SQL function registration via a Lean-closure-called-from-C trampoline (mirroring the one built forDatabase.SQLite.Simple).Database.DuckDB.Simple— aduckdb-simple-style mid-level client atopDatabase.DuckDB.FFI: the error-accumulatingOkapplicative,Query/Only/row-cons types, theToField/FromField/ToRow/FromRowparameter/row conversion classes (tuple instances up to arity 7) including DuckDB'sSTRUCT/UNION/LIST/MAP/ENUM/DECIMALlogical-type decode support and hand-writtenSTRUCT/UNIONdecode combinators standing in for GHC-generics-derived instances; catalog/config/file-system/logging helpers and user-defined scalar SQL function registration; and a public facade (withConnection,query/query_/execute/execute_, streamingfold/fold_,withTransaction) built on the appender-free prepared-statement/DataChunkfetch pipeline. Completes thesqlite-simple→duckdb-ffi→duckdb-simpleimport chain.Database.Redis— ahedis-style Redis client: the RESP2 wire protocol (Replyencoder/parser overStd.Internal.Parsec.ByteArray) and request/response pipelining overNetwork.Socket/Network.TLS; theRedismonad withRedisCtx/MonadRedis; the full command surface (simple + irregularManualCommands);ConnectInfo/redis://-URL parsing and anIO.Ref-guarded connection pool;MULTI/EXEC/WATCHtransactions; Pub/Sub (publish,pubSub,pubSubForever); Redis Cluster (CRC16 hash slots, topology,MOVED/ASKredirects); and Sentinel-based master discovery with failover — no separate prerequisite Hackage package needed, every dependency resolving onto the Lean stdlib, an existinglinenport, or a small inlined slice.Data.Stream— astreamly-core-style stream-fusion library: the fused, direct-styleStream(aYield/Skip/StopStep-machine stepper) and the CPS-encodedStreamK, bridged both ways;Fold/Scanlterminating left folds and scans,Unfold/Producergenerators, and a backtracking streamingParser; plus an unboxed-array subsystem (MutByteArray/Unbox,MutArray,Array.Unboxed). A distinct streaming paradigm from the existingConduitport — the fused data encoding is reproduced faithfully, without GHC'sfusion-pluginoptimizer that eager Lean has no analogue for; bounded tostreamly-core, deferring the concurrentSVarscheduler.Text.DocLayout— a Wadler/Leijen-style pretty-printer: theDocdocument algebra (literal,<+>,$$,vcat,nest,hang,flush, side-by-side blocks, ANSI/OSC-8 styling) and itsrender/renderANSIline- wrapping engine, plus theHasChars/Attributed/ANSIFontlayers beneath it.linen's first document-layout algebra, and a blocking prerequisite ofpandoc— every pandoc writer renders through it.Text.Pandoc— apandoc-style universal document converter: the shared AST (Pandoc/Block/Inline, folded in frompandoc-types) with itsWalk/Generictraversals andBuildercombinator API; the shared reader/writer infrastructure (Extensions,Options,Parsing,Shared,Templates,Writers.Shared'sgridTable) and the purePandocMonad/PandocPure; and a working Markdown↔AST↔HTML round-trip (Readers/Writers.{Markdown,HTML}) plus the AST-nativeNative/JSONformats, dispatched through a top-levelgetReader/getWriter/convertfacade.tagsoup(the HTML tokenizer) and a YAML front-matter parser had no other consumer, so each is folded in as a bounded slice directly inside its one reader module rather than imported as its own package;blaze-htmlsubstitutes onto the existingWeb.Html. Scoped to the AST plus Markdown and HTML (both directions) — the long tail of exotic formats, binary/zip formats, the Lua-filter system, syntax highlighting, math typesetting, citations, and templating are all deferred.Crypto.JOSE— JOSE/JWT verification (HMAC/RSA/EC) over OpenSSL.Network.OAuth2— ahoauth2-style OAuth2 client: authorization-code, client-credentials, device-authorization, JWT-bearer, resource-owner-password and refresh-token grants; PKCE (S256) via two new OpenSSL-backedCrypto.SHA256/Crypto.SecureRandomFFI primitives; typed request-builder application config; and token/user-info/device-authorization HTTP flows overNetwork.HTTP.Client.Conduit.Crypto.Zlib/Crypto.MD5/Crypto.RC4/Crypto.AES— zlib inflate, RFC 1321 MD5, the RC4 stream cipher, and AES-128 CBC decryption- PKCS5 unpadding — the primitives behind the PDF Standard Security Handler.
System.Keychain— OS credential-store access (macOS Keychain, Linux Secret Service, Windows Credential Manager).Data.PDF.Stream/Data.PDF.Core/Data.PDF.Content/Data.PDF.Document— PDF parsing, rendering, and text extraction: a buffer-residentio-streamsport, the low-level object model/parser/ xref/encryption layer, content-stream operators and font/text-encoding handling, and a document/page-tree API with text extraction.Options.Applicative—optparse-applicative-style command-line argument parsing.PostgREST— a Lean port of PostgREST's request/response pipeline: API request parsing, config, schema cache introspection, query planning, auth, and OpenAPI generation.CDP— a Chrome DevTools Protocol client: typed commands/events/types for every protocol domain, plus a WebSocket-based runtime to connect to a browser, send commands, and subscribe to events.Data.Colour— acolour-style colour library:Colour/AlphaColourblending, CIE chromaticity/illuminants, RGB colour spaces (HSL/HSV, sRGB), and named SVG colours.Data.Array.Shaped— arepa-style rank-polymorphic, shape-indexed array library:Delayed/Manifest/Cursored/Partitioned/Undefinedrepresentations, index-space operators, and stencil-based convolution.System.Console.Ansi/System.Exit/System.Log.FastLogger— terminal styling, process exit codes, and buffered logging.Graphics.Netpbm— anetpbm-style parser for the PBM/PGM/PPM "portable anymap" image formats (ASCII and binary variants, magic numbersP1–P6) overByteArray.Codec.Picture— aJuicyPixels-style image codec suite: pixel/image types and colorspace conversions, plus PNG, JPEG (baseline + progressive), GIF (including animation), BMP, TGA, TIFF, and Radiance HDR encoders/ decoders, Exif/TIFF/JFIF metadata, and median-cut colour quantization.Graphics.Image— ahip-style image-processing library:Y/RGB/HSI/CMYK/YCbCr/complex/binary colour spaces over a shape-indexed pixel array; geometric transforms (rotate/scale/translate/crop) with nearest-neighbour/bilinear interpolation; kernel convolution and named filters (Sobel, Gaussian, Laplacian, …); binary morphology (erode/dilate/ open/close); FFT-based complex-image processing; adaptive histogram equalization; Hough-transform line detection; salt-and-pepper noise generation; and PNG/JPEG/GIF/BMP/TGA/TIFF/HDR and PNM/PGM/PPM file I/O viaCodec.Picture/Graphics.Netpbm.
Add to your lakefile.toml:
[[require]]
name = "linen"
git = "https://github.com/typednotes/linen"
rev = "main"Then import what you need:
import Linen.Data.Functor
import Linen.Control.Monad
open Data.Functor Control.Monad
#eval join (some (some 3)) -- some 3
#eval replicateM 3 (some 7) -- some [7, 7, 7]See docs/MODULES.md for the full module table (all 721 modules).
lake build # build the library
lake build Tests # run every #guard / #eval checkExample programs live under Examples/ and share one entrypoint,
lake exe examples <name> [args...] (run with no name to list them):
lake exe examples # list the available examples
lake exe examples echo # green-threaded echo server — self-checking demo (exits 0)
lake exe examples echo serve 9099 # run the echo server forever; then: nc 127.0.0.1 9099
lake exe examples bench # network round-trips w/ a few-ms server delay: Green vs blocking pool (same #cores threads)
lake exe examples postgrest # in-memory PostgREST request handling + OpenAPI spec generation — self-checking demo
lake exe examples quic # QUIC types/config + HTTP/3 QPACK/frame wire round trip — self-checking demo
lake exe examples recv # Network.Socket.Blocking accept/connect/send/recv round trip — self-checking demo
lake exe examples resourcet # Control.Monad.Trans.Resource LIFO cleanup over real scratch files — self-checking demo
lake exe examples conduit # Data.Conduit / Data.Conduit.Combinators pipelines, incl. bracketP/runConduitRes — self-checking demo
lake exe examples stm # Control.Monad.STM + Concurrent.STM.{TVar,TMVar,TQueue} — self-checking demo
lake exe examples streaming-commons # Data.Streaming.Network bindPortTCP/getSocketTCP/acceptSafe/AppData round trip — self-checking demo
lake exe examples streaming-commons serve 9098 # run it forever; then: nc 127.0.0.1 9098
lake exe examples tls # Network.TLS.Context handshake over loopback against a self-signed cert — self-checking demo
lake exe examples httpclient # Network.HTTP.Client connect/request/response + redirect-following, over loopback — self-checking demo
lake exe examples httpconduit # Network.HTTP.Client.Conduit / Network.HTTP.Simple streaming HTTP, over loopback — self-checking demo
lake exe examples req # Network.HTTP.Req type-safe req/runReq (HttpBodyAllowed-checked GET/POST), over loopback — self-checking demo
lake exe examples webapp # Network.WebApp: Application/Middleware/AppM (composeMiddleware/ifRequest/modifyResponse), over loopback — self-checking demo
lake exe examples webappstatic # Network.WebApp.Static: staticApp/static + defaultFileServerSettings over a real scratch directory — self-checking demo
lake exe examples vault # Data.Vault type-safe heterogeneous map: typed keys, adjust/delete/union — self-checking demo
lake exe examples vector # Data.Vector-derived Array combinators: generate/ifilter/folds/reductions/backpermute/slice — self-checking demo
lake exe examples todo # Web.Html/Web.Css typed TODO list over Network.WebApp.Server — self-checks, then keeps serving; try: curl localhost:<port>
lake exe examples todo check # same self-check round trip, but exits instead of staying up (for scripting)The echo example exercises the whole socket stack end-to-end — a green accept
loop forks a green handler per connection, each suspending on
recvGreen/sendAllGreen (via the kqueue/epoll EventDispatcher) instead of
holding an OS thread, so one small worker pool serves many connections. Adding
an example is a new module under Examples/ plus one line in the registry in
Examples/Main.lean.
The quic example demonstrates the HTTP/3-over-QUIC wire format end-to-end —
Network.HTTP3.QPACK.Encode/Frame.encode producing bytes that
Frame.decode/Network.HTTP3.QPACK.Decode reproduce exactly — without
needing a live connection, since Network.QUIC.Client/Server are stubbed
pending TLS 1.3 FFI. It also calls Client.connect/Server.run/Server.accept
directly and checks that each fails with exactly its documented
"not yet implemented" error, so the demo stays honest about what is and isn't
wired up yet.
The stm example puts ten green tasks through a thousand atomically
increments each of a shared TVar, hands values between a producer and
consumer through an empty TMVar, checks TQueue's FIFO order survives its
two-list representation, and shows orElse falling through to its alternative
on retry.
The streaming-commons example drives Data.Streaming.Network's AppData
abstraction over a real loopback connection; streaming-commons serve <port>
runs runTCPServer forever for manual testing with nc.
The tls example runs a full TLS 1.2/1.3 handshake over loopback against a
self-signed CN=localhost certificate, trusting it directly as its own CA via
createClientContextWithCA so the demo stays fully offline. It also documents
a real API limitation: getAlpn always reports none, because setAlpn only
registers the server's selection callback — nothing in the current client API
calls SSL_set_alpn_protos to advertise a protocol list for it to select from.
The httpclient and httpconduit examples each stand up a tiny hand-rolled
HTTP/1.1 server over a real loopback socket and drive it with a different
layer of the client stack: httpclient uses Client.connectPlain +
Client.performRequest directly, then Client.execute to show a 302 Found
→ /final redirect followed automatically; httpconduit uses
Simple.parseUrl!/httpBS, the callback-scoped Client.Conduit.withResponse,
and Client.Conduit.httpSource streamed through a .| sinkList conduit
pipeline.
The req example exercises Network.HTTP.Req's type-safe client — a GET
with NoReqBody and a POST with a ReqBodyBs payload, both against a
loopback server, both admitted by the HttpBodyAllowed typeclass at compile
time (swapping a body onto the GET would instead fail to compile, since
there is no HttpBodyAllowed .NoBody .YesBody instance).
The webapp example drives a Network.WebApp.Application through the same
kind of hand-rolled loopback HTTP/1.1 server as httpclient/req, but this
time the request handler itself is the thing under test: raw bytes are parsed
into a Request, run through the application via Green.block, and the
resulting Response serialized back. The demo application composes an echo
handler with a /health route and a Server header, entirely from
Middleware combinators — composeMiddleware, ifRequest,
modifyResponse, addHeader — the same combinators the algebraic-law
theorems in Network.WebApp (idMiddleware_comp_left/_right,
modifyResponse_id, ifRequest_false) prove associative/identity laws for.
The webappstatic example serves a real scratch directory through
Network.WebApp.Static.static (defaultFileServerSettings + staticApp),
reusing webapp's loopback harness — including its Sendfile.sendFile path
for .responseFile, since defaultFileServerSettings serves files that way
rather than buffering them into a .responseBuilder. It checks a direct file
hit (with its Cache-Control: max-age=3600 default), a directory request
redirected to index.html, a 404 for a missing path, and a 403 for a
dotfile-shaped path segment (rejected by Piece's no_dot invariant before
any filesystem lookup runs).
The vault example mints distinctly-typed keys with Key.new and stores
unrelated payloads under each in the same Vault, showing that a key only
ever yields back the type it was minted for, plus adjust/delete/union.
The vector example runs through every combinator Linen.Data.Vector adds to
Array (generate, ifilter, foldl1'/foldr1, ifoldl'/ifoldr,
and/or/product, notElem, backpermute, slice) — everything else
Haskell's Data.Vector offers already exists verbatim on Array.
The todo example is a small in-memory TODO list whose every page is built
from Web.Html/Web.Css typed constructors — the <ul>/<li> nesting, each
item's <form>s, and its inline style all go through the same
illegal-construct-is-a-compile-error discipline as Tests.Linen.Web.HtmlTest/
CssTest (e.g. a <div> inside a <p>, or a color declaration given a
Display value, simply fails to compile). Routing and state reuse
Network.WebApp's Application/AppM, driven by the real
Network.WebApp.Server engine via withApplication, exactly as the server
example drives webapp's demoApplication. Unlike the other examples, todo
doesn't exit after checking itself — it self-checks against its own live
server and then keeps that same server (with its accumulated state) running
on the printed OS-assigned port, so you can immediately curl it by hand;
todo check runs the identical round trip but exits instead, for scripting.
lake exe examples postgrest (no args) and ... postgrest spec are fully
self-contained — they run against a hand-built, in-memory SchemaCache and
need no external services. postgrest live instead connects to a real
PostgreSQL instance, introspects its public schema with the same catalog
queries (SchemaCache.tablesSql/columnsSql) PostgREST itself runs at
startup, and serves a couple of requests against the real tables.
Start a disposable local Postgres with Docker:
docker run --rm -e POSTGRES_PASSWORD=postgres -p 5432:5432 postgresThen, in another terminal:
lake exe examples postgrest liveThis connects with host=localhost port=5432 user=postgres password=postgres dbname=postgres — matching the container above — prints every table found in
public, serves GET / and GET /<first table> through the same
App.handleRequest code path as the in-memory demo, and prints the live
schema's OpenAPI spec. Pass a different libpq connection string as the next
argument to point at another instance or database, e.g.:
lake exe examples postgrest live "host=localhost port=5432 user=postgres password=postgres dbname=mydb"If nothing is listening, the example prints a short "could not connect" hint
(with this same docker run command) and exits 1, rather than crashing.
- docs/MODULES.md — the full module feature list and module table.
- docs/imports/index.md — Hackage-package import order, with a
per-package module dependency list under
docs/imports/<Package>/dependencies.md. - AGENTS.md — conventions for contributing to the library.
See LICENSE for details.