Superfluid
A functional language with dependent types and customisable type representations
Install / Use
/learn @kontheocharis/SuperfluidREADME
superfluid
A functional language with dependent types and customisable type representations
Installing
Requirements: stack.
git clone https://github.com/superfluid-lang/superfluid.git
stack build
Then run stack run -- -h to see the available options.
A tour of Superfluid
Superfluid is a dependently typed language, similar to Idris, Agda, Coq and Lean. It is meant as a tool for experimenting with efficient compilation of dependent types.
A brief overview of its features:
- Dependent function types
(a : A) -> B. - Implicit function types
[a : A] -> B. - Instance function types, which are automatically resolved
[[Monad M]] -> A -> M A. - Access to the universe
Type, withType : Type(no universe levels). - Quantitative type theory with 1, 0, \omega.
- Whitespace, insensitive; grouping is done with braces
{}. - Typed holes with
?foo.
Basic syntax
Each file consists of a series of definitions.
Each definition is either:
-
a function definition
def or : Bool -> Bool -> Bool { \a b => case a { true => true, false => b, } }Function definitions support general recursion.
-
a data definition
data Bool : Type { true : Bool, false : Bool } -
or a primitive (postulate)
prim conjure : (0 A : Type) -> APrimitives need to have definitions accessible with the same name (but camel case) in the output JavaScript file.
Every file has access to the contents of bootstrap/prelude.sf.
Functions (anonymous lambdas) are written as
\x f => f x
This would be of type A -> (A -> B) -> B.
Both function arguments and function type arguments can be stacked like:
def id : (A : Type) (a : A) -> A {
\A a => a
}
Superfluid supports Rust-style let blocks using braces:
{
let x = 1;
let y = add x 1;
let z = mul x y;
(z, y)
}
This would be of type (Nat, Nat).
Pattern matching can be done using the case keyword:
case x {
true => true,
false => false,
}
Nested and dependent pattern matching is not supported yet, but can be emulated using explicit motives and equality transports.
An explicit motive can be provided using to:
def snd : [A : Type, B : A -> Type] -> (p : Sigma A B) -> B (fst p) {
\[_] [B] p => case p to (\p => B (fst p)) {
(a, b) => b
}
}
Inductive families
Superfluid supports inductive families, such as
data Bool : Type {
true : Bool,
false : Bool
}
data Sigma (A : Type, B : A -> Type) : Type {
pair : (a : A) -> B a -> Sigma A B
}
data Equal [A : Type] : A -> A -> Type {
refl : [x : A] -> Equal x x
}
Equality is not special; it is just an inductive family.
Quantities
Superfluid supports quantities (0, 1, * for omega), such as
data Subset (A : Type) (B : A -> Type) : Type {
element : (a : A) -> (0 _ : B a) -> Subset A B
}
Quantities can be added in all binders, including lets:
(0 a : A) -> B
let 1 a = b
[* a : A] -> B
[[0 _ : Monad M]] -> Q
A usage check is run after typechecking to ensure that variables with quantity
0 are not used in a runtime context, and with 1 are not used more than once
(technically the 1 quantity is affine, not linear).
All binders with quantity 0 are erased before codegen.
Custom representations
Each module item (other than primitives) can be given a custom representation.
- For function definitions, we merely need to provide a function of the same type (though technically we should also verify that the representation is functionally equivalent to the original).
- For data definitions, we need to "implement" the constructors and induction of the original data type.
For example, this is how the prelude represents natural numbers as JavaScript numbers:
repr data Nat as JsUint {
z as js-uint-zero,
s n as js-uint-plus js-uint-one (repr n),
case n to E {
z => b,
s => r,
} as {
js-zero-or-pos [\x => E (unrepr x)] (\_ => b) (\x => r (unrepr x)) (repr n)
}
}
repr def add as \a b => unrepr (js-uint-plus (repr a) (repr b))
repr def sub as \a b => unrepr (js-uint-minus (repr a) (repr b))
repr def mul as \a b => unrepr (js-uint-times (repr a) (repr b))
We also have some operators to reason about representations:
- if
x : A, thenrepr x : Repr A - if
x : Repr B, thenunrepr x : B
These reduce definitionally to the item's defined representation.
In the prelude, all list-like things with irrelevant indices are represented as
JavaScript arrays. See examples/paper/refinement.sf for an example of zero-cost
conversion between lists and vectors.
The system of custom representations is explored further in the paper: https://arxiv.org/abs/2505.21225
Code generation
Superfluid generates code for Node.js. Upon compilation of a file with the -g flag (generate code),
the generated code is written to a file with the same name as the input file, but with a .js extension.
This can then be run with Node.js. Often you might need a larger stack size.
For example, node --stack-size=200000 examples/paper/fizzbuzz.sf.js.
Examples
The bootstrap/prelude already contains quite a few examples of custom representations, including:
FinandNatas JS numbers, with representations for common numeric operations.Listas JS arraysByteListandByteVecas JS buffersStringas JS stringsBoolas JS booleans
Also see the examples/ directory for:
paper/amicable.sf: Finding amicable numbers. Again, runs fast due toNatrepresentation. However we can run the function at compile-time too, which doesn't have access to the fastNatrepresentation, and thus is really slow (but can do symbolic theorem proving!).paper/fizzbuzz.sf: Just a plain fizzbuzz implementation, but actually runs fast due toNatrepresentation.paper/refinement.sf: Zero-cost conversion between lists and vectors.paper/cons-snoc.sf: Zero-cost conversion between lists and snoc-lists.paper/de-brujin.sf: Free weakening of de-Brujin levels while still retaining the well-scoped indexing structure.paper/flat.sf: Message buffers with zero-copy serialization and deserialization into records (TODO).paper/lambda.sf: Typechecking the simply-typed lambda calculus into a well-typed representation without creating a new tree.paper/linear.sf: Linear lists with cons that doesn't copy data.
I have also written day 1 of Advent of Code 2024 Superfluid: https://github.com/kontheocharis/advent-of-code-2024.
More examples coming very soon.
Acknowledgments
- A lot of the work on Superfluid started by mirroring the code in Andras Kovacs' elaboration zoo.
- Thank you to Edwin Brady for the detailed discussions about: unification with postponing, quantitative type theory, instance resolution, and dependent pattern matching.
Missing features
- Induction-induction and induction-recursion for data types.
- Forward declarations.
- Positivity and termination checking.
- Mutual recursion for functions.
- Dependent pattern matching (coming soon).
- Coherence checking for representations.
- A well-scoped version of this compiler that is cleaner and less hacky. Work has begun: https://github.com/kontheocharis/superfluid-idr.
- Coinductive data types.
- A better compilation target: C/Rust/LLVM.
- More internal control over compilation: custom function/closure compilation, flat dependent pair types (as a library).
Related Skills
node-connect
343.1kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
90.0kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
343.1kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
343.1kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
