Gleam is a type-safe and scalable language for the Erlang virtual machine and JavaScript runtimes. Today Gleam v1.13.0 has been published. Let's go over at the highlights now.
External API for Gleam data
One of Gleam's strengths is that it is part of the BEAM and JavaScript ecosystems, enabling Gleam programs to take advantage of code written in Erlang, Elixir, JavaScript, and more. This is a large part of how Gleam was able to become a practical production-ready language so quickly, by not restricting Gleam programmers to just the comparatively young Gleam package ecosystem.
A function written in one of these other languages can be imported into a Gleam module as an "external function", and then called without any additional performance overhead.
@external(erlang, "moon_base", "launch_spaceship") pub fn lift_off(countdown: Int) -> Result(Spaceship, LaunchError)Here the Erlang function launch_spaceship from the module moon_base is being imported.
One restriction to external functions is that they will need to return data types that Gleam can understand. Gleam's "external type" feature lets the programmer refer to types defined in other languages, but if the programmer wants to be able to directly construct and pattern match data from external functions it will need to be in a format compatible with Gleam data types.
Often a programmer will write a small wrapper function in the external language to convert the data, and also to make the interface adhere to Gleam patterns and conventions.
-module(moon_base). -export([launch_spaceship/1]). launch_spaceship(Countdown) -> try Spaceship = launch_control:launch_spaceship(Countdown), {ok, Spaceship} catch error:no_fuel -> {error, no_fuel}; error:bad_weather -> {error, bad_weather} end.This launch_spaceship Erlang function wraps the function from the launch_control module, converting the exception-based API into a Result type, the Erlang representation of Gleam's Ok and Error variants.
One thing that a Gleam programmer may find challenging is knowing how to construct Gleam data in these wrapper functions. A lack of detailed documentation made it unclear what the correct approach should be.
This lack of clarity makes learning how to use external code more challenging, and worse, it may result in programmers using internal APIs that are intended only to be used by the compiled Gleam code. If the Gleam ecosystem were to grow with many packages using these internal APIs, it would force the Gleam core team to support them as if they were public APIs. Committing to these APIs would greatly limit what changes we can make to the internal representation of Gleam data, and make many potential performance improvements impossible.
To fix this we have done two things. First, we have created a guide on Gleam externals, detailing how to correctly write and use externals.
Secondly, a dedicated API is now provided for JavaScript based code to work with Gleam data, both making usage clearer and giving the Gleam core team maximum freedom to improve performance in future. Each data type defined in Gleam will have a set of functions defined to work with it, for example:
pub type Person { Teacher(name: String, subject: String) Student(name: String) } import {...} from "./person.mjs"; let teacher = Person$Teacher("Joe Armstrong", "Computer Science"); let student = Person$Student("Louis Pilfold"); let randomPerson = Math.random() > 0.5 ? teacher : student; let randomIsTeacher = Person$isTeacher(randomPerson); let teacherSubject = Person$Teacher$subject(teacher); let personName = Person$name(randomPerson);There will be a migration period where existing JavaScript externals will need to migrate over to the new API. We have created tooling to analyse the Gleam package ecosystem to identify code that is in need of updating, and we will be helping with this process.
Further additions will be made to the externals guide detailing useful patterns, how to avoid common problems, and advising when and how to use externals.
Thank you Surya Rose for taking the lead role in implementing these new APIs, and for the Gleam team more widely for the design of this addition!
Improved bit array exhaustiveness checking
Gleam's bit array syntax allows you to declaratively construct and parse binary data in a way that may be easier to understand than using binary operators.
The compiler now applies an optimisation known as "interference based pruning" when compiling bit array pattern matching where matches are performed at the start of bit arrays. This optimisation drastically reduces compile times, memory usage and the compiled code size, removing many redundant checks.
It is particularly impactful for programs that pattern match on some fixed patterns at the start of the bit array. For example, network protocol parsers.
pub fn parser_headers(headers: BitArray, bytes: Int) -> Headers { case headers { <<"CONTENT_LENGTH" as header, 0, value:size(bytes), 0, rest:bytes>> | <<"QUERY_STRING" as header, 0, value:size(bytes), 0, rest:bytes>> | <<"REQUEST_URI" as header, 0, value:size(bytes), 0, rest:bytes>> | <<"REDIRECT_STATUS" as header, 0, value:size(bytes), 0, rest:bytes>> | <<"SCRIPT_NAME" as header, 0, value:size(bytes), 0, rest:bytes>> -> [#(header, value), ..parse_headers(rest)] } }Additionally, the compiler now raises a warning for unreachable branches that are matching on bit array segments that could never match. Consider this example:
pub fn get_payload(packet: BitArray) -> Result(BitArray, Nil) { case packet { <<200, payload:bytes>> -> Ok(payload) <<404, _:bits>> -> Error(Nil) _ -> Ok(packet) } }There's a subtle bug here. The second branch can never match since it's impossible for the first byte of the bit array to have the value 404. The new error explains this nicely:
warning: Unreachable pattern ┌─ /src.gleam:4:5 │ 4 │ <<404, _:bits>> -> Error(Nil) │ ^^^^^^^^^^^^^^^ │ │ │ A 1 byte unsigned integer will never match this value This pattern cannot be reached as it contains segments that will never match. Hint: It can be safely removed.Thank you Giacomo Cavalieri for these improvements! Exhaustiveness checking is a very complex field, so these additions are very impressive.
Unused argument detection
Gleam's unused code detection and purity tracking emits a warning any time some code is unused and could be removed without changing the behaviour of the program.
This has been extended to be able to identify function arguments that are used when the function calls itself recursively, but never actually used in the function's implementation. For example:
import gleam/io pub fn greet(x, times) { case times { 0 -> Nil _ -> { io.println("Hello, Joe!") greet(x, times - 1) } } }In this piece of code the x argument is unused, so the compiler will raise the following warning:
warning: Unused function argument ┌─ /Users/giacomocavalieri/Desktop/prova/src/prova.gleam:3:14 │ 3 │ pub fn greet(x, times) { │ ^ This argument is unused This argument is passed to the function when recursing, but it's never used for anything.Thank you Giacomo Cavalieri!
Better meaningless opaque type error
A public custom type can be marked as "opaque", meaning that while other modules can import and reference the type, they are unable to construct or pattern match on values of that type. This is useful for restricting the ways that a data type can be used in order to provide a more robust API.
pub opaque type Permission { SuperUser Regular Restricted }It is invalid to mark a private type as opaque. Previously this would result in a slightly cryptic syntax, but now a specific helpful error has been added for this case.
error: Private opaque type ┌─ /src/one/two.gleam:2:1 │ 2 │ opaque type Wibble { │ ^^^^^^ You can safely remove this. Only a public type can be opaque.The language server now also offers a "quick fix" code action to remove opaque from a private type:
opaque type Wibble { Wobble }If you hover over the type and trigger the quick fix, the language server will automatically remove the opaque keyword:
type Wibble { Wobble }Thank you Giacomo Cavalieri!
More fault tolerance
Gleam's compiler implements fault tolerant analysis. This means that when there is some error in the code the compiler can still continue to analyse the code to the best of its ability, ignoring the invalid parts. Because of this, the Gleam language server can have a good understanding of the code and provide IDE features even when the codebase is in an invalid state.
Giacomo Cavalieri and sobolevn) have improved the compiler to be fault tolerant for errors relating to analysis of labeled fields in variant patterns, parsing of private opaque type definitions, and parsing of type names followed by (), further improving the experience of using the Gleam language server.
Thank you both!
Redundant pattern alias warning
_ as x is a valid pattern in Gleam. The _ means "don't assign any name to this value", and the as x part means "assign the name x to this value".
As you can see, this is quite a silly pattern. The alias as pattern makes the discard _ pattern redundant, and it would always be better to use the pattern x, which means "assign this value to the name x".
let _ as x = something() let x = something()Using an alias pattern with a discard pattern has been deprecated, and the Gleam code formatter will rewrite any instances of it to the recommended syntax.
Thank you eutampieri for this!
More inefficient list check warnings
Gleam's basic sequence type is an immutable linked list with structural sharing, a data type inherited from Erlang and one common in functional programming languages.
The correct way to check if a list is empty is to pattern match on it with the empty-list pattern, or to compare it to an empty list literal.
pub fn both_empty(list1: List(a), list2: List(b)) -> Bool { let list1_empty = case list1 { [] -> True _ -> False } let list2_empty = list2 == [] list1_empty && list2_empty }The standard library's list.length function returns the length of a given list. Gleam and Erlang lists don't store the length on them as a static property, so this function has to traverse the full list, and count the number of elements, making it a very wasteful way to determine if a list is empty.
This behaviour may be suprising to programmers familiar with languages with a different core sequence data type, so they might not realise this is not a good way to check for an empty list. To remove this confusion the compiler would emit a warning for code like list.length(items) == 0, informing the programmer of better alternatives.
With this release the warning will also be emitted for more inefficient use of list.length, including checks for non-empty lists using operators like > and <.
warning: Inefficient use of `list.length` ┌─ /data/data/com.termux/files/home/test_gleam/src/test_gleam.gleam:5:13 │ 5 │ let _ = 0 < list.length(numbers) │ ^^^^^^^^^^^^^^^^^^^^^^^ The `list.length` function has to iterate across the whole list to calculate the length, which is wasteful if you only need to know if the list is empty or not. Hint: You can use `the_list != []` instead.Thank you Andrey Kozhev!
A helpful syntax error for JavaScripters
In Gleam names are assigned to values within functions using the let keyword. There is a const keyword too, but it is used to declare module-level constants, and it is a syntax error to use it within functions.
The compiler now provides an helpful error message for any programmers accustomed to languages where const is used within functions, such as JavaScript.
pub fn deep_thought() -> Int { const the_answer = 42 the_answer } error: Syntax error ┌─ /src/file.gleam:2:3 │ 3 │ const the_answer = 43 │ ^^^^^ Constants are not allowed inside functions All variables are immutable in Gleam, so constants inside functions are not necessary. Hint: Either move this into the global scope or use `let` binding instead.Thank you Surya Rose!
A helpful error for Rustaceans, C#ers, and friends
Gleam has a consistent syntax for constructors at the type and value level, using () for both. It does not use () for value constructors and <> for type constructors, as is common in some other languages.
To help folks coming from other languages a set of helpful errors have been added for when they try to use this non-Gleam syntax in their Gleam code.
error: Syntax error ┌─ /src/parse/error.gleam:2:12 │ 2 │ type Either<a, b> { │ ^ I was expecting `(` here. Type parameters use lowercase names and are surrounded by parentheses. type Either(a, b) { See: https://tour.gleam.run/data-types/generic-custom-types/Notice how the error message includes the correct syntax for the specific code that the programmer has written. The programmer could copy/paste the correct version into their code, if they so desired. A link to the documentation is also provided, linking to whichever feature the syntax error is for.
Thank you Aaron Christiansen!
A helpful syntax error for Pythonistas, Elixirists, and friends
Similar to the last two new error messages, there's now a helpful error message for programmers trying to use # instead of // to write a comment. Thank you sobolevn!
Displaying dependency version information
Gleam's built tool integrates with Hex, the package management system and primary package repository for the BEAM ecosystem. Gleam implements dependency version locking to ensure that builds are deterministic, and to prevent unaudited code from unexpectedly becoming part of your application. Dependency code is just as much of a risk and responsibility as code directly written by the programmer, so it must be treated with great care and consideration.
The only time the build tool will select new versions of dependency packages is if the programmer adds or removes a dependency, if the programmer changes the package's dependency version requirements, or if the programmer requests the dependency versions be upgraded using the gleam update command.
In these cases when dependencies are changed, added, or removed the build tool will now print the changes, to help the programmer understand and go on to audit the new code.
Resolving versions Downloading packages Downloaded 3 packages in 0.04s Added gleam_json v3.0.2 Added houdini v1.2.0 Added lustre v5.3.5hex owner transfer
The hex owner transfer command has been added to the build tool, allowing Gleam programmers to transfer ownership of existing Hex packages to another account. Thank you Giacomo Cavalieri!
Improved type displaying
When a Gleam package is published to Hex HTML documentation is generated and published to the HexDocs documentation hosting website. This documentation has now been improved to now print the names of public type aliases instead of internal type names when annotating functions and types. This makes the documentation more likely to use the APIs that the package author intends for their users.
For example, for the following code:
import my_package/internal pub type ExternalAlias = internal.InternalRepresentation pub fn do_thing() -> ExternalAlias { ... }This is what the build tool used to generate:
pub fn do_thing() -> @internal InternalRepresentationThis is technically correct, but not very useful for the reader of the documentation, as they cannot learn anything about these internal types. Now it will not use the internal name, allowing the programmer to click-through to its documentation, and understand how they should refer to this type in their code.
pub fn do_thing() -> ExternalAliasThis improvement also applies to the language server, both in information displayed on hover, and in code actions such as "add annotations".
import lustre/html import lustre/element import lustre/attribute pub fn make_link(attribute, element) { html.a([attribute], [elements]) }If the "add annotations" code action is run on this function the language server will identify that the imported lustre/attribute module has public export of the Attribute type, and that the imported lustre/element module has public export of the Element type, so they will both be used instead of the internal definition.
pub fn make_link( attribute: attribute.Attribute, element: element.Element(a) ) -> element.Element(a) { html.a([attribute], [elements]) }Thank you Surya Rose!
Improved type naming in code actions
Another way in which code actions need to consider type names is with type parameters. The "add type annotations" and "generate function" code actions must find names for any type variables that do not clash with any already in use. Previously the language server would track names in-use at the module level which could result in correct but unexpected names being used.
Take this code, for example.
fn something(a: a, b: b, c: c) -> d { todo } fn pair(a, b) { #(a, b) }Previously, when triggering the "Add type annotations" code action on the pair function, the language server would have used these names:
fn pair(a: e, b: f) -> #(e, f) { #(a, b) }However in 1.13, it will now use these names:
fn pair(a: a, b: b) -> #(a, b) { #(a, b) }Thank you Surya Rose!
Tangled support
Tangled is a new open source source forge, and the Gleam build tool now has support for it. When specified the HTML documentation will include links to the source code definitions for each type and value in the package, as it would for previously supported source forges such as GitHub and Forgejo.
repository = { type = "tangled", user = "me", repo = "my_project" }Thank you to Naomi Roberts for this. As part of this work she added support for links to multi-line sections of code in Tangled itself!
Further language server support
The language server was missing support for a few syntaxes, this has now been fixed. You can now go to definition, rename, etc. from alternative patterns in case expressions:
case wibble { Wibble | Wobble -> 0 }And hovering over a record field in a record access expression will now show the documentation for that field, if any exists.
Thank you Surya Rose and fruno!
Remove unreachable clauses code action
Gleam's pattern matching analysis can identify any clauses of a case expression are unreachable due to previous patterns already matching any values the redundant one could match.
pub fn main() { case find_user() { Ok(person) -> todo Ok(Admin) -> todo Ok(User) -> todo Error(_) -> todo } }Here the Ok(Admin) and Ok(User) patterns could never match as all the Ok values would be instead matched by the earlier Ok(person) pattern.
This is clearly a mistake in the code, so the compiler will emit a warning and highlight the unreachable clauses. Most commonly the programmer will want to edit the patterns to correct them, but some times the clauses are no longer needed, and can be deleted entirely. To help with this scenario the language server now offers a quick-fix code action to delete any redundant clauses.
Triggering it on the above code will result in the code being edited like so:
pub fn main() { case find_user() { Ok(person) -> todo Error(_) -> todo } }Thank you Giacomo Cavalieri!
Pattern match on value code action improvements
Gleam has a single flow-control feature: pattern matching with the case expression. Because of this there's a lot of pattern matching in Gleam programs! The language server offers a code action to quickly pattern match on a focused value, and with this release it has been further improved.
It can now be triggered on lists, with the default clauses including one for when the list is empty, and one for when it is non-empty.
pub fn is_empty(list: List(a)) -> Bool { }Triggering the action over the list argument would result in the following code:
pub fn is_empty(list: List(a)) -> Bool { case list { [] -> todo [first, ..rest] -> todo } }The code action can now be triggered on variables introduced by other patterns. For example, here we have a let statement with a pattern defining the variables name and role.
pub fn main() { let User(name:, role:) = find_user("lucy") }Triggering the action on role results in a case expression being inserted on the line below, with a clause for each of the possible variants of the Role type that variable held.
pub fn main() { let User(name:, role:) = find_user("lucy") case role { Admin -> todo Member -> todo } }If the variable was introduced within a case expression already then the behaviour is different. For example:
pub fn main() { case find_user() { Ok(user) -> todo Error(_) -> todo } }Triggering the code action on the user variable would cause the code to be rewritten to expand that clause of the case expression, replacing it with one specialised clause for each of the possible variants of the type of that variable.
pub fn main() { case find_user() { Ok(Admin) -> todo Ok(Member) -> todo Error(_) -> todo } }Thank you Giacomo Cavalieri! Pattern matching is such a important core feature of Gleam that these improvements make a big difference to the experience of writing and editing Gleam code.
Collapse nested case expressions code action
Another new code action is one to collapse nested case expressions into one, reducing nesting and enabling further optimisations in some situations.
case user { User(name:) -> case name { "Joe" -> "Hello, Joe!" _ -> "Hello there!" } Guest -> "You're not logged in!" }Triggering the code action on the first clause will result in it being replaced by multiple clauses that produce the same behaviour as the nested version.
case user { User(name: "Joe") -> "Hello, Joe!" User(name: _) -> "Hello there!" Guest -> "You're not logged in!" }Thank you Giacomo Cavalieri!
Add omitted labels code action
Function parameters and record fields can have labels, names that can be used at the call-site to make it clearer what each argument is, and to make the ordering of the arguments not matter. The language server now offers a code action to add the omitted labels in a call. For example:
pub type User { User(first_name: String, last_name: String, likes: List(String)) } pub fn main() { let first_name = "Giacomo" User(first_name, "Cavalieri", ["gleam"]) }Triggering the code action on the User constructor will result in the language server adding the labels to the arguments.
pub type User { User(first_name: String, last_name: String, likes: List(String)) } pub fn main() { let first_name = "Giacomo" User(first_name:, last_name: "Cavalieri", likes: ["gleam"]) }Inter-module generate function code action
The "Generate function" code action now works when the missing function is to be defined in another module. For example:
pub fn add(a: Int, b: Int) -> Int { a + b } import maths pub fn main() -> Nil { echo maths.add(1, 2) echo maths.subtract(2, 1) Nil }The app module is calling a function called subtract from the maths module, but that function doesn't exist. Triggering the code action on the call to maths.subtract will edit the maths.gleam file to add the outline of the function, for the programmer to complete.
pub fn add(a: Int, b: Int) -> Int { a + b } pub fn subtract(int: Int, int_2: Int) -> Int { todo }Thank you Surya Rose! This is a nice little quality-of-life improvement for Gleam programmers.
And the last code action of this release, one that has been eagerly anticipated for some time by many Gleam programmers: extract function.
const head_byte_count = 256 pub fn get_head_of_file() { let assert Ok(contents) = read_file() case contents { <<head:bytes-size(head_byte_count), _:bits>> -> Ok(head) _ -> Error(Nil) } }If you were to select the case expression in your editor and trigger the code action, then it would be extracted to a new function, like so:
const head_byte_count = 256 pub fn get_head_of_file() { let assert Ok(contents) = read_file() function(contents) } fn function(contents: BitArray) -> Result(BitArray, Nil) { case contents { <<head:bytes-size(head_byte_count), _:bits>> -> Ok(head) _ -> Error(Nil) } }Unfortunately the language server protocol design is rather limiting, so the Gleam language server cannot prompt the programmer for a suitable name, it has to use a meaningless name instead. The "rename" feature of the language server can be triggered to give it a more appropriate name.
I believe that Microsoft's rewrite of the TypeScript toolchain will include it using the language server protocol instead of their custom protocol, so hopefully this will result in them expanding the LSP specification to include features their custom protocol has, such as code actions being able to ask for more information.
Thank you again Surya Rose!
Formatter improvements
Gleam has a code formatter that can clean up a file of Gleam code in an instant, freeing up time that would be otherwise spent on the busy-work of manually laying out code. Typically it is run by the programmer's text editor when a file is saved. Several improvements have been made to it with this release.
Bools can be negated with the ! operator, and ints can be negated with the - operator. Negating a value multiple times is redundant and does nothing, so the formatter now collapses duplicate negations.
pub fn useless_negations() { let lucky_number = --11 let lucy_is_a_star = !!!False }The code is rewritten like so:
pub fn useless_negations() { let lucky_number = 11 let lucy_is_a_star = !False }Additionally, the formatter no longer removes blocks from case clause guards, as the programmer may wish to include them to make code clearer, even if they are not required according to Gleam's operator precedency rules. This also makes the behaviour consistent with the formatting of regular expressions in Gleam functions.
Thank you Giacomo Cavalieri!
And the rest
And thank you to the bug fixers and experience polishers: Andrey Kozhev, Benjamin Peinhardt, Danielle Maywood, fruno, Giacomo Cavalieri, Joohoon Cha, Matias Carlander, Surya Rose, and Tristan-Mihai Radulescu).
For full details of the many fixes and improvements they've implemented see the changelog.
A call for support
Gleam is not owned by a corporation; instead it is entirely supported by sponsors, most of which contribute between $5 and $20 USD per month, and Gleam is my sole source of income.
We have made great progress towards our goal of being able to appropriately pay the core team members, but we still have further to go. Please consider supporting the project or core team members Giacomo Cavalieri and Surya Rose on GitHub Sponsors.
Thank you to all our sponsors! And special thanks to our top sponsors:
- 0riginaln0
- Aaron Christiansen
- Aaron Gunderson
- Abel Jimenez
- abs0luty
- ad-ops
- Adam Brodzinski
- Adam Johnston
- Adam Shannon
- Adam Wyłuda
- Adi Iyengar
- Adrian Mouat
- Ajit Krishna
- Aleksei Gurianov
- Alembic
- Alex Houseago
- Alex Manning
- Alexander Stensrud
- Alexandre Del Vecchio
- Ameen Radwan
- Andrea Bueide
- Andrew Ivchenkov
- Andrey
- André Mazoni
- Andy Young
- Antharuu
- Anthony Khong
- Anthony Maxwell
- Anthony Scotti
- Arya Irani
- Austin Beau Bodzas
- Azure Flash
- Barry Moore II
- Ben Martin
- Ben Marx
- Ben Myles
- benev0
- Benjamin
- Benjamin Kane
- Benjamin Moss
- bgw
- Billuc
- Bjarte Aarmo Lund
- Bjoern Paschen
- bondiano
- Brad Mehder
- Brett Kolodny
- Brian Dawn
- Brian Dukes
- Brian Glusman
- Bruce Williams
- Bruno Michel
- bucsi
- Cam Ray
- Cameron Presley
- Carlo Munguia
- Carlos Saltos
- Cassidy Spring (Bee)
- Chad Selph
- Charlie Govea
- Chengjun Xie
- Chew Choon Keat
- Chris Donnelly
- Chris King
- Chris Lloyd
- Chris Ohk
- Chris Rybicki
- Chris Vincent
- Christopher David Shirk
- Christopher De Vries
- Christopher Dieringer
- Christopher Jung
- Christopher Keele
- CJ Salem
- Clifford Anderson
- Coder
- Cole Lawrence
- Comamoca
- Comet
- Constantin (Cleo) Winkler
- Corentin J.
- Courtcircuits
- Cris Holm
- Damir Vandic
- Dan
- Dan Dresselhaus
- Dan Gieschen Knutson
- Dan Strong
- Daniel Kurz
- Daniel S Jeremiah
- Danielle Maywood
- Daniil Nevdah
- Danny Arnold
- Danny Martini
- Dave Lucia
- David Bernheisel
- David Cornu
- David Pendray
- Dennis Lustre
- dependabot[bot
- Diego
- Diemo Gebhardt
- Donnie Flood
- duzda
- Dylan Anthony
- Dylan Carlson
- Ed Hinrichsen
- Ed Rosewright
- Edon Gashi
- Eileen Noonan
- eli
- Eli Treuherz
- Emma
- Eric Koslow
- Erik Terpstra
- erikareads
- ErikML
- erlend-axelsson
- Ernesto Malave
- Ethan Olpin
- eutampieri
- Evaldo Bratti
- Evan Johnson
- evanasse
- Fabrizio Damicelli
- Falk Pauser
- Fede Esteban
- Felix
- Fernando Farias
- Filip Figiel
- Fleey
- Florian Kraft
- Francis Hamel
- Francisco Budaszewski Zanatta
- Francisco Budaszewski Zanatta
- frankwang
- fruno
- G-J van Rooyen
- Gabriel Vincent
- Gareth Pendleton
- Gavin Panella
- GearsDatapacks
- Geir Arne Hjelle
- ggobbe
- Giacomo Cavalieri
- ginkogruen
- Giovanni Kock Bonetti
- godalming123
- Graham
- Grant Everett
- graphiteisaac
- Guilherme de Maio
- Guillaume Heu
- Guillaume Hivert
- Hammad Javed
- Hannes Nevalainen
- Hannes Schnaitter
- Hans Raaf
- Hayleigh Thompson
- Hazel Bachrach
- Henning Dahlheim
- Henrik Tudborg
- Henry Warren
- Hizuru3
- Hubert Małkowski
- Iain H
- Ian González
- Ian M. Jones
- Igor Montagner
- imlargo
- inoas
- Isaac Harris-Holt
- Isaac McQueen
- Ivar Vong
- Jachin Rupe
- Jacob Lamb
- Jake Cleary
- Jake Wood
- James Birtles
- James MacAulay
- Jan Pieper
- Jan Skriver Sørensen
- jcha0713
- Jean Niklas L'orange
- Jean-Adrien Ducastaing
- Jean-Luc Geering
- Jean-Marc QUERE
- Jen Stehlik
- Jerred Shepherd
- Jesse Cooke
- Jimpjorps™
- Joey Kilpatrick
- Joey Trapp
- Johan Strand
- John Björk
- JOHN STEINBERGER
- John Strunk
- Jojor
- Jon Charter
- Jon Lambert
- Jonas E. P
- Jonas Hedman Engström
- jooaf
- Joseph Lozano
- Joseph Myers
- Joseph T. Lyons
- Joshua Steele
- Julian Hirn
- Julian Lukwata
- Julian Schurhammer
- Justin Lubin
- justisCrocker
- Jérôme Schaeffer
- Jørgen Andersen
- Kadei
- KamilaP
- Kemp Brinson
- Kero van Gelder
- Kevin Schweikert
- khalidbelk
- Kristoffer Grönlund
- Kritsada Sunthornwutthikrai
- Krzysztof Gasienica-Bednarz
- Kuma Taro
- Landon
- lazno
- Leah Ulmschneider
- Leandro Ostera
- Lee Jarvis
- Lennon Day-Reynolds
- Leon Qadirie
- Leonardo Donelli
- Lexx
- lidashuang
- linqingmo
- Lukas Bjarre
- Luke Amdor
- Luna
- Manuel Rubio
- marcus
- Mario Vellandi
- Marius Kalvø
- Mark Dodwell
- Mark Holmes
- Mark Markaryan
- Mark Rudolph
- Marshall Bowers
- Martin Janiczek
- Martin Poelstra
- Martin Rechsteiner
- matiascr
- Matt Heise
- Matt Mullenweg
- Matt Savoia
- Matt Van Horn
- Matthew Jackson
- Matthew Whitworth
- Max McDonnell
- Maxim Philippov
- metame
- METATEXX GmbH
- Metin Emiroğlu
- Michael Duffy
- Michael Jones
- Michael Mazurczak
- Michal Timko
- Mikael Karlsson
- Mike Roach
- Mikey J
- MoeDev
- MzRyuKa
- n8n - Workflow Automation
- Naomi Roberts
- Natalie Rose
- Natanael Sirqueira
- Nathaniel Knight
- Nguyễn Hồng Quân
- Nick Chapman
- Nicklas Sindlev Andersen
- NicoVIII
- Niket Shah
- Nikolai Steen Kjosnes
- Ninaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
- NineFX
- Noah Betzen
- Nomio
- nunulk
- Ocean
- Olaf Sebelin
- OldhamMade
- Oliver Medhurst
- Oliver Tosky
- ollie
- optizio
- Patrick Wheeler
- Pattadon Sa-ngasri
- Paul Guse
- Pedro Correa
- peonynopes
- Pete Jodo
- Peter Rice
- Philpax
- Pierre Rouleau
- Qdentity
- Quentin BETTOUM
- Race Williams
- raineycat
- Rasmus
- Raúl Chouza
- Redmar Kerkhoff
- Reilly Tucker Siemens
- Renata Amutio Herrero
- Renato Massaro
- Renovator
- Richard Viney
- Rico Leuthold
- Rintaro Okamura
- Ripta Pasay
- Robert Attard
- Robert Ellen
- Robert Malko
- Rodrigo Álvarez
- Rotabull
- Rupus Reinefjord
- Ruslan Ustitc
- Russell Clarey
- Sakari Bergen
- Sam Aaron
- Sam Zanca
- sambit
- Sammy Isseyegh
- Samu
- Savva
- Saša Jurić
- Scott Trinh
- Scott Wey
- Scott Zhu Reeves
- Sean Cribbs
- Sean Roberts
- Sebastian Porto
- Seve Salazar
- Sgregory42
- Shane Poppleton
- Shawn Drape
- Shritesh Bhattarai
- Sigma
- simone
- sobolevn
- Stefan
- Stefan Hagen
- Steinar Eliassen
- Stephen Belanger
- Strandinator
- Sławomir Ehlert
- TA
- Theo Harris
- Thomas
- Thomas Coopman
- Thomas Crescenzi
- Tim Brown
- Timo Sulg
- Tom Hughes
- Tom Schuster
- Tomasz Kowal
- tommaisey
- Tristan de Cacqueray
- Tristan Sloughter
- Tudor Luca
- upsidedowncake
- Valerio Viperino
- Viv Verner
- Volker Rabe
- vshakitskiy
- Walton Hoops
- Weizheng Liu
- Willyboar
- Wilson Silva
- wingdeans
- yagogarea
- Yamen Sader
- Yasuo Higano
- yoshie
- zenconomist
- Zsombor Gasparin
- ZWubs
- ~1814730
- ~1847917
- ~1867501
- Éber Freitas Dias
.png)

