Introduction
Welcome to Glistix! Glistix is a fork of the compiler for the Gleam language 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.
If you'd like to give Glistix a quick spin before installing, make sure to check out the Glistix playground.
If you have a question or would like to contribute, please join our Zulip chat with this invite link. The chat's history can be accessed here.
If you came to this page looking for more information on patching dependencies for Nix compatibility, check out "Overriding incompatible packages".
This book aims to explain many core concepts related to Glistix, possibly helping you use it more effectively for your projects.
Please note that this book is a work in progress! There's much more to come. You can contribute to the book either on GitHub or Codeberg.
About Glistix
This chapter presents important general information regarding Glistix.
Check out the Getting Started chapter once you're ready to start using Glistix!
Goals & Roadmap
Glistix's Goals
Glistix has the following primary goals:
-
Provide and maintain a Nix compilation target for Gleam. That is, you should be able to write Gleam code and use it with Nix, aiming to improve the Nix development experience thanks to Gleam's compile-time guarantees, type-safety and great tooling, which should also improve the accessibility and lower the entry barrier of working with Nix (especially when it is needed to create complex and dynamic Nix configurations).
-
Integrate the Gleam ecosystem with the Nix ecosystem as much as possible. In particular, the existing packages for Gleam should be made usable within Nix where possible.
In addition, we have the following secondary goals:
-
Aid proper usage of the Gleam programming language among Nix users. The tools we are creating to use Glistix within Nix, such as builders for packages, should ideally be easily reusable for the upstream Gleam compiler as well, eventually including proper support for the Erlang and JavaScript targets, if possible.
-
Provide packages and bindings to aid in interacting with the Nix ecosystem from projects using Glistix. For example, we'd like to release bindings for the easy creation of NixOS modules, configuration of flakes etc. with pure Gleam, and maybe even integrate some of those into the compiler itself. Our first step so far has been the creation of the
glistix_nix
package. -
Contribute back to the upstream Gleam compiler where possible. We have certain needs and challenges which are shared with Gleam users which use the Erlang and JavaScript targets. Therefore, where possible, we'd like to contribute our solutions to upstream and use them, in order to ensure we maintain some amount of uniformity with the rest of the Gleam ecosystem. This doesn't mean we can't add features exclusive to Glistix, but, if they aren't Nix-related, they should be kept to a minimum.
Finally, we want to clarify what we are NOT trying to achieve:
-
Glistix is not meant to replace or compete with Gleam. Rather, we are focused on extending and integrating Gleam to the Nix ecosystem. This includes the addition of the Nix target, but also the maintenance of tools to improve the Gleam on Nix experience as much as possible, as outlined above.
-
Glistix is not meant to replace all usage of the Nix language. We do want to provide a better experience when working with Nix, but it is absolutely not our goal (nor is it practical) to have Nix users switch to using exclusively Glistix for their configurations and other applications of Nix. This is also because Nix's (and
nixpkgs
) APIs are large and change relatively often, and thus it is not possible to always provide fully accurate type bindings for all aspects of the Nix ecosystem.
Roadmap
Here is a non-exhaustive list of tasks we want to eventually tackle within the Glistix project. This can change at any time.
- Add more real-world examples and generally make the documentation more robust.
- Add some form of tail-call optimization. This depends on changes to Nix itself to be fully practical, but there might be improvements we can make in the meantime.
- Fully decide the semantics of Nix's lazy evaluation when using Glistix. In particular, we should take a final stance on how discarded expressions behave in this regard by default.
- Add bindings for NixOS interfaces. Being the initial use case that sparked the development of Glistix, this is a fairly important priority. However, more fundamental compiler work has been receiving more attention at this time.
-
Improve the package management and patching story. We should cooperate with the upstream Gleam compiler to avoid future incompatibilities. Currently, our methods of overriding the Gleam standard library through Git submodules are quite manual and need replacement with proper Git dependencies together with requirement overriding.
- This is now available! Glistix has implemented its own patching system, which can be used to replace Hex packages with others, as described in the book page about 'overriding incompatible packages'.. However, proper Git dependencies are left to a future update (already available on upstream).
-
Create a playground page in which you can compile Gleam to Nix online. Would be nice to be able to give Glistix a quick try in your web browser (maybe even in the documentation)!
- This is now available! Try Glistix online here: https://glistix.github.io/playground
- For more information, read the book page about the playground.
Limitations
Here is a non-exhaustive list of relevant issues and warnings to consider when using Glistix.
Table of Contents
Lack of Git dependencies
The Gleam ecosystem, whose packages are mostly available through Hex, contains many useful packages which can also be used with Glistix. However, oftentimes you will find packages which do not work on Nix, because they rely on FFI with the usual Gleam targets (Erlang and JavaScript). This includes, most importantly, Gleam's standard library. The way to deal with this is to use forks of those packages patched for Nix support.
Since Glistix v0.7.0, this can be easily done through patching using [glistix.preview.patch]
, as described at "Overriding incompatible packages".
However, that only works seamlessly for forks published on Hex. Git dependencies are supported on upstream since Gleam 1.9.0, but Glistix has not adopted that Gleam version yet, so you'll have to use them as local dependencies to Git submodules if Git dependencies are truly required. We hope to lift this restriction in a future Glistix version.
This is tracked by Glistix issue #47.
For more information and further instructions, please check out "Overriding incompatible packages".
Missing tail-call optimization
Compared to Gleam's Erlang and JavaScript targets, Glistix's Nix target does not have tail-call optimization. This means that recursion continually grows the stack and, as such, it is possible to trigger stack overflows on Nix through deep enough recursion. As a consequence, at least for now, you should avoid relying on recursion to process very large data.
There are some ways to try to work around this limitation:
-
Gleam's built-in
List
type works as a linked list across all targets (including Nix). It is therefore ideal for recursion, but should be avoided when recursion needs to be avoided (e.g. because the list might have thousands of elements). In those cases, consider using Nix's arrays (called "lists" in Nix's official docs) instead. Theglistix_nix
package contains various helpful functions to aid you in using arrays. The array functions avoid recursion where possible (functions using recursion indicate they are doing so clearly in the docs - they are few). -
It is possible to use Nix's
builtins.genericClosure
function to improve efficiency when traversing lots of data, as it allows creating arrays from existing (possibly recursive) structures, but is not recursive itself, thus allowing for some light "tail-call optimization" in some cases, particularly when you need to create arrays.- For more information, check out this great NixOS Discourse thread by user sternenseemann: https://discourse.nixos.org/t/tail-call-optimization-in-nix-today/17763
Lazy evaluation
Nix code is evaluated lazily, meaning an expression isn't evaluated until requested. This has implications on side-effects, such as when using logging for debugging during development; more importantly, however, values which aren't evaluated lead to the creation of thunks in memory; Creating too many thunks can lead to slowdown and/or high RAM usage. Therefore, and especially when working with complex and/or heavy programs or code within Nix, try to reduce recursion and memory usage as much as possible to avoid surprises.
Discarded expressions
Due to the above, it is natural that expressions not bound to any variables would never be evaluated (in principle), due to laziness. To tackle that, Glistix ensures all discarded expressions are evaluated (at least shallowly). For example:
pub fn main() {
// The panic below won't run due to laziness.
// The variable is never used.
let var = panic as "message"
// The panic below, however, WILL run.
// Glistix forces discarded expressions
// to be evaluated before the function's
// return value using Nix's `builtins.seq`
// functionality.
panic as "other message"
// The panic below will also run.
let _ = panic
// However, the panic below won't run,
// as the evaluation of discarded expressions
// is shallow.
// This might change in the future.
#(panic as "inside tuple")
// It is worth saying that any returned values
// are evaluated, of course.
Nil
}
To force deep evaluation of expressions, you can use the nix.deep_eval
function from the glistix_nix
package:
import glistix/nix
pub fn main() {
nix.deep_eval(#(panic as "this will run"))
Nil
}
Additionally, assertions are always evaluated:
pub fn main() {
// This will cause a panic, even though
// this expression doesn't bind any variables.
let assert False = True
Ok("finished")
}
Glistix does this as certain packages rely on this behavior, such as gleeunit
(the default test runner), which generally encourages performing multiple assertions in one function, for example - however, said assertions are usually done through function calls not bound to any variables (or let assert
expressions). In other words, if discarded expressions were ignored, the test suites of multiple packages would simply not do (almost) anything, as multiple assertions per test function are often used and their side effects (of panic
king upon failure) relied upon.
Contributing
You may contribute in many ways. The compiler's source code, in Rust, is publicly available at its GitHub and Codeberg mirrors. You may contribute to it by first browsing (or submitting) issues and then submitting pull requests at either of its mirrors.
However, you don't need to know Rust to contribute. If you know some Nix, please give us a hand with porting Gleam packages to Nix. For example, the stdlib port is currently incomplete.
Please discuss first before working on a major contribution. The ideal channels for this are:
- GitHub issues. Feel free to open an issue proposing a feature and we can discuss it there!
- Our Zulip chat. To join, you may use the invite link at the introduction page.
- Please open an issue in the book repository if the link doesn't work for you.
Additionally, make sure to read the rest of the book for useful information on how the compiler works. The "Glistix Architecture" chapter goes into great insight in this regard, and is a valuable resource.
Please note that we generally try to follow an upstream-first policy. This means that any feature ideas which are not related to Nix should generally be brought to the the upstream Gleam repository first; if accepted there, the feature should be implemented (/contributed) upstream. Otherwise, we can consider implementing it on Glistix if it would significantly improve the experience of using Glistix, especially regarding usage with Nix, but not before proper discussions. The idea is to not only keep up with the Gleam ecosystem at large, but also to ensure most improvements to the Glistix compiler also benefit users of the upstream Gleam compiler.
Thanks
-
Thanks to Louis Pilfold and the rest of the Gleam team for their amazing work on the Gleam compiler and the Gleam ecosystem, which was fundamental for Glistix to come to exist.
-
Thanks to the Purenix project for being one of our main inspirations. It has a lot of goals in common with the Glistix project, and generally demonstrated that the initial idea which led to Glistix could actually have been something viable to do.
-
More generally, thanks to all contributors to Nix and the aforementioned projects as well.
Getting Started
The pages in this chapter will guide you with getting started with Glistix.
Installation
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.)
In addition, Glistix supports running in the browser (using WebAssembly). You can try it out in the playground without installation: https://glistix.github.io/playground
You can install Glistix to your computer in one of the following ways.
-
From GitHub Releases: Regardless of your OS or distribution, 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.7.0).
nix run 'github:Glistix/glistix/v0.7.0' -- --help
To install permanently, you can either add
github:Glistix/glistix/v0.7.0
as an input to your system/Home Manager configuration, or usenix profile
:nix profile install 'github:Glistix/glistix/v0.7.0'
-
With Cargo: You can use Cargo to compile and install Glistix's latest release (v0.7.0 at the time of writing):
cargo install --git https://github.com/glistix/glistix --tag v0.7.0 --locked
Currently, Glistix cannot be installed through nixpkgs.
Basic Usage
Before you start, note that you can give Glistix a try in your browser without installing anything! You can try it out in the playground at https://glistix.github.io/playground. Read the book page about the playground for more information.
Once you're ready, make sure to install the Glistix compiler to your computer. Afterwards, here's how you can start working on a new Glistix project straight away:
-
Use the
glistix new NAME
command to create a new Glistix project.-
This command will set (almost) everything up for you, including initialize a Git repository, initialize the project structure (
gleam.toml
,src/
,test/
etc.), prepare essential*.nix
files, and even clone Glistix's standard library toexternal/stdlib
as a Git submodule (this is a workaround which is currently needed while we don't have Git dependencies!). -
We also generate a default GitHub Actions CI workflow file which tries to build your project through
flake.nix
. You can add--skip-github
toglistix new
to opt out of the creation of this file (or just delete it).
-
-
You can edit
src
to customize the Gleam code, as well as editgleam.toml
to your liking.-
You can use
glistix add name
to add a dependency from Hex. For instance, you may want to use theglistix_nix
package to easily access certain Nix built-in types from Gleam, which can be done withglistix add glistix_nix
.- If your desired dependency doesn't support Nix, you will have to use a fork patched for Nix support. Check "Overriding incompatible packages" for more information.
-
Note that Git dependencies are not yet supported by Glistix. If you need those, you'll have to clone them as submodules and use as local dependencies. See Limitations for more information.
-
-
Run
glistix build
at least once, not only to make sure everything is working, but also to generate themanifest.toml
(which should be checked into your repository). -
Afterwards, to complete the Nix side of your setup, ensure you have Nix with flakes support available (the
nix
command), as well as rungit add .
so all relevant files are checked in, and then run the command below to generate yourflake.lock
.nix flake update
Nice! Your project is now ready to be used by both Nix users (which will use your Gleam code compiled to Nix) and also other Glistix users.
To import a Gleam module in your project from within Nix, the default.nix
and flake.nix
files in your new project export a lib.loadGlistixPackage { module = "module/name"; }
function, which, when used, will give you an attribute set with all names exported by that module, so you can use its record constructors, constants and functions from within Nix. See "Import a Gleam package in Nix" for more information.
Using the Compiler
This chapter describes some common ways to use the Glistix compiler.
Online playground
Glistix has an online playground available at https://glistix.github.io/playground. Give it a try!
Without installing anything, the playground can be used to:
- Quickly convert some Gleam code to Nix code using Glistix. Just type the Gleam code on the left and the Nix code appears on the "Compiled Nix" tab to the right.
- Quickly share some Gleam code compiled to Nix to the web. After typing the Gleam code to the left, just press the "Share code" button to the right, and you will get a permanent link to the playground with your code.
Please note that it currently has the following limitations:
- The only package available is the Glistix port of Gleam's
stdlib
. As such, you cannot import other packages online just yet. - The "Output" tab uses the JavaScript target, as it's the only target that runs in the browser. As such, when you're using Nix-exclusive features, that tab will display some errors you can ignore. What matters is the "Compiled Nix" tab.
Found an issue with the playground? Feel free to open an issue at the playground's repository.
Command-Line Interface
Here are some of the most common compiler commands.
-
glistix new
: An essential command, creates a new Glistix project for you with batteries included, containing:- Several basic directories and files of the project structure expected by the compiler (see "Project structure");
- This includes an initial
gleam.toml
tuned for Glistix-specific defaults.
- This includes an initial
- An initial
flake.nix
(see "Import a Gleam package in Nix"); - An initial Git repository with a
.gitignore
file.
- Several basic directories and files of the project structure expected by the compiler (see "Project structure");
-
glistix build [--target nix]
: Builds your project tobuild/dev/<target>
, by default the target specified in yourgleam.toml
unless you specify--target <target>
.- Note that, if no target is specified in your
gleam.toml
, the compiler will default to theerlang
target to be compatible with existing Gleam projects. As such, make sure to specifytarget = "nix"
in yourgleam.toml
.
- Note that, if no target is specified in your
-
glistix run [--target nix]
: Runs your project's main function in the target specified either by--target
or bygleam.toml
(orerlang
by default, for the same reason as before).- For the Nix target, this will call
nix-instantiate
to evaluate yourpackagename.gleam
'smain
function.
- For the Nix target, this will call
-
glistix test [--target nix]
: Similar toglistix run
, but runs the main function compiled fromtest/packagename_test.gleam
. -
glistix format
: Formats your Gleam code according to Gleam's standards. -
glistix clean
: Deletes yourbuild/
directory. Useful to get rid of stale package versions. -
glistix add name
: Adds a Hex dependency to your project. -
glistix docs build
: Builds documentation for your package tobuild/docs
. -
glistix publish
: Publishes your package to Hex.
Project Structure
When you run glistix new
, the compiler will generate a project which should conform to the following structure (largely based on Gleam's structure, which by itself is based on Erlang's project structure):
gleam.toml
contains all essential information regarding your project that the compiler should be aware of, including metadata (such as package name), your preferred target (defaults tonix
, can also bejavascript
orerlang
for compatibility with other Gleam projects), and also specifying your dependencies (see "Project Configuration").src/
contains the source code of your package. This can contain both.gleam
files and also FFI files (.nix
for the Nix target). Apackagename.gleam
file with amain
public function with zero arguments is expected if your package is not a Gleam library (but rather made to be used within Nix). If present, you can check its output withgleam run
.test/
optionally contains apackagename_test.gleam
file containing a singlemain
function with zero arguments which is called when runninggleam test
. Use this withglistix_gleeunit
or some other test runner.priv/
is an optional folder for assets and other general files needed by your project and is not present by default. It is, however, symlinked tobuild/dev/<target>/<package>
upon build.external/
is an optional folder for external dependencies cloned locally as Git submodules (see "Overriding incompatible packages").
Additionally, some projects may opt into creating an output/
folder to cache build output for ease of use from Nix (see "Import a Gleam package in Nix").
Project Configuration
You can configure your project through a gleam.toml
file. Its settings are mostly the same as Gleam's usual settings, available at https://gleam.run/writing-gleam/gleam-toml/. However, Glistix additionally defines a few extra settings:
-
There is a new
[glistix.preview]
section for temporary settings while Glistix is in beta. It is expected that those settings will change in the future. -
It contains a new
[glistix.preview.patch]
section, which is similar to[dependencies]
, however it is used to override transitive dependencies with other packages. This can be used to replace dependencies with Nix-compatible forks. Note that this setting is only read for top-level projects, and ignored for libraries; end users must patch all necessary transitive dependencies by themselves. (More information at "Overriding incompatible packages".)For example:
[dependencies] # When published to Hex, depend on the normal stdlib instead. # Downstream users will have to patch manually. gleam_stdlib = ">= 0.34.0 and < 2.0.0" [glistix.preview.patch] # When developing locally, replace 'gleam_stdlib' with 'glistix_stdlib' from Hex # on all transitive dependencies. gleam_stdlib = { name = "glistix_stdlib", version = ">= 0.34.0 and < 2.0.0" } # Can also replace with local packages. gleam_json = { name = "glistix_json", path = "./external/json" }
Default configuration
This is the default configuration for new Glistix projects created with glistix new project_name
, as of Glistix v0.7.0:
name = "project_name"
version = "1.0.0"
target = "nix"
# Fill out these fields if you intend to generate HTML documentation or publish
# your project to the Hex package manager.
#
# description = ""
# licences = ["Apache-2.0"]
# repository = { type = "github", user = "", repo = "" }
# links = [{ title = "Website", href = "" }]
#
# For a full reference of all the available options, you can have a look at
# https://gleam.run/writing-gleam/gleam-toml/.
[dependencies]
# Use Glistix-maintained fork of the Gleam standard library with support for the
# Nix target.
#
# Consider depending on gleam_stdlib instead if you're publishing a package to
# non-Glistix users on Hex (Glistix users can still patch gleam_stdlib on their
# projects). Otherwise, you can depend on the fork directly.
glistix_stdlib = ">= 0.34.0 and < 2.0.0"
[dev-dependencies]
glistix_gleeunit = ">= 1.0.0 and < 2.0.0"
# The [glistix.preview] namespace contains useful settings which will be needed
# during Glistix beta. In the future, we hope these won't be necessary anymore.
# None of the settings below are recognized by the official Gleam compiler.
#
# For more information on those options, check out the Glistix handbook at
# this link: https://glistix.github.io/book/
# The section below allows replacing transitive dependencies with other packages,
# such as forks providing support for the Nix target. For example, `gleam_stdlib`
# does not support the Nix target, so we replace it with the `glistix_stdlib` fork.
# Replacing Hex packages with local packages is also supported (and Git packages
# in a future Glistix version).
#
# Specifying a version (or local path) is always required.
#
# NOTE: This section is ignored when publishing to Hex. It is only read on top-level
# Glistix projects. However, it can still be useful on packages to allow running unit
# tests, so you can keep this here regardless. Just keep this in mind if a user
# complains about a missing dependency: they are responsible for patching.
[glistix.preview.patch]
# Replaces 'gleam_stdlib' with 'glistix_stdlib' on all transitive dependencies.
# This is needed so stdlib will work on the Nix target.
gleam_stdlib = { name = "glistix_stdlib", version = ">= 0.34.0 and < 2.0.0" }
# otherpkg = { version = "3.4.5" } # replace with another Hex version
# anotherpkg = { path = "./external/submodule1" } # replace with local package
# renamedpkg = { name = "differentpkg", path = "./external/submodule2" }
Recipes
This chapter will provide some insights on tasks which often come up when using Glistix.
Import a Gleam package in Nix
Great, you've made an awesome Glistix project which you now want to use in Nix, maybe for some Nix derivation you're configuring in some other repository, or for your NixOS configuration, for example. Your main
function under src/packagename.gleam
is ready and returns some functions and values in an attribute set which you plan to use in those projects (again an example).
Now, how will your other Nix projects access your Gleam main
function? (Or any other function!)
Luckily for us, glistix new
automatically creates a flake.nix
file for our new project, as well as default.nix
. This makes it easy to import your Glistix project as part of another Nix project: everything you need is exported by these two Nix files (the former with builtins.getFlake
or as a flake input of another flake, the latter with import
).
This is because both files (with default.nix
just mirroring flake.nix
) export lib.loadGlistixPackage
. This is a Nix function with the sole purpose of importing your Gleam code transpiled to Nix. You simply call it with lib.loadGlistixPackage { }
and, by default, it will import your package's main module (with the same name as your package, which usually has the main
function). You can pick the imported module with lib.loadGlistixPackage { module = "my/module"; }
, for example.
It does that by invoking the Glistix compiler to build your project from scratch, and then importing the resulting build folder (moved to somewhere at /nix/store
). However, since that process depends on the Glistix compiler, that might trigger a full compilation of Glistix itself, which is slow. Therefore, you might want to cache the built Nix files in your repository to speed up the process - more at the next section.
When in Nix's pure evaluation mode (e.g. when using Flakes without --impure
), you will have to specify the current system in package.lib.loadGlistixPackage { system = "system name"; }
(e.g. "x86_64-linux"
), as the package would have to be built using a Glistix derivation (which depends on the system), unless that package caches build output as explained in the section below (in which case the system isn't necessary at all, as the Nix files are ready to be imported, so no build occurs).
As an example, let's assume your Glistix project is stored in a subfolder of your main Nix project. You can then import it like this:
let
yourProject = import ./project/folder;
yourPackage = yourProject.lib.loadGlistixPackage { };
mainResult = yourPackage.main { }; # call main()
in { inherit mainResult; } # use for whatever you want!
With Flakes, you'd add your project as an input. For example:
{
inputs = {
yourProject.url = "github:your/repo";
# ...
};
outputs = inputs@{ yourProject, somethingElse, ... }:
let
# You can import functions from other modules, not just main.
# 'system' is needed here so the correct Glistix derivation
# is used to build your package.
yourModule = system: yourProject.lib.loadGlistixPackage {
inherit system; module = "mod/name";
};
funcResult = system: (yourModule system).add 50 12;
in
{
# Use it as you wish!
# For example, within your NixOS configuration,
# or as a parameter to a builder, or anything else, really.
outputName.x86_64-linux.default = somethingElse (funcResult "x86_64-linux");
outputName.x86_64-darwin.default = somethingElse (funcResult "x86_64-darwin");
};
}
And that's it! You can now use your Gleam project within other Nix projects.
When invoking a Gleam function from Nix, make sure to use the correct representation of Gleam's types, as well as follow the conventions for calling Gleam functions. Read the "Nix Target" chapter for more information.
Caching your built Nix files
It is worth noting, however, that, by default, your project is built from scratch each time it is loaded, which requires Glistix. Since machines might not have Glistix installed, the first load might require building Glistix from source (as Glistix is not currently available on nixpkgs
). That's also why the system
parameter is needed when evaluating in pure mode (the default for flakes), as builtins.currentSystem
(the default) fails.
To avoid that problem and have your Glistix project not depend on Glistix for Nix consumers, you can cache the built Nix files. To do so, trigger a Glistix build locally - for example, glistix build -t nix
- and then copy the build results to an output/
directory, like so:
# Create destination
mkdir -p output/dev
# Copy just the resulting Nix files
# (IMPORTANT: -L flag used to deep copy symlinked folders in 'build')
cp -rL build/dev/nix -t output/dev
# Check it in so the flake can access and import from the folder
git add output
The -L
flag is needed as the Glistix compiler (and the upstream Gleam compiler, for that matter) generates symlinks at the build directory to each package's priv/
folder (if it has one). This option will ensure the symlinks are properly resolved into actual files and folders (they are duplicated from the build directory).
Once that's in place, future calls to lib.loadGlistixPackage { }
will automatically pick up your output/
folder, skipping the build process entirely for downstream Nix users, as long as it's properly set up (output/dev/nix
is present and added to Git). Otherwise (if not properly set up), it will fallback to building from scratch. (You can force it to not do that by passing { derivation = null; }
- the flake.nix
has a user-friendly option at the top to enable that. Then it will just error instead.)
The major downside, of course, is having to keep the output/
folder up-to-date whenever you make relevant changes to your Gleam code. The tradeoffs should be considered.
Using loadGlistixPackage
The default lib.loadGlistixPackage
function exported by packages, generated through glistix new
, is basically a wrapper over the same function exported by the compiler itself. It takes the following additional named arguments (in { ... }
):
system
: Used to select the derivation of the Glistix compiler with which to build the package from scratch (if build output isn't cached). Defaults tobuiltins.currentSystem
, if available.glistix
: Used to override the Glistix compiler derivation entirely, if desired.
The following arguments are inherited from the compiler's function:
package
: Name of the package to take from the build output. Defaults to the top-level project's package (read fromgleam.toml
), if available, otherwise fails.module
: Gleam module to read from the build output, in the forma/b/c
(no extension). Defaults to the value ofpackage
, where themain
function is usually located.output
: The cached build output path. If it exists, it will be used over compiling the package from scratch. This defaults to(src)/output
.derivation
: The derivation which builds the package from scratch. This is provided by the package itself usually, so this is only here to override it if you want to.nixRoot
: Defaults todev/nix
, indicates the root of the built Nix files.
Overriding incompatible packages
Many existing Gleam packages available over Hex will not work on Nix by default, as they rely on FFI with Gleam's typical targets (Erlang and JavaScript). Most importantly, this includes Gleam's standard library (gleam_stdlib
).
The workaround is to create and/or use Nix-compatible forks of those packages. For instance, Glistix maintains its own fork of Gleam's standard library, called glistix_stdlib
.
If those forks are published on Hex (which is the case of Glistix's forks, for example), then using them is easy - you just need to add the following line(s) under [glistix.preview.patch]
to your configuration, and then all of your dependencies and transitive dependencies will be using the fork instead of the original package, providing Nix target support:
# Example: patch gleam_stdlib with glistix_stdlib
[glistix.preview.patch]
gleam_stdlib = { name = "glistix_stdlib", version = ">= 0.34.0, < 2.0.0" }
Regarding gleam_stdlib
specifically, don't worry - the glistix new
command automatically suggests patching gleam_stdlib
(the lines shown above are already written to the config by default). However, you will still have to add more patches by yourself for any other package patches you - or your dependencies - might need (e.g. glistix_json
as a replacement for gleam_json
).
You will need to add more such lines under [glistix.preview.patch]
in the configuration of your top-level Glistix project until all Nix-incompatible packages have been replaced with compatible forks (you may have to create your own if no existing forks are available).
The compiler tries to add hints to compilation errors when it guesses that a dependency probably needs a patch, but in general, a dependency which fails to compile is a strong sign of incompatibility with Nix.
Note that a dependency failing to compile could also indicate that you applied a patch which a dependency is incompatible with! For example, if a dependency needs gleam_stdlib
v0.37 and you applied a patch for glistix_stdlib
v0.38, you might receive an error when compiling that dependency.
Please note that patches are ignored when publishing a package to Hex - they are only applied on top-level projects (those which you can glistix run
, so, not libraries). End users are responsible for patching transitive dependencies by themselves. Therefore, it could be interesting to warn about patches in your package docs/README in case there are confused users of your library (assuming you are creating a library). However, patches are still useful for packages in order to allow the package authors to execute tests with glistix test
(test modules count as part of a top-level project).
However, if the desired fork is not published on Hex, the above process is not enough: you will need to use patches to local dependencies corresponding to Git submodules cloned to an external/
folder, as a workaround while Glistix does not yet support Git dependencies (they were implemented upstream on Gleam 1.9.0, which hasn't made its way to Glistix's codebase yet). This is tracked by Glistix issue #47.
In the meantime, the full instructions remain below.
Steps to override a package using Git submodules
Ideally, overriding a package with a Nix-compatible fork is as simple as patching the package to a fork published on Hex. If the fork you want to use is on Hex, then please use the instructions above instead and skip this section entirely.
However, if the fork is only available on a Git repository (such as GitHub), a separate workaround is needed, through Git submodules. This is only while Git dependencies are not yet available on Glistix.
As an example, the official gleam_json
package does not support the Nix target by default, while several Gleam packages depend on it, creating a problem if we want to use those packages. Luckily, the Glistix project maintains a Nix-compatible fork of this package, glistix_json
, at https://github.com/glistix/json.
Since glistix_json
is available on Hex, we can use the procedure described in the previous section to apply a patch from gleam_json
to glistix_json
, ensuring we can use packages which depend on gleam_json
(and also so we can depend on it ourselves).
However, for demonstration purposes only, here's how we could use it as a Git submodule (you'd apply the same instructions for forks not available on Hex):
-
Run the command below to add the repository as a Git submodule of your project. We add submodules to the
external/
folder as a convention:git submodule add --name json -- https://github.com/glistix/json external/json
-
Add
glistix_json
to yourgleam.toml
as a patch ofgleam_json
pointing to theglistix_json
submodule you cloned locally:[glistix.preview.patch] gleam_json = { name = "glistix_json", path = "./external/json" }
Please note that this patch, much like a Hex patch, is ignored if you publish your package to Hex. This section is read for top-level packages used by
glistix run
,glistix build
andglistix test
, but not for any dependencies or libraries, as previously mentioned. -
Optional: If you also want to use
gleam_json
in your own project, feel free to add it as a regular dependency of your project atgleam.toml
:[dependencies] gleam_json = ">= 2.0.0, < 3.0.0"
-
Finally, make sure to update your
flake.nix
file so that it will correctly clone the submodule when building your package through Nix. You can do so by adding the fork's repository as a Flake input, and then passing its downloaded source to thesubmodules
list (which is later used as an argument tobuildGlistixPackage
), as below:# At your flake.nix { inputs = { # ... json = { # <-- add this input url = "github:glistix/json"; flake = false; # <-- get the source, not the flake }; }; outputs = inputs@{ # ... json, # <-- add this argument # ... }: let # ... initial definitions here ... submodules = [ { src = json; # <-- add this here dest = "external/json"; } ]; # ... in { # ... }; }
-
Run
nix flake lock
to update yourflake.lock
to include the new submodule input.
Finally, make sure everything is working by running:
-
glistix build
, to update themanifest.toml
and ensure your package builds; -
nix build
, to ensure your package can still be built from the flake.
The Nix Target
This chapter will provide details on how Glistix translates your Gleam code to Nix, which is the Glistix compiler's main feature, and how you can use this information within your projects.
Types
Built-in types
The table below shows how Glistix translates most common Gleam types into Nix types.
Gleam Type | Nix Type | Support | Notes |
---|---|---|---|
Bool | Bool (true , false ) |
Full | |
Int | Int | Full* |
|
Float | Float | Full |
|
String | String | Full |
|
Functions | Lambdas | Full* |
|
Tuples | Arrays (Nix Lists) | Full | |
Lists | Nested attribute sets When the tag is Empty , has no fields; when the tag is NotEmpty , has head (contained element) and tail (next item) |
Full |
|
Records | Tagged attribute sets (see below) | Full | |
Bit Arrays | Attribute set with buffer field containing array of bytes (0-255 integers) |
Partial |
|
Records
User-created types are translated to Nix as follows:
-
Types without constructors only exist in the Gleam type system. Therefore, it is not possible to create a type without a constructor unless you do it through FFI. This is useful to create Gleam representations of Nix types. For example, you can define an
Array
type which you can't construct through Gleam, but can through FFI:// Only constructible via FFI pub type Array(a) /// Create a new Array @external(nix, "./ffi.nix", "createArray") pub fn new() -> Array(a)
Then, on the Nix side (
./ffi.nix
):let createArray = { }: [ ]; in { inherit createArray; }
Otherwise, the
Array
type is not known to Nix at all (even if it understands the underlying representation of instances ofArray
). -
Records with constructors are always represented by attribute sets. Those attribute sets contain, at least, their constructors' tags, as well as any fields. For example:
pub type Example { Constructor1 Constructor2(Int, Float) Constructor3(field: Int, inherit: Int) // Nix keyword? No problem Constructor4(Int, mixed: Int, Float, Int) }
The module above compiles to
let # ! A record without fields is not a function ! Constructor1 = { __gleamTag = "Constructor1"; }; Constructor2 = x0: x1: { __gleamTag = "Constructor2"; _0 = x0; _1 = x1; }; Constructor3 = field: inherit': { __gleamTag = "Constructor3"; inherit field; "inherit" = inherit'; }; Constructor4 = x0: mixed: x2: x3: { __gleamTag = "Constructor4"; inherit mixed; _0 = x0; _2 = x2; _3 = x3; }; in { inherit Constructor1 Constructor2 Constructor3 Constructor4; }
Note that positional fields become
_N
fields, whereN
is the field's position. Named fields keep their names, even if they are Nix keywords.You can construct these records in Nix by just calling their constructors. In this case,
Constructor1
can be used directly;Constructor2(a, b)
in Gleam would correspond toConstructor2 a b
in Nix; and so on.
Modules
In Gleam, each module corresponds to a single .gleam
file, and has its own exported names (pub
), which includes functions, constants and type constructors.
When translated to Nix, each Gleam module becomes a single Nix file.
For example, if your package is named hello
, the file at src/my/module.gleam
will generate a file build/dev/nix/hello/my/module.nix
on build.
Each module file evaluates to an attribute set containing all exported names.
For example, the module below:
pub type Type {
Constructor(a: Int, b: Int)
}
pub const constant: Int = 5
pub fn name() {
"Hello World!"
}
transpiles to
let
Constructor = a: b: { __gleamTag = "Constructor"; inherit a b; };
name = { }: "Hello World!";
constant = 5;
in
{ inherit Constructor name constant; }
Prelude
The prelude is always at build/dev/nix/prelude.nix
by default, and contains functions which are automatically imported by the compiler as needed.
Functions
We follow the conventions below for functions. Please keep those in mind when calling Gleam functions from Nix.
-
Gleam functions with zero arguments are called with empty attribute sets. For example, a function such as below would be called with
main { }
, which would give you the Nix integer5
(as per "Types").pub fn main() { 5 }
-
Gleam functions with one or more arguments take them positionally. For example, the function below would be called as
add 1 2
and would return3
.pub fn add(a: Int, b: Int) -> Int { a + b }
Function bodies
Function bodies, just like blocks, are translated into let...in
expressions. For example, the module below:
pub fn myfunc() -> Int {
let x = 5
let y = 10
let z = x * y
let w = x - z
x + y * w
}
is translated to
let
myfunc = { }: let x = 5; y = 10; z = x * y; w = x - z; in x + (y * w);
in
{ inherit myfunc; }
Standard Library
The Gleam standard library can be used within Glistix through Glistix's glistix_stdlib
port (developed at https://github.com/Glistix/stdlib).
It must be used as a patch over gleam_stdlib
through the [glistix.preview.patch]
configuration section. See "Overriding incompatible packages" for instructions.
In the specific case of the standard library, however, this is already done automatically by glistix new
.
Nix bindings library
The Glistix project maintains a library for fundamental bindings to Nix's built-in types and functions. It is rather small at the moment, but might expand further in the future. It contains bindings for Array
(built-in Nix lists), AttrSet
and a few other types, as well as some built-in functions.
It is available on Hex, so, to use it, just run the command below:
glistix add glistix_nix
Check its documentation at HexDocs.
Glistix Architecture
The compiler architecture inherited from the Gleam compiler is largely unchanged, more information of which can be found in the Gleam repository.
Therefore, this chapter intends to discuss a bit about the changes made in the Glistix code base on top of the base Gleam compiler. Those changes should be the main focus of contribution to the Glistix project.
General modifications
Glistix has applied several patches and additions to the base Gleam compiler. We intend to document most of them here.
-
The most important change is the addition of a Nix compilation target. This is reflected as a variant in the
Target
enumeration incompiler-core/src/build.rs
. This lead to changes across many files in the compiler, mostly related to replicating code otherwise meant for other targets for Nix as well.- We created a Nix codegen backend (which compiles Gleam code to Nix) for the Nix target at
compiler-core/src/nix.rs
and its submodules. There is some information about the Nix backend in the relevant section.- This required creating a
Nix
structure incompiler-core/src/codegen.rs
, whose methods manage the Nix codegen process, compiling every module in a project. - This also required the creation of a
prelude.nix
file undercompiler-core/templates
to be consumed by the Nix backend.
- This required creating a
- We updated the parser (at
compiler-core/src/parser.rs
) to add support for the@external(nix, ..., ...)
attribute.- This introduced new
external_nix
-like fields in multiple structures across the compiler. - This required changes to
analyse.rs
in order to validate the paths and names of external Nix functions. - Similarly,
format.rs
was also changed so that@external(nix, ..., ...)
is properly kept after formatting.
- This introduced new
- This required a few changes in the compiler's Cap'n Proto schema (
compiler-core/schema.capnp
and the generated files atcompiler-core/src/generated
) in order to store information for the Nix target in cache (in particular, external Nix functions). - Many compiler tests and test snapshots had to be updated as a consequence of that and other changes.
- We created a Nix codegen backend (which compiles Gleam code to Nix) for the Nix target at
-
We have customized several bits of the compiler so that they display information relevant to Glistix instead of Gleam compiler. Those changes are mostly minor, and include changing error messages to point to the Glistix repository, for example.
-
In addition to the point above, the compiler's crates were renamed to
glistix-*
instead ofgleam-*
and their versions were changed to Glistix's own version scheme (with the initial version beingv0.1.0
) to better reflect Glistix as a separate project.-
As a consequence, the file
compiler-core/src/version.rs
was changed to reflect not the Glistix version (defined inCargo.toml
files), but the Gleam compiler version we are basing ourselves on. This means that this file must be updated each time Glistix is updated to a new Gleam version. That version is checked by packages to ensure they are being used with a compatible Gleam compiler, so using Glistix's own version would cause wrong results and/or incompatibilities when checking compiler version restrictions. -
This also required renaming all test snapshots as they are prefixed with the crate name they apply to.
-
-
We have customized several CLI commands to add Nix-specific functionality. This includes at least
glistix new
,glistix build
,glistix run
andglistix test
. Please check the dedicated chapter for CLI more information. -
We have added a
[glistix]
section togleam.toml
for Glistix-specific configuration. This was done atcompiler-core/src/config.rs
. Right now, it includes the[glistix.preview]
section, which contains:-
[glistix.preview.patch]
: allows overriding transitive dependencies with Nix-compatible forks. For example, specifygleam_stdlib = { name = "glistix_stdlib", version = ">= 0.34.0 and < 2.0.0" }
to replace the incompatible packagegleam_stdlib
with the Nix-compatibleglistix_stdlib
published on Hex.- Note that this section is ignored when publishing to Hex, and only dependencies on
[dependencies]
will be read. End users must patch any transitive dependencies by themselves. - More details in the configuration book page.
- This was implemented in #44.
- Relevant implementation is spread across
compiler-core/src/config.rs
(contains the main patching logic, as well as detection of patched packages in the manifest to be unlocked after patch changes),compiler-core/src/dependency.rs
(patches transitive dependencies from Hex),compiler-core/src/manifest.rs
(manifest now stores patches used when building it),compiler-core/build/project_compiler.rs
(patches module names at build time),compiler-cli/src/config.rs
(invokes patching of the root config and dependency configs onread
),compiler-cli/src/dependencies.rs
(reads the root config, patches local dependencies and invokes hex dependency patching, as well as detects when patches have changed and the manifest has to be updated, and stores patches in the manifest),compiler-core/src/language_server/router.rs
(patches config read by the language server) andcompiler-cli/src/add.rs
(now has to resolve package versions twice when the added package was already patched: once without the patch to pick a version for[dependencies]
, and once again with the patch to fix themanifest.toml
).
- Note that this section is ignored when publishing to Hex, and only dependencies on
-
Two other settings,
local-override
and[glistix.preview.hex-patch]
, were previously used to select transitive local dependencies to be overridden by the root package's local dependencies in case of conflict and to override dependencies listed when publishing to Hex, respectively. They are now deprecated in favor of[glistix.preview.patch]
, which can be used for both purposes (you can patch transitive local dependencies to ensure they don't conflict with the root dependencies, and[dependencies]
are the dependencies effectively published to Hex and can be patched separately).
-
-
We have added some more useful error hints, as well as Glistix-exclusive errors, at
compiler-core/src/error.rs
. -
We have added Nix syntax highlighting support to packages' generated documentation (through
glistix docs
). The relevanthighlight.js
-compatible file is atcompiler-core/templates/docs-js/highlightjs-nix.min.js
. -
It is worth noting that we have reutilized most of the Gleam compiler's GitHub Actions workflows for our own usage in CI and other tasks. However, noteworthy changes were made to better adapt them to Glistix's needs.
-
We have modified language tests (in
test/language
) to support the Nix target. These tests run on CI.
Nix backend
The Nix codegen backend is very much based on the JavaScript backend, and is implemented as follows in the compiler:
- The entrypoint is at
compiler-core/src/nix.rs
. This Rust module implements the compilation of a Gleam module (file) to a Nix file. Themodule
function is the entrypoint and invokes thecompile
method ofnix::Generator
, which is ultimately responsible for generating the module's corresponding Nix code.nix::module
is invoked atcompiler-core/src/codegen.rs
whenever the project is being compiled with the Nix target.nix::Generator
contains methods such as for generating a function definition, a record definition and module constants, as well as handling imports and exports, delegating tonix::import
where necessary (see below).
- The file
compiler-core/src/nix/expression.rs
is very important, as it implements the compilation of each kind of Gleam expression to Nix code. It contains aGenerator
struct which is initialized once for each function in a module, and it essentially traverses the Typed AST for each expression, converting each inner expression to Nix, recursively. - The file
compiler-core/src/nix/import.rs
handles imports, generating a series ofimport
lines which appear at the top of the generated Nix file, as well as listing names to be exported at the bottom of the file (which are picked up bynix::Generator::compile
). - The file
compiler-core/src/nix/pattern.rs
is responsible for traversing patterns (used incase
clauses andlet
/let assert
statements) and converting them into assignments and conditionals (in an abstract manner), which are then consumed by generators ofcase
andlet/let assert
atnix/expression.rs
and translated toif...then...else if
statements where appropriate. - The file
compiler-core/src/nix/syntax.rs
contains multiple helpers to generate specific kinds of Nix expressions. For example, to generate alet...in
expression, to generate an attribute set from a list of name/value pairs, and so on. These helpers are extensively used by other submodules ofnix
. - Tests are located in
compiler-core/src/nix/tests
.
This page is under construction.
CLI Modifications
We have changed the following parts of the CLI (crate compiler-cli
):
new.rs
(glistix new
):- Added Nix-relevant file templates (
default.nix
,shell.nix
andflake.nix
); - Changed default
gleam.toml
to include Glistix-specific options.- It now patches
gleam_stdlib
toglistix_stdlib
by default on[glistix.preview.patch]
. - See more information at the configuration book page.
- It now patches
- Added Nix-relevant file templates (
run.rs
(glistix run
,glistix test
):- Use
nix-instantiate
when callingglistix run
orglistix test
on the Nix target.
- Use
publish.rs
(glistix publish
):- Uses
read_unpatched
to read configurations, thus using[dependencies]
as the published Hex dependencies, ignoring patches from[glistix.preview.patch]
.
- Uses
dependency.rs
(resolving versions for themanifest.toml
):- Implement features related to
[glistix.preview.patch]
, including detecting if patches changed before updating the manifest, ensuring local and Git dependencies specified through patches are provided and available, passing patch information to Hex dependency resolving, and storing patches in the manifest.
- Implement features related to
add.rs
:- Now has an additional dependency resolving step when the added package was affected by a patch (first, a version is resolved ignoring patches, generating the wrong manifest, and then we fix the manifest, keeping the initially resolved version in
[dependencies]
).
- Now has an additional dependency resolving step when the added package was affected by a patch (first, a version is resolved ignoring patches, generating the wrong manifest, and then we fix the manifest, keeping the initially resolved version in
config.rs
: Invokes patching when reading configuration files as necessary.
glistix build
(build.rs
) wasn't directly modified, but it now supports --target nix
as well.
Changelog
Contains changes made for each Glistix release.
Glistix v0.1.0 (2024-04-27)
-
Base Gleam version: v1.1.0
-
Initial (beta) release of ✨ Glistix ❄️, a fork of the Gleam compiler which allows compiling Gleam to Nix.
Glistix v0.2.0 (2024-06-12)
-
Base Gleam version: v1.2.1
-
Updated Glistix to Gleam v1.2.1 (#1 and #3).
- This brings great improvements to the LSP, as well as many important bug fixes and improvements to diagnostics.
- Target
aarch64-unknown-linux-gnu
was added to CI.
-
Ported some fixes for the JavaScript target from Gleam v1.2.0 to the Nix target as well (#2).
- This fixes a miscompilation when using a module alias in a guard clause (see gleam-lang/gleam#3045).
Glistix v0.3.0 (2024-07-29)
-
Base Gleam version: v1.3.2
-
Updated Glistix to Gleam v1.3.2 (#8, #11 and #12).
- This release improves LSP autocomplete, adds arithmetic operation support to case clause guards, adds version specifier support to
gleam add
, and brings several other improvements and bug fixes to the compiler. - A prebuilt Wasm binary, in order to use the compiler in the browser, has been added to releases.
- This release improves LSP autocomplete, adds arithmetic operation support to case clause guards, adds version specifier support to
-
Ported some fixes for the JavaScript target from Gleam 1.3 to the Nix target as well (#13).
- This fixes a miscompilation when using a record constructor alias in a constant (see gleam-lang/gleam#3294).
- A similar fix has been made to record constructor aliases in case clause guards as well (see gleam-lang/gleam#3447).
- This also adds a compile-time error when trying to use a non-byte-aligned bit array on the Nix target (which currently only supports byte-aligned bit arrays).
- Finally, this ensures
gleam.nix
(exported to the build folder and used to import the Nix prelude) isn't unnecessarily rewritten to avoid problems with watchers.
-
Added Nix target support to language tests in the compiler (#10).
- This change doesn't directly affect Glistix users, but adds some proper testing of Gleam's language features to the Nix target, improving the likelihood of bugs being caught in the Nix target implementation.
Glistix v0.4.0 (2024-09-07)
-
Base Gleam version: v1.4.1
-
Updated Glistix to Gleam v1.4.1 (#15 and #19).
- This release adds label punning syntax (writing
function(data:)
instead offunction(data: data)
), adds support for the<>
(string concatenation) operator inconst
variables' definitions, as well as several language server improvements (listing document symbols, completion for record fields on access, signature help when calling functions, and so on). - The JavaScript target also received support for endianness (
little
andbig
) and signedness (signed
andunsigned
integers) options on bit arrays, as well as sized float options (32-bit and 64-bit) and pattern matching onutf8
bit arrays. These options are not yet supported on the Nix target (but will be on a future Glistix release). - The
gleam docs
(and thus the equivalentglistix docs
) command received support for--target
, allowing you to pick the target used to compile the project to retrieve docs for. This can be handy to generate Nix-specific docs withglistix docs --target nix
.
- This release adds label punning syntax (writing
Glistix v0.5.0 (2024-12-23)
-
Base Gleam version: v1.5.1
-
You can now try Glistix in your browser! Check out the Glistix online playground at https://glistix.github.io/playground
- The new playground supports compiling Gleam to Nix and quickly sharing Gleam code compiled to Nix to the web.
- It only supports importing the Gleam standard library for now, at an older version (0.38), but there are plans to include more packages in the future, such as
glistix_nix
. - Huge thanks to the Gleam team for providing the base code for the playground!
- Read more at the dedicated book page, which you can find by clicking here.
-
Updated Glistix to Gleam v1.5.1 (#26 and #29).
- This release brings great improvements to diagnostics and to the LSP. For example, there is now support for completion of local variables and arguments in functions, as well as missing module imports and missing
case
clauses suggestions, which are all very welcome quality of life features. - In addition, you can now omit
:utf8
in BitArray segments, writing just<<"Hello">>
instead of<<"Hello":utf8>>
. - You can read more in the official Gleam blog post here: https://gleam.run/news/convenient-code-actions/
- This release brings great improvements to diagnostics and to the LSP. For example, there is now support for completion of local variables and arguments in functions, as well as missing module imports and missing
-
Glistix now supports being compiled to WASM through the
compiler-wasm
crate (#22).- The Glistix web files are now distributed on every release with the
-browser
suffix. - This was essential to have the playground work!
- The Glistix web files are now distributed on every release with the
-
Glistix now pins the Rust version it uses in its official flake through
fenix
, so you can compile Glistix from source using Nix more reliably (#25).
Glistix v0.6.0 (2025-01-19)
-
Base Gleam version: v1.6.3
-
Updated Glistix to Gleam v1.6.3 (#34 and #35).
- Remarkably for Docker container users, this release brings a new
scratch
container without anything installed other than the Glistix compiler, which is useful for Nix target users. - In addition, this release brings variant inference in pattern matching, allowing one to access and update fields specific to each matched type variant in a
case
expression, as well as more LSP code actions such as to add missing type annotations to a function. - You can read more in the official Gleam blog post here: https://gleam.run/news/context-aware-compilation/
- Remarkably for Docker container users, this release brings a new
Glistix v0.7.0 (2025-03-15)
-
Base Gleam version: v1.7.0
-
Updated Glistix to Gleam v1.7.0 (#39).
- Most notably, this brings support for Nix FFI files in subdirectories! This is a very important step for Glistix, as it allows, for example, gradual adoption of Gleam in an existing Nix config or codebase by interfacing with existing files.
- Other goodies include custom pattern matching errors, record variant deprecation, generate decoder code action (soon to be fully supported by Glistix when
glistix_stdlib
is updated), extract variable code action, and more. - See the Gleam blog post for the full news: https://gleam.run/news/improved-performance-and-publishing/
-
Dependency patching for Nix support has received great improvements! (#44 and #48)
-
Read "Migration instructions" below to upgrade existing Glistix projects.
-
There is a new
gleam.toml
section for Glistix projects:[glistix.preview.patch]
. -
This section has a similar syntax to the dependencies section, and is used to replace transitive dependencies with other packages, even if they have different names, such as forks adding Nix target compatibility.
-
For example, one can replace
gleam_stdlib
withglistix_stdlib
(now published on Hex) using the snippet below:[glistix.preview.patch] gleam_stdlib = { name = "glistix_stdlib", version = ">= 0.34.0, < 2.0.0" }
-
With that snippet, all packages in your dependencies which depend on
gleam_stdlib
will depend onglistix_stdlib
instead, thus gaining Nix target support (where possible). -
Please note that this setting only has an effect on top-level projects. Patches from dependencies are ignored, though they may have their own patches to be able to run unit tests, for example (so it's still useful for packages).
-
This setting effectively deprecates the existing
local-override
andhex-patch
options.local-override
can be achieved throughpatch
by patching an incompatible local package to the same local package your project is using, whereashex-patch
can be achieved by specifying the dependencies you want published to Hex in[dependencies]
and the dependencies you want to use when running your project in[glistix.preview.patch]
. After some time, it's likely those settings will be removed.
-
-
Glistix now uses a "patchset" model for fork maintenance, which should hopefully lead to faster and cleaner Gleam version bumps. (#42 and #43)
- This works by keeping a branch with all changes applied to the upstream Gleam codebase. A Gleam version bump then consists of rebasing those patches on top of the updated codebase, letting us know which patches are outdated and which patches need fixing, rather than relying solely on the maintainer's recollection of events.
-
Backported an upstream fix for publishing packages with native (
.nix
) files in subdirectories. (#46)- This ensures you can publish packages to Hex using the new feature of
.nix
files in subdirectories introduced in this update.
- This ensures you can publish packages to Hex using the new feature of
-
Some JS target fixes from Gleam v1.7.0 were ported to the Nix target as well. (#40)
- If you have a file named
file.gleam
and another namedfile.nix
, you will now receive an error as that is invalid. - Fixes a bug where string pattern matching nested in patterns would generate wrong Nix code.
- If you have a file named
Migration instructions
-
There are new instructions for patching Gleam packages with Nix-compatible forks, as noted in "Overriding incompatible packages".
-
If you were using the default Glistix project configuration, you're likely depending on the standard library through a local dependency on a Git submodule.
In that case, you should remove the stdlib submodule and start using a patch to
glistix_stdlib
which is available on Hex using the instructions below.-
Run
git rm external/stdlib
to remove theexternal/stdlib
submodule. -
Replace the local
gleam_stdlib
dependency with either a direct dependency onglistix_stdlib
, or with a dependency on the originalgleam_stdlib
together with a patch. (A patch is necessary regardless of your choice, since your dependencies themselves will still be depending ongleam_stdlib
.)Tip: You can check out the new default
gleam.toml
file for Glistix projects in "Project configuration" and copy its contents over to your project, allowing you to quickly apply the necessary changes to your configuration.[dependencies] - gleam_stdlib = { path = "./external/stdlib" } + gleam_stdlib = ">= 0.34.0 and < 2.0.0" + # OR (for packages only available for the Nix target): + # glistix_stdlib = ">= 0.34.0 and < 2.0.0" + + [glistix.preview.patch] + # Replaces 'gleam_stdlib' with 'glistix_stdlib' on all transitive dependencies. + # This is needed so stdlib will work on the Nix target. + gleam_stdlib = { name = "glistix_stdlib", version = ">= 0.34.0 and < 2.0.0" }
-
Delete the
local-overrides
and[glistix.preview.hex-patch]
settings.- [glistix.preview] - local-overrides = ["gleam_stdlib"] - - [glistix.preview.hex-patch] - gleam_stdlib = ">= 0.34.0 and < 2.0.0"
-
-
Repeat the procedure above for other forks, such as
glistix_json
orglistix_birl
. -
If you were actually using
local-overrides
to bypass a conflict between a local dependency in your root project and in a dependency, you can replace this with a patch.[dependencies] local-dep = { path = "external/local-dep" } - [glistix.preview] - local-overrides = ["local-dep"] + [glistix.preview.patch] + # Force transitive dependencies on local-dep to use the same path + local-dep = { path = "external/local-dep" }
-
If you were using
hex-patch
to be able to use local dependencies and switch to a Hex dependency when publishing your package, you can move your local dependencies to[glistix.preview.patch]
and your Hex dependencies to[dependencies]
.- [dependencies] - dep = { path = "external/dep-fork" } - - [glistix.preview.hex-patch] - dep = "1.2.3" + [dependencies] + dep = "1.2.3" + + [glistix.preview.patch] + # Use local dependency when testing package locally + dep = { path = "external/dep-fork" }