For those that don't know its also built upon OTP, the erlang vm that makes concurrency and queues a trivial problem in my opinion.
Absolutely wonderful ecosystem.
I've been wanting to make Gleam my primary language, but I fear LLMs have frozen programming language advancement and adoption for anything past 2021.
But I am hopeful that Gleam has slid just under the closing door and LLMs will get up to speed on it fast.
This isn't correct. It can compile to run on the BEAM: that is the Erlang VM. OTP isn't the Erlang VM; rather, "OTP is set of Erlang libraries and design principles providing middle-ware to develop [concurrent/distributed/fault tolerant] systems."
Gleam itself provides what I believe is a substantial subset of OTP support via a library: https://github.com/gleam-lang/otp
Importantly: "Gleam has its own version of OTP which is type safe, but has a smaller feature set. [vs. Elixir, another BEAM language with OTP support]"
The comment you are replying to is correct, and you are incorrect.
All OTP APIs are usable as normal within Gleam, the language is designed with it in mind, and there’s an additional set of Gleam specific additions to OTP (which you have linked there).
Gleam does not have access to only a subset of OTP, and it does not have its own distinct OTP inspired OTP. It uses the OTP framework.
The library the parent links to says this:
> Not all Erlang/OTP functionality is included in this library. Some is not possible to represent in a type safe way, so it is not included.
Does this mean in practice that you can use all parts of OTP, but you might lose type checking for the parts the library doesn't cover?
What's the state of Gleam's JSON parsing / serialization capabilities right now?
I find it to be a lovely little language, but having to essentially write every type three times (once for the type definition, once for the serializer, once for the deserializer) isn't something I'm looking forward to.
A functional language that can run both on the backend (Beam) and frontend (JS) lets one do a lot of cool stuff, like optimistic updates, server reconciliation, easy rollback on failure etc, but that requires making actions (and likely also states) easily serializable and deserializable.
But also, you shouldn’t think of it as writing the same type twice! If you couple your external API and your internal data model you are greatly restricting your domain modelling cability. Even in languages where JSON serialisation works with reflection I would recommend having a distinct definition for the internal and external structure so you can have the optimal structure for each context, dodging the “lowest common decimator” problem.
I'm waiting for something similar to serde in Rust, where you simply tag your type and it'll generate type-safe serialization and deserialization for you.
Gleam has some feature to generate the code for you via the LSP, but it's just not good enough IMHO.
Could you point to a solution that provides serde level of convenience?
Why would that be the case? Many models have knowledge cutoffs in this calendar year. Furthermore I’ve found that LLMs are generally pretty good at picking up new (or just obscure) languages as long as you have a few examples. As wide and varied as programming languages are, syntactically and ideologically they can only be so different.
Because LLMs make it that much faster to develop software, any potential advantage you may get from adopting a very niche language is overshadowed by the fact that you can't use it with an LLM. This makes it that much harder for your new language to gain traction. If your new language doesn't gain enough traction, it'll never end up in LLM datasets, so programmers are never going to pick it up.
It’d be like inventing a new assembly language when everyone is writing code in higher level languages that compile to assembly.
I hope it’s not true, but I believe that’s what OP meant and I think the concern is valid!
- Simple semantics (e.g. easy to understand for developers + LLMs, code is "obviously" correct)
- Very strongly typed, so you can model even very complex domains in a way the compiler can verify
- Really good error messages, to make agent loops more productive
- [Maybe] Easily integrates with existing languages, or at least makes it easy to port from existing languages
We may get to a point where humans don't need to look at the code at all, but we aren't there yet, so making the code easy to vet is important. Plus, there's also a few bajillion lines of legacy code that we need to deal with, wouldn't it be cool if you could port (or at least extend it) it into some standardized, performant, LLM-friendly language for future development?
We're still in early days with LLMs! I don't think we're anywhere near the global optimum yet.
Isn't that what WASM is? Or more or less what is going on when people devise a new intermediate representation for a new virtual machine? Creating new assembly languages is a useful thing that people continue to do!
Positive:
- It can be pretty performant if you do it right. For example, with some thought I got many days down to double digit microseconds. That said, you do need to be careful how you write it and many patterns that work well in other languages fall flat in Gleam.
- The language server is incredibly good. It autoformats, autocompletes even with functions from not-yet-imported-but-known-to-the-compiler packages, shows hints with regarding to code style and can autofix many of these, autofills missing patterns in pattern matches, automatically imports new packages when you start using them and much much more. It has definitely redefined my view of what an LSP can do for a language.
- The language is generally a joy to work with. The core team has put a lot of effort into devex and it shows. The pipe operator is nice as always, the type system is no haskell but is expressive enough, and in general it has a lot of well-thought out interactions that you only notice after using it for a while.
Negative:
- The autoformatter can be a bit overly aggressive in rewriting (for example) a single line function call with many arguments to a function call with each argument on a different line. I get that not using "too much" horizontal space is important, but using up all my vertical space instead is not always better.
- The language (on purpose) focuses a lot on simplicity over terseness, but sometimes it gets a little bit much. Having to type `list.map` instead of `map` or `dict.Dict` instead `Dict` a hundred times does add up over the course of a few weeks, and does not really add a lot of extra readability. OTOH, I have also seen people who really really like this part of Gleam so YMMV.
- Sometimes the libraries are a bit lacking. There are no matrix libraries as far as I could find. One memoisation library had a mid-AoC update to fix it after the v1.0 release had broken it but nobody noticed for months. The maintainer did push out a fix within a day of realizing it was broken though. The ones that exist and are maintained are great though.
I do agree the language server is great. And it works in basically any IDE, which is another huge bonus.
With regards to having to type `list.map`, you actually don't need to! You can do this:
import gleam/list.{range, map}
import gleam/int
pub fn main() {
range(0,10) |> map(int.to_string) |> echo
}
Some libraries just aren't there, and I do wonder how hard it would be to port C libraries over. Something I want to play with! case x < 0 {
True -> ...
False ->
case x > 10 {
True -> ...
False ->
case x <= 10 {
True -> ...
False -> ...
}
}
} case x {
n if x < 0 -> ...
n if x > 10 -> ...
n if x <= 10 -> ...
}
Guards are a bit limited in that they cannot contain function calls, but that's a problem of the BEAM and not something Gleam could control.> Guards are a bit limited in that they cannot contain function calls,
I feel like that's not a small sacrifice.
> but that's a problem of the BEAM and not something Gleam could control.
Could Gleam desugar to a case expression like I wrote above?
EDIT: I am wrong. Apparently there are, but it's a bit of a strange thing where they can only be used as clauses in `if` statements, and without doing any calculations.
I did it in F# this year and this was my feeling as well. All of the List.map and Seq.filter would have just been better to be called off of the actual list or Seq. Not having the functions attached to the objects really hurts discoverability too.
However in my experience it's much better than the alternative - e.g. clang-format's default "binpack"ing of arguments (lay them out like prose). That just makes them hard to read and leads to horrible diffs and horrible merge conflicts.
list.map(fn(line) { line |> calculate_instruction })
Could be written list.map(calculate_instruction)
?And I wonder if Gleam + Lustre could become the new Elm.
I have bumped into "the Elm architecture" in other projects though and it was nice.
Just so no one misunderstands this. The creator (Evan) didn't get into, or start, any drama himself that I ever noticed. I'd argue he's a very chill and nice dude.
I've been on the edges of the community for probably a decade now (lurker), and all of the drama came from other people who simply didn't like the BDFL and slow releases strategy.
I can't believe this is still up tbh. And I can't believe there's still people defending Elm's lack of development
> It’s true that there hasn’t been a new release of the Elm compiler for some time. That’s on purpose: it’s essentially feature-complete.
Last talk I saw by Evan Czaplicki (from the 2025 Scala Days conf) he seemed to be working on some sort of database language https://www.youtube.com/watch?v=9OtN4iiFBsQ
What are you lacking in ruby and rails, besides the types?
By the developers own action of adding generics ultimately the golang team admits they were wrong or that generics are better. If gleam gets popular I think much of the same will occur.
There’s simply too much repeated code without generics. I tried writing a parser combinator in gleam and it wasn’t pretty.
Gleam doesn’t support interfaces. Not generics. You are completely right.
Haskell allows both sorts of generics. In Haskell parlance they call this higher-kinded polymorphism and the generic version of map they call fmap (as a method of the class Functor).
Gauche has a generic sequence interface which is great, and it's one of the reasons as a Python user I like Gauche as my "daily driver" Scheme.
It might be the same with gleam, with first version in 2019 and 1.0 in 2024. The language authors might think they are either uneeded and lead to anti patterns, or are waiting to see the best way to implement them.
Which feels super strange, but doesn't seem to really be a problem, e.g. imagine a language where you'd write
fun sum_all_numbers(Iterable<T> it) { it.fold(...) } # an interface
sum_all_numbers(a_list) # a list implements iterable
Gleam wants you to write fun sum_all_numbers(Iterator<T> it) { it.fold(...) } # a concrete type
sum_all_numbers(a_list.iterator) # get the caller to build the right object
edit: found an article that explained this a bit better https://mckayla.blog/posts/all-you-need-is-data-and-function...The advantage rather for llms in strongly typed languages is that compilers can catch errors early and give the model early automated feedback so you don’t have to.
With weakly typed (and typically interpreted) languages they will need to run the code which maybe quite slow to do so or not realistic.
Simply put agentic coding loops prefer stronger static analysis capabilities.
The only problem I’ve ever had was on maybe 3 total occasions it’s added a return statement, I assume because of the syntax similarity with ruby
Contrast with the likes of Swift - been around for years but it’s so bloated and obscure that coding agents (not just humans) have problems using it fully.
And those people are the people that develop the body of material that later people (and now LLMs) learn from.
A language doesn't have to be unique to still have a particular taste associated with its patterns and idioms, and it would unfortunate if LLM influence had the effect of suppressing the ability for that new style to develop.
Both of type classes and interfaces desugar to high order functions, so anything you write with them can be written with first class functions, though with a less concise API.
Of course dynamic dispatch can be implemented in almost every language. The Linux kernel uses dynamic dispatch with C!
But that's a hack, not a language feature.
document.body.style.setProperty('font-variant-ligatures','none','important');