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.
In fact, I'd say most of the Gleam code that has been generated has been surprisingly reliable and easy to reason about. I suspect this has to do with the static typing, incredible language tooling, and small surface area of the language.
I literally just copy the docs from https://tour.gleam.run/everything/ into a local MD file and let it run. Packages are also well documented, and Claude has had no issue looping with tests/type checking.
In the past month I've built the following, all primarily with Claude writing the Gleam parts:
- A websocket-first analytics/feature flag platform (Gleam as the backend): https://github.com/devdumpling/beacon
- A realtime holiday celebration app for my team where Gleam manages presence, cursor state, emojis, and guestbook writes (still rough): https://github.com/devdumpling/snowglobe
- A private autobattler game backend built for the web
While it's obviously not as well-trodden as building in typescript or Go or Rust, I've been really happy with the results as someone not super familiar with the BEAM/Erlang.
EDIT: Sorry I don't have demos up for these yet. Wasn't really ready to share them but felt relevant to this thread.
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!
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.
Elixir is slowly rolling out set-theoretic typing: https://hexdocs.pm/elixir/main/gradual-set-theoretic-types.h...
Why use something complex and half working, when you can have the real thing?
Gleam can call any erlang function, and can somewhat handle the idc types. [ im sure it has another name ].
Did i miss something that gleam fails on, because this is one of my concerns.
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.Was this the time of everything or just the time of your code after loading in the text file etc.? The hello world starter takes around 110 ms to run on my PC via the script generated with `gleam export erlang-shipment` and 190 ms with `gleam run`. Is there a way to make this faster, or is the startup time an inherent limitation of Gleam/the BEAM VM?
As to whether you can make startup time faster, I suppose you could keep a BEAM running at all times and have your CLI tools hotswap in some code, run it, and get the results back out or something. That way you can skip VM startup time. Since the BEAM is targeted more at very long-running (server) processes with heaps and heaps of concurrency, I don't think ultrafast startup time is really a focus of it.
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.
To me it felt less elegant than Scheme (GNU Guile) which I usually use (with nice parallelism if I want to, pipelines, and also pattern matching), and, aside from syntax, conceptually perhaps less elegant than Erlang. On the other hand it has static typing.
I also tried OCaml this year, but there are issues in the ecosystem making a truly reproducible environment/setup, because opam doesn't produce proper lock files (only version numbers) and it seemed silly to not be able to even include another file, without reaching for dune, or having to specify every single file/module on command line for the OCaml compiler. So I was left unsatisfied, even though the language is elegant and I like its ML-ness. I wish there was a large ecosystem around SML, but oh well ...
Might be I should take another look at Erlang soon, or just finally get started with Haskell. Erlang using rebar3 should have proper lock files, has pattern matching, and if I remember correctly no such limitations for calling functions recursively. No longer sure how or whether Erlang did inner functions though.
I like Haskell in theory, but: just to get a hello world takes a lot of CPU and disk space. The standard library is full of exceptions (you can use a different prelude, that opens a whole different can of worms). The ergonomics of converting between the thousand different string types are awful.
So, you being basically me, I have some recommendations:
Idris (2): good stdlib, has dependent types. A beautiful language. The compiler is self-hosted and bootstrapped by lisp - very elegant! The ecosystem is basically nonexistent though.
PureScript: also improves on Haskell in many ways. But, it's more of a frontend language, and though you can do backend, you're stuck with JavaScript runtime. Oh well.
By the way, the number of partial functions is base that throw compiler warnings is increasing, for example:
https://hackage.haskell.org/package/base-4.21.0.0/docs/Prelu...
I hope it will increase further.
array
.slice(0, 10)
.filter(s => s[0].toLowerCase() < 'm') // a<->l
.map(s => s.toUpperCase());
It seems like it should be a common feature to be able to view between each array operation in a debugger without having to augment the code with `echo`The out of bounds handling didn't seem all that good to me. Sure you can filter out undefined. You could also just make a function that returns an empty array if out of bounds, or array of 1 element if not.
// JS
function getElemFromGrid(grid, x, y) {
return (x < 0 || x >= grid.width ||
y < 0 || y >= grid.height)
? []
: [grid.elems[y][x]]
}
...
neighbors = [
...getElemFromGrid(grid, x + 1, y + 0),
...getElemFromGrid(grid, x + 1, y + 1),
...getElemFromGrid(grid, x + 0, y + 1),
...getElemFromGrid(grid, x - 1, y + 1),
...getElemFromGrid(grid, x - 1, y + 0),
...getElemFromGrid(grid, x - 1, y - 1),
...getElemFromGrid(grid, x + 0, y - 1),
...getElemFromGrid(grid, x + 1, y - 1),
]
I also find grids made of 2 dimensional array to be code small. An array of arrays is NOT A GRID as there is nothing enforcing the inner arrays to be the same length as each other. Also, in efficienct code it would be better to provide a 1 dimensional array and bounds. Now, out of bounds checks based on accessing outside the array won't work. You need to check actual bounds. Again giving preference using a helperThe more programming languages we play with, the better we become as engineers. We learn the different design decisions that go into each language. And we learn the language of Computer Science itself.
Plus, Advent of Code!
So I finally got everything installed. But then I realized there are no easily accessible offline docs. I don't have Internet service at home. So I have to grab service when I can make it out.
So it looks like mix can download offline hexdocs, but I don't have elixir installed. And there's a hexdocs_offline dev package for Gleam. But it errors out on "gleam/dynamic does not have a 'from' value.
Maybe it's a "teaching moment" about the basics of the language. But I have to run out to another appointment now, so these teaching moments don't always help.
Anyways, I guess I'll dive in after the holidays and just wget the tour or get some well regarded projects for reference. I'm actually still really excited with all the functional features.
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.
Right now I’m toying with the idea of building a GNOME application in rust, and the framework I’m using is relm4 which provides elm like abstractions over gtk-rs.
Previously I’ve built web applications with F# and elmish, which again provides elm like abstractions for building F# applications.
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'm a backend dev mostly and use Elm for all my frontend needs. Yes there are some things compiler-side that could be improved, but basically it's fine.
I appreciate not having to keep up with new releases!
What are you lacking in ruby and rails, besides the types?
Seems to be a filesystem, how would it replace a database?
I'm hoping it succeeds and gets bigger because I really like its ergonomics.
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.
I have similar concerns to you - how well a language works with LLMs is indeed an issue we have to consider. But why do you assume that it's the volume of training data that drives this advantage? Another assumption, equally if not more valid IMO, is that languages which have fewer, well-defined, simpler constructs are easier for LLMs to generate.
Languages with sprawling complexity, where edge cases dominate dev time, all but require PBs of training data to be feasible.
Languages that are simple (objectively), with a solid unwavering mental model, can match LLMs strengths - and completely leap-frog the competition in accurate code gen.
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.
it seems semi intuitive to me that a typesafe, functional programming language with referential transparency would be ideal if you could decompose a program to small components and code those.
once you have a referentially transparent function with input/out tests you can spin on that forever until its solved and then be sure that it works.
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.
But I think two things really hold it back:
* it's verbose.
* they compose awkwardly.
Neither of these are showstoppers, but I think fixing these problems--maybe with something like syntax-level support--could really lead to a beautiful programming language.
> But you cannot do [first, ..middle, last].
I don't think you're supposed to do that! It's probably expensive!
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...