Gleam: a type safe language on the Erlang VM
gleam.run
gleam.run
pub fn replace(
in string: String,
each pattern: String,
with replacement: String,
) {
// The variables `string`, `pattern`, and `replacement` are in scope here
}
replace(in: "A,B,C", each: ",", with: " ")> The pipe operator will first check to see if the left hand value could be used as the first argument to the call, e.g. a |> b(1, 2) would become b(a, 1, 2).
Optimizing for one less _ typed is a really bad trade-off in terms of clarity here.
That allows arbitrary expressions on the RHS of the pipe, so for example this would be valid Gleam:
pub fn main() { io.println( "Hello" |> (_ <> " world") ) }
Applicative builder APIs are the only thing we've found would be much much better if we had auto currying.
Without labels—i.e., with only traditional positional arguments—the example would simply be a function taking three string parameters:
replace(String, String, String)
Of course, the documentation should specify which arguments are which, but still at call sites it would be easy to accidentally permute the arguments: replace("https://[^/]*", "", url) // wrong!
(In this case, the problem is exacerbated since different languages/libraries pick different orders here! For instance, the incorrect ordering above is correct for Python's `re.sub`, so it's easy to see how mistakes might arise.)Labels solve this problem by making the binding explicit at the call site:
replace(each: "https://[^/]*", with: "", in: url) // now correct!
(Looking at the Gleam docs [1], it seems that labeled arguments "can be given in any order".)Now, how does the implementation of this function look? The obvious first approach is to require the labels to be the same as the local variable names, but this often leads to awkward "grammar", because (in the convention of Gleam and some other languages, like those in the Smalltalk/ObjC/Swift heritage) good label names tend to be more like prepositions, whereas variable names like to be nouns:
pub fn replace(in: String, each: String, with: String) {
// variables `in`, `each`, and `with` in scope...
in.split(each).intersperse(with).join() // hmm...
}
Now, of course, the implementation can simply re-bind its arguments to new names: pub fn replace(in: String, each: String, with: String) {
let string = in, pattern = each, replacement = with;
string.split(pattern).intersperse(replacement).join() // better.
}
And labeled arguments are sugar for precisely that: pub fn replace(in string: String, each pattern: String, with replacement: String) {
string.split(pattern).intersperse(replacement).join()
}
[1]: https://gleam.run/book/tour/functions.html#labelled-argument... val p = Pattern("https://[^/]*")
val in = "https://google.com/some/path"
val path = in.replace(p, "") // /some/path
In fact this specific example is solved just by having a dedicated type, for example URI: val path = URI("https://google/com/some/path").path; // /some/path
This feels like to me an answer in search of a problem. pub fn replace(
in string: String,
each pattern: String,
with replacement: String,
) {
// The variables `string`, `pattern`, and `replacement` are in scope here
}
replace(in: "A,B,C", each: ",", with: " ")
vs pub fn replace(
in: String,
each: String,
with: String,
) {
}
replace(in: "A,B,C", each: ",", with: " ")1. Labels are accidental. If all arguments automatically create labels then the programmer has not considered and deliberately designed the API. Gleam is designed to make sure that APIs are always carefully thought about and designed as appropriate.
2. Renaming a variable becomes a breaking change. We don't believe that renaming a variable should ever result in a semver major version bump.
1. Not every code is an API. And APIs exist in multiple languages that don't have labels
2. If you wanted named parameters, you could go the C# way: https://learn.microsoft.com/en-us/dotnet/csharp/programming-...
3. Since Gleam doesn't enforce labels or check labels in any way, it doesn't "make sure APIs are carefully thought out". Labels will be just as random and accidental as parameter names following the whims of the programmer who decides to use them
> Renaming a variable becomes a breaking change.
So does renaming a label.
> So does renaming a label.
In languages without this feature, any renaming breaks the API, unlike the ones with distinct internal and external names. This is not the same.
`replace(string, pattern)` is just as (if not more) readable as `replace(in, each)`.
I was specifically replying to the claim about readability.
These are often very at odds. And if you don't want to repeat yourself twice with two identical names... you don't have to.
And unlike Smalltalk, in Swift at least, externally unnamed but internally named is easy (just make the external name _).
How often is this an issue?
> And unlike Smalltalk, in Swift at least, externally unnamed but internally named is easy (just make the external name _).
So, visual clutter for very little gain. IMO
In our opinion, almost always.
Having shipped code in nearly two dozen different programming languages, there were very few times when I wanted my internal parameter name to be different from my external parameter name. The reasons are very simple:
- if your function is simple, you just use the params immediately, like in your `replace` example. It doesn't matter what they are called as long as their names make sense
- if your function is not simple, the parameters are usually immediately transformed into something else (mapped to different data, split to different data etc.), and their original name doesn't matter.
So this leaves a small weird case of "somehow parameter names don't make sense for the caller of the function", and I can't for the life of me come up with such an example (the example with replace in Gleam docs is not convincing, to say the least).
It is something that you start appreciating a lot more once you have used it significantly. It helps a lot with writing more self-documenting interfaces that are legible at a glance.
Another detail I did not mention is that _public names are part of the interface_. This means that two functions that differ only in the public names are distinct overloads.
To give an extremely simple example (taken from Apple's docs):
class Counter {
var count = 0
func increment() {
count += 1
}
func increment(by amount: Int) {
count += amount
}
...
}This starts becoming even more valuable when you have more complex objects, and allows you to move (e.g. on some kind of repository class) mangling of overload names into more readable named argument overloads.
Things I like about Gleam's Syntax - https://news.ycombinator.com/item?id=38031342 - Oct 2023 (54 comments)
Gleam v0.29 – Gleam gets autocompletion - https://news.ycombinator.com/item?id=36044484 - May 2023 (1 comment)
V0.28 released of Gleam, a type safe Erlang-family language - https://news.ycombinator.com/item?id=35425855 - April 2023 (4 comments)
Gleam v0.25 – Introducing use expressions - https://news.ycombinator.com/item?id=33731871 - Nov 2022 (4 comments)
The Gleam Language Server - https://news.ycombinator.com/item?id=31143792 - April 2022 (1 comment)
Gleam v0.18 Released (gleam language on the erlang vm) - https://news.ycombinator.com/item?id=29464307 - Dec 2021 (1 comment)
Gleam 0.16 compiles to JavaScript - https://news.ycombinator.com/item?id=27538919 - June 2021 (24 comments)
Gleam 0.15 - https://news.ycombinator.com/item?id=27061500 - May 2021 (78 comments)
Phantom Types in Gleam - https://news.ycombinator.com/item?id=26284976 - Feb 2021 (11 comments)
Gleam 0.14 – Type-safe language for the Erlang VM - https://news.ycombinator.com/item?id=26185690 - Feb 2021 (51 comments)
Lean HTTP Server for Gleam - https://news.ycombinator.com/item?id=24265936 - Aug 2020 (5 comments)
V0.10 of Gleam, a statically typed language for the Erlang VM, is out - https://news.ycombinator.com/item?id=23706211 - July 2020 (1 comment)
Gleam: A statically typed language for the Erlang VM - https://news.ycombinator.com/item?id=22902462 - April 2020 (109 comments)
Hello, Gleam - https://news.ycombinator.com/item?id=19686413 - April 2019 (1 comment)
An interview with the creator of Gleam: an ML like language for the Erlang VM - https://news.ycombinator.com/item?id=19547418 - April 2019 (0 comments)
I’d love to hear from anyone running it in production.
I have always been BEAM-curious, but never felt comfortable running it in production, as it feels like a big black box, and I’m not confident that I’d be able to diagnose problems as readily as I can in .NET, Go, or Node. I’m not sure why I feel that way about BEAM.
Yeah you'd need to learn it, but it's like any other tool.
unfamiliarity? Plus the problem of 'nobody ever got fired for running IBM' plaguing your work.
I highly recommend the talk The Soul of Erlang and Elixir by Sasa Juric which shows off the essence of the BEAM.
Uncaught throws, hanging errors - while someone much smarter than I could have done it better, I was doing a lot of async/retry of APIs with high failure rate and the BEAM just made it easy.
I’ve got massive hopes for things like Bun, but especially Deno’s architecture of message passing, but I’m now humbled by my not knowing that existed in V8.
From the whole zoo of system probing stuff or the downright amazing dynamic tracing.
Have a quick look at https://www.erlang-in-anger.com/
If you are writing a distributed system with plenty of microservices BEAM will be magnitudes easier to debug if you write the same system in BEAM native
But don't listen to me - take it from none other than José Valim, Elixir's creator:
> Folks tend to understate the influence of Erlang and overstate the influence of Ruby on Elixir. … Ruby did influence the syntax and the names in the standard library, but the latter was also done in a more "democratic" fashion: I would look into Ruby, JavaScript, Clojure, Haskell, and choose a name that was common and closer reflected the semantics that would fit Elixir (for example, the term "protocol" come from Clojure).
You hit the nail with this one
The ambiguity caused by optional elements is what bothers me most. For this reason Ruby doesn't even have first class function calling syntax, since you are forced to use invoke on block objects.
Me curly brackets, you no curly brackets. Etc.
Today is not that day.
https://blog.lambdaclass.com/an-interview-with-the-creator-o....
This is what I wrote regarding using otp in gleam. One of the issues I'm working on next is to send typed messages to processes, which has me a little stumped.
At the moment the typed channels work, you can't register () a channel or a pid.
I will likely continue working on this document once I have my current project completed.
YMMV, but a BEAM language without OTP severely limits its appeal and usability.
All OTP is usable from Gleam.
So you can use untyped OTP as you would from (say) Erlang?
There are some one-person attempts at compilers from F#/OCaml to BEAM, but they haven’t seen an update for some years.
Supported and battle-tested OCaml or StandardML for BEAM would be great, of course. I'm waiting for something like this for years.
Rust was partially inspired by F# and OCaml and actually had a much more ML-like syntax early on. Probably something similar goes for Typescript. And actually, the same for Gleam as well.
F# doesn't really relate to Haskell at all. They are very different languages. F# is a multiparadigm language that easily supports concise code in the imperative, OOP, and functional styles.
It sounds like you maybe don't know much about F# if you think it's like Haskell, which is okay. But that's also why I would recommend checking it out.
Love Rust and everything about it.
However, to script something small/casual I would like all the rest of Rust but with automatic garbage collection, without having to worry about lifetimes.
I don't see how working with types in scripts would be helpful. To me this looks like a bad idea. Everything being a string is a very valuable feature of Unix Shell. Once you have to spell out types interactively your life becomes a misery. I had similar experience having to do some testing for a mostly Linux-based product on MS Windows (an iSCSI portal that I needed to check if the MS side can connect to).
Using PowerShell is like talking to a very pedantic and a very stupid army bureaucrat, where each time you speak you have to define every word you use afresh, declare the titles of all people involved in a conversation up-front and in the end, all while being unable to connect two obviously related pieces of information because the said bureaucrat has no folder that can contain both.
So, IMO, Erlang or any other Beam language would be awesome for infra automation, and it only gets better the bigger is the organization that infra is supporting. I'm not sure there's any benefit to having ML-style explicit typing in a Beam language though. To explain this better: in the times Flash was relevant, AVM -- Flash' virtual machine was written in such a way that it had to perform runtime type checks almost every other instruction. If you could prove types statically (which Haxe did), then you could generate much tighter bytecode which lead both to smaller memory footprint and somewhat faster execution.
I don't think Beam works in the same way. So, being able to statically prove something about your code doesn't give you the same kind of benefits. And the remaining benefits could be eclipsed by the effort necessary to support the code containing type assertions (i.e. the extra testing that you need to do to assure the type assertions are correct, the increased refactoring cost and valid functionality prevented by lack of insight on the part of the programmer writing type assertions might not be worth the correctness guarantees given by these assertions).
I'm sure some people might read all this and conclude that they might as well just use OCaml or Haskell, and those are really good languages for compilers too! The tooling and ecosystem make Rust more appealing to me personally though, and I suspect that there are probably others who would find Rust's ecosystem a bit more approachable as well.
That and Rust's iterators are terrible at introducing ownership agony.
In any case, I've ... done it (https://github.com/rdaum/moor/blob/main/crates/compiler/src/...) but can't say I liked it.
I do really like "pest" as a parser generator though. In general, yes, the Rust ecosystem is bigger/richer, and growing.
And yet... The Rust ecosystem win. Easily. For a simple reason.
Salsa. Oh and also clap. Lsp bindings. Ungrammar. Miette. Clap. Parsers. Etc
At every level the Rust packages are far better and allow to drastically reduce the cost of building this stuff.
They are nowhere as easy. At the very least because their documentation is usually non existent or non comprehensible.
Rust’s frontend now has support for running in parallel on nightly (they haven’t announced on the blog yet). But your point stands, Rust got pretty far all these years while keeping things simple and single threaded.
There are a lot of new languages coming out these days, which I find interesting and positive. It gives hope for evolution and better solutions to come along.
A problem is that on the surface they have similar syntax and keywords.
I can get confused as to what language I am programming in since some syntax nearly transfers between more than one programming language.
There are of course differences, and those differences can be profound which is what gives us a richer eco system but my brain gets confused by it.
Processes are central to how Beam works, and you would have to incorporate this notion into design and execution of your bindings when interfacing with Beam from native code. I.e. you'd have to consider that the scheduler needs to be able to suspend / resume your function, think about how work can be done in small time slices, preferably w/o blocking and w/o starting own processes or threads.
If you are interested in practical guidance, search for Erlang port drivers and NIFs. Often times when interfacing with eg. Python from native code, you'd run into marshaling problem. I.e. when you need to pass large and/or structured data to the native code, you'd have to do a lot of packing and unpacking of Python objects before you get something usable out of them in the native land, similarly, the other way around.
Erlang's ports are designed with some idea about how to deal with this problem. NIFs are intended for simpler things, where continued / stateful communication between two worlds isn't necessary.
I'd like to see Gleam add support for structural types and maybe set theoretic [1]
The docs are a little sparse on this (being initially focused on BEAM), but the discord & community is very helpful which makes up for it.
It's a very neat and well thought out language.
I believe Gleam offers deeper pattern-matching facilities. That is of course not the type system, but feels closely related to me.
There was really no reason to do so at all - it's an ordinary company building an ordinary application. But some CTO or dev lead years ago thought it important to use whatever the fancy tech of the day was to build a fairly ordinary business application.
But now they have trouble hiring people because there's a tiny pool of people who know that language. And it is years down the track and all their technology is built with whatever that language/framework is - too expensive to easily bail out, but probably necessary anyway.
In 2023 you've got to have really good reasons for not using C#, or Java, or Python, or TypeScript for building your web project. Maybe Golang - it's getting some good traction in the DevOps space.
If you make decisions about technologies and this latest thing appeals to you - maybe use it for your personal projects. The CEO has employed you to make responsible decisions not play toys.
Well, yeah that's really your only choice if the experience you need is not easily available.
Don't tell me, you are the guy who puts in the not mainstream technology and needs to defend that with justifications such as "when we use (non mainstream technology X) we get super motivated job applicants because anyone with an interest in non mainstream technologies is inherently passionate and interested".
That's not a sound hiring strategy.
These inconvenient counterexamples blow up the entire thesis. In practice, the "safe choice" for longevity is only evident in hindsight.
If anyone is hiring for TypeScript in 2050 I'll eat my hat
There are interesting discussions about Typescript becoming more of a run-time type checker, which would be opt-in and have significant performance penalties, but would give more guarantees of type safety.
Loop variable pointer and nil struct is not nil interface are the two that continue to hound us.
type MyError {}
var _ error = (&MyError)(nil)
errors.As(err, &MyError{})
---
Panicking because it needs to be &&MyError{} instead is a similar gotcha.
However, I don't think raw "intelligence" is the right metric for hiring. Unless you specifically mean emotional intelligence, which can curtainly contribute to improving communication skills on the team.
I've also found that when hiring someone with mostly expertise outside your current stack it's critical to provide good feedback loops and mentoring early on so they can "get up to speed" and contributing valuable, idiomatic code as fast as possible without feeling as though your entire onboarding experience is trial by fire. This mentoring takes up extra resources and is a net drain on your team in the short term, but pays off in the long term.
In time, it starts to become clear that this non mainstream thing hasn't really picked up the steam to become mainstream. Your developers are starting to realise there are no jobs advertised for Nim, so this isn't good for their career. Then the Nim project starts to stagnate. But you've written so much code in it that it's hard to turn back. But the person who decided on the non-mainstream technology - if they are still there they are defending it to avoid losing face. if they have moved on and left you the problem then the new CTO is explaining to the CEO the bind the company is in. Time to start planning the budget for a complete rewrite in the language that is standard practice for the industry for this sort of thing.
At this point, C++ ain't looking so bad for this sort of project.
I live in a very different world and over the past 13 years have been paid to write Obj. C, Python, PHP, CSS, JS, Ruby, Elixir, Golang and a small amount of Rust. All are still useful skills that I’m likely to encounter again.
In a few cases, I used languages that later lost steam (Flash, CoffeeScript and Elm). It wasn’t a disaster, though. I just took what I’d learned and moved on.
These types of problems are pretty common.
The point of Nim is that we’re exercising all of their C and C++ skills as one of its venefits is being able to leverage existing drivers and modules for peripherals. Nim is C, at the end of the day.
I’m not some fresh faced 20 something, I’ve got nearly two decades in industry at this point, and I just frankly disagree with your assessment.
Please - I can do a ton of stuff in Elixir without having to reach for some weird/paid third party service. https://twitter.com/ryanrwinchester/status/15806523244057763...
Now with Liveview I can even ditch the grotesque React toolchain entirely.
Also, in my experience as defacto CTO of a unicorn YC company (Papa), hiring was not insanely difficult. I hired about ~45 engineers Great Ruby/Go devs translated pretty easily into Elixir after minimal training. The language is beautiful and simple.
As long as you have one experienced elixir engineer to guide the people learning I think it’s easy to move fast.
As usual the problem is that leadership has no fucking clue how to hire.
Sure, in a downturn after a year of waves of layoffs, and maybe in San Francisco, but the non-mainstream tech will live longer than a few economic employment ups and downs.
I’ll also add that over many years of doing this, the elixir devs I’ve interviewed tend to be far above average, which makes up for the lower number of candidates.
If you want to hire thousands of fresh grads each year and don’t want to spend any time on training, sure go for something they teach in school.
(ironically, the erlang solution never went anywhere because management decided they did not want to take a chance on a lesser-used language. but from a getting employees standpoint it's perfectly feasible to hire people who are simply good engineers in whatever language and give them a month to learn erlang.)
While I was there, I was the third most Erlang knowledgeable server hire, because I rememebered Ericsson open sourcing it decades ago, but hadn't used it before. We hired two people I can recall who were experienced with Erlang (well, and a client developer who used Erlang in school but didn't use it for WhatsApp).
This wasn't a big deal, Erlang is a small language, and smart developers pick up enough to be useful pretty quickly. There's a learning curve for distributed systems challenges, but IMHO, message passing concurrency makes a lot of things a lot simpler than shared memory concurrency, and you can get a lot of good work done where the new person does the bulk of the work and a mentor helps with distributed systems bits.
Anyway, Facebook chat decided to abandon Erlang for hiring reasons, and transitioned to a C++ server, and I don't think it was that hard; Erlang wasn't some boat anchor holding them back when they decided to switch, like the GP suggested. Even if it was, that's kind of an amusing complaint: this thing is so awful, and we can't even replace it because ??? it's too good?
Obviously they were suggesting that rewrites/changing your tech stack for a project is expensive and not everyone has the luxury to do it. Facebook does.
>In 2023 you've got to have really good reasons for not using C#, or Java, or Python, or TypeScript for building your web project. Maybe Golang
In my country, very few people know Go, so we hire devs without requiring them to know Go - we teach them the language, and after a few weeks, they already feel pretty confident with it. In fact, most devs are actually interested in trying something new, some new language.
Maybe your employer's problem is that they are only looking for CVs where the language is mentioned as a keyword, and don't consider anything else?
As for using a unpopular language. My (mostly untested) conviction is that if this unpopular language is still popular among an albeit small group of programmers, it's a blessing. While hiring is difficult, you get much higher quality human resources. You get to enjoy working with smart people.
I've known a company whose business was to track real estate values. They had a Web site and some backend that did somewhat complicated statistics trying to predict prices or rent based on all kinds of factors they aggregated. A lot of the functionality of the backend was in scraping local news, city authority documents etc.
They wrote everything in Clojure (including front-end, which was Clojure-script). Great people, created a great, useful and successful business.
I've worked in companies early adopters of Go and Rust. It was a great experience. I've learned a lot during that time from my coworkers. I also briefly worked for a company whose main product was written in D. It was another great place to work in terms of quality of human resources and the quality of code I had to work with.
Except that if you don't agree with the authors political opinions:
> Black lives matter. Trans rights are human rights. No nazi bullsh*t.
Yet another project run by people who shoves politics into unnecessary spaces. I would never dare to use a project run by people that pushes their personal agendas on their website for a programming language.
I do believe that there is only two genders, man and female. Am I a nazi now or what do Gleam mean by this message? Don't use projects like this.
I don't really care that they have this message (except that I find it to be pretty insulting), I just think it's an unnecessary thing to do if you want people to use what you've made. I won't and I will encourage people not to use it just because of this.
Sounds like you care a lot if you're gonna actively discourage people from interacting with the project.
If someone would ask me in the future if I heard anything about Gleam, yes then I would actively discourage people from interacting with the project. If I read about it in a positive manner in the future here or in other forums, yes then I would probably write a comment warning people about it.
If you think that is caring a lot then I care a lot. I don't consider that to be caring a lot though.
If I say I'm interested in using Gleam and you tell me there's only two genders, I do not see how that makes you any better.
The discussion is about everything about the language not just the technical aspects so no I am not.
> The real issue is not that someone is putting politics in a technical place, it's that it contradicts yours.
No I wouldn't care whatever political agendas the author hadn't he put it straight on the website calling me a nazi.
> If I say I'm interested in using Gleam and you tell me there's only two genders, I do not see how that makes you any better.
Why would you assume I would say anything about genders if you asked me about gleam?
I would say it's run by political extremists that use their programming language to push political agendas and that due to that, they are probably untrustworthy. I would say I would refuse to use it because of that but encourage you to make up your own mind.
Because nobody asked you about Gleam and you still brought up gender. And then you said you would continue to bring up politics whenever the language came up.
> I would say it's run by political extremists that use their programming language to push political agendas
I find it comical you think a programming language can be used to push a political agenda. That's got to be the least effective way to spread political ideology I've ever heard. You make it sound so sinister.
> No I wouldn't care whatever political agendas the author hadn't he put it straight on the website calling me a nazi.
Nobody called you a Nazi.
No I read on the website hacker news, someone submitted a link to the language website. The website brought up gender and centered in in the page about community guidelines.
> I find it comical you think a programming language can be used to push a political agenda. That's got to be the least effective way to spread political ideology I've ever heard. You make it sound so sinister.
Well it is because it is sinister imo. Maybe it's ineffective but it's still there which is kind of undeniable. A lot of technical projects have had political agendas on their websites. React, Go, Node.js, Emberjs etc etc. I kind of get it and support the anti-war stuff for Ukraine but for normal everyday politics that's mainly american? C'mon.
> Nobody called you a Nazi.
Yes the website kind of did, since I don't adhere to the ideology that they do.
Stop being so god damn naive dude, it's ridiculous.
Right, so it doesn't align with your politics.
> Yes the website kind of did, since I don't adhere to the ideology that they do
This is a pretty conservative website and even most people disagree with you here, which is why your comment about "these people" was removed.
> Stop being so god damn naive dude, it's ridiculous.
I'm just gonna end it here since you're clearly looking for reasons to get offended.
There is a dot there, so they're separate issues.