Glistix is a fork of the Gleam compiler which adds a Nix backend, so that you can compile Gleam code into Nix and use it in your configurations!
This allows you to leverage Gleam's type-safety and simplicity to write reasonable and more correct code. You will also be able to use Gleam's tooling in your favor, such as unit tests and easy package management.
For more information on Gleam, including tutorials, please check the Gleam language's website and its language tour. Likewise, to learn more about the Nix language, check the NixOS website as well.
NOTE: Glistix is beta software, and may have breaking changes. You shouldn't rely on it on production just yet, but feel free to give it a shot on personal or smaller projects and report any issues and bugs you find. It should be functional, but we still need people to try it out and report any bugs.
NOTE: Glistix's latest stable version currently tracks Gleam v1.8.1, meaning features and fixes from up to that Gleam version are available.
NOTE: Glistix is an unofficial project and is therefore not affiliated with the Gleam project.
Overall status: Usable, but still missing easy bindings (to Nixpkgs, NixOS, Home Manager) and usage examples. Please contribute!
Codegen (Gleam -> Nix): Pretty much done (only a few bit array options aren't supported)
@external(nix, "file.nix", "function_name") to import a Nix function in Gleam(import ./glistix/project/dir).lib.loadGlistixPackage { module = ... } to import a Gleam module in Nixglistix/stdlib), still missing important functionsglistix_nixPlease consider supporting the team behind Gleam on GitHub. Without their awesome project, Glistix wouldn't exist!
If you also want to support the Glistix project directly, feel free to support me on GitHub as well!
If you like the project and know some Nix, consider contributing! At the time of writing, we have a few important issues open at glistix/stdlib which could use some help. Additionally, feel free to explore issues at the Glistix compiler's repository (make sure to read the "Contributing" section below).
You can talk to us at:
Glistix officially supports Linux, MacOS and Windows. (Note, however, that Nix doesn't support Windows yet, so you won't be able to test your projects, but glistix build should work at least.)
You can install Glistix in one of the following ways.
From GitHub Releases: If you're using Linux (any distro, including NixOS), MacOS or Windows, you can install Glistix by downloading the latest precompiled binary for your platform at https://github.com/glistix/glistix/releases.
With Nix flakes: Invoke the command below in the command line to download, compile and run a specific release of Glistix - here the latest at the time of writing (v0.8.0).
sh
nix run 'github:Glistix/glistix/v0.8.0' -- --help
To install permanently, you can either add github:Glistix/glistix/v0.8.0 as an input to your system/Home Manager configuration, or use nix profile:
sh
nix profile install 'github:Glistix/glistix/v0.8.0'
With Cargo: You can use Cargo to compile and install Glistix's latest release (v0.8.0 at the time of writing):
sh
cargo install --git https://github.com/glistix/glistix --tag v0.8.0 --locked
Glistix was initially conceived in an attempt to improve the development experience of working with more complex Nix code. Whereas Nix can be fairly straightforward to use at its core (the language itself is tiny in terms of syntax), it can be observed in reality that writing more involved Nix code can take considerable time and/or effort to get right due to the lack of type-safety in Nix, which leads to limitations in Nix LSPs and often hard to understand errors which could have been avoided with static type-checking - that is, checking the correctness of your Nix code before you even try to evaluate it.
Personally, I (author of Glistix) have faced this kind of problem while writing my own NixOS and Home Manager modules - oftentimes the configuration logic can get more involved and become particularly hard to debug and/or expand on without some form of static type-checking (and thus proper autocomplete and so on), which leads to a worse development experience.
While there have been several attempts to add some form of static type-checking to Nix, very few have had great amounts of success so far. It always seemed like Nix needed some quite large (and thus unfeasible) changes to make it possible.
That's where Gleam comes in. Gleam is a functional and statically-typed language with friendly syntax and great tooling (including the compiler and its helpful errors, great support for unit testing, good LSP support across editors, and so on). What's more, Gleam was designed for transpilation to other dynamically-typed languages (namely Erlang and JavaScript). So, it was the perfect match: we could leverage the Gleam compiler and its tooling to write more complicated logic with Gleam, transpile that to Nix, and attempt to keep complexity of actual Nix source code at a minimum - the Nix code would then focus on calling the transpiled Gleam code as much as possible.
Thus Glistix was born: it adds a Nix backend to the Gleam compiler, alongside auxiliary features and tooling to make Gleam easier to use within the Nix ecosystem. The main goal is precisely to integrate Gleam and Nix together as much as possible, so that you may write Gleam code and use it within Nix, and vice-versa (Gleam code can invoke Nix functions through FFI).
Importantly, this is very much inspired by PureNix's approach. That project, which allows compiling PureScript to Nix, was one of the options considered before settling on Glistix's design, and has similar goals regarding type-checking and tooling. The main difference in our approach is the usage of Gleam, whose simplicity in syntax, concepts and usage makes it stand out among other languages (in our opinion). Nevertheless, make sure to check out PureNix as well if you're interested in learning more about alternatives - it is an awesome project as well!
We hope you will enjoy using Glistix for your Nix and NixOS projects!
Glistix provides the following features on top of Gleam's compiler and tooling:
You can write Gleam code and have Glistix produce equivalent Nix code. Almost all of the base language's features are supported (bar a few BitArray specifics).
The generated Nix code aims to be as readable as possible, and attempts to follow common formatting standards within the Nix ecosystem. In particular, we have taken a few concepts from Nix RFC 0166 and plan to be more conformant with said RFC in the future.
This also allows using packages from the Gleam ecosystem within Nix. Note that pure Gleam packages are instantly usable, while packages which require Erlang/JS FFI need to be manually ported.
@external(nix, "./file.nix", "functionName") and @target(nix). The first one allows importing a function declared in Nix (see the next section for an example), while the second allows specializing a function's implementation for each target supported by Glistix (one implementation for @target(erlang), one for @target(javascript) and another for @target(nix)).Consider this example code in src/example.gleam, added after running glistix new example:
pub type Time {
Seconds(Int)
Minutes(amount: Int)
}
pub fn as_seconds(time: Time) -> Int {
case time {
Seconds(amount) -> amount
Minutes(amount) -> 60 * amount
}
}
pub fn main() {
let example1 = Seconds(5)
let example2 = Minutes(30)
let seconds: Int = as_seconds(example1)
let minutes = as_seconds(example2)
#(seconds, minutes)
}
Running glistix build, Glistix will compile this to:
let
Seconds = x0: { __gleamTag = "Seconds"; _0 = x0; };
Minutes = amount: { __gleamTag = "Minutes"; inherit amount; };
as_seconds =
time:
if time.__gleamTag == "Seconds" then let amount = time._0; in amount
else let amount = time.amount; in 60 * amount;
main =
{ }:
let
example1 = Seconds 5;
example2 = Minutes 30;
seconds = as_seconds example1;
minutes = as_seconds example2;
in
[ seconds minutes ];
in
{ inherit Seconds Minutes as_seconds main; }
We can use glistix run (in this case with -- --strict so the tuple is fully evaluated), which runs nix-instantiate, to verify that main { } evaluates to [ 5 1800 ].
Note that:
- Generated Nix code is formatted and readable;
- Gleam records can be easily created and manipulated from Nix code through the generated constructor functions and the resulting attribute sets (the field names are preserved, and have the form _INDEX when strictly positional);
- Gleam's built-in types are adequately mapped to Nix's built-in types where possible (there are notable non-obvious exceptions, such as List and BitArray - check the relevant book page for more info);
- By convention, functions without arguments take an empty set { } as argument (you'd call main { } for example), whereas functions with one or more arguments take them positionally (for example, f(a, b, c) in Gleam translates to f a b c in Nix).
Make sure to check out the chapter on the Nix target of the official Glistix book for more information!
You can add .nix files to your src and import them from Gleam. For example, consider the Gleam code below at src/example.gleam:
// We will need some help from Nix to check
// if a string contains a comma. (In practice,
// you'd use Gleam's stdlib for this.)
@external(nix, "./ffi.nix", "containsComma")
fn contains_comma(string: String) -> Bool
/// Converts True/False to "Yes"/"No"
fn yes_or_no(boolean: Bool) -> String {
case boolean {
True -> "Yes"
False -> "No"
}
}
pub fn main() {
let text = "abc,d"
// Yes, you can use piping!
contains_comma(text)
|> yes_or_no
}
Also consider that we add src/ffi.nix as follows:
let
containsComma = s: builtins.match ".*,.*" s != null;
in
{ inherit containsComma; }
With glistix run (which uses nix-instantiate to evaluate the main function), we get the expected output of "Yes" (text does indeed contain a comma).
$ claude mcp add glistix \
-- python -m otcore.mcp_server <graph>