> I won’t fall into the trap of trying to define Monads in this post. Instead, let’s talk about monadic-style APIs – that is, APIs that allow you to do a bunch of things one after another, with the ability to use the result of a previous computation in the next computation, and also allows some logic to happen between steps.
Am I crazy, or did he just give a really good definition of monads in programming? I think that it benefits by not letting itself get bogged down in Category Theory nomenclature which doesn't actually matter when programming.
He described a problem people use monads to solve, not monads themselves.
Haskell people do talk about monadic vs. applicative combinators that are different by whether you can use the results of a previous step on the next ones. But that doesn't have a direct relation with the actual definition of those.
But yes, if you are teaching a programming language that uses monads to someone, you will probably want to explain the problem they solve, not the actual structures. As most things in math, the structures become obvious once you understand the problem.
It's a good description of one application of monads, which is often helpful to beginners if they have been thrown into real code without yet understanding the "why" of monads. If you look up "railway-oriented programming," you'll find more presentations of it.
I think it is a very practical place to start, especially for programmers who have been thrown into a codebase while still new with monads, because it helps them avoid a common mistake that plagues beginners: accidentally dropping error values on the non-success track. Often you simply want to drop values on the non-success track, and there are convenient idioms for doing so, but just as often, you need to examine those values so you can report failures, by returning metrics on validation failures, by providing the right status code in an HTTP response, etc. Railway-oriented programming is a vivid metaphor that reminds programmers that they need to make a decision about how to handle values on the other track.
Not really. The big important part of monads is flattening/unnesting the output.
Basically, if you can convert a `Foo<T>` into a `Foo<U>` by applying a function `T -> U`, it's a monoid. Think `map` or `fold`.
But if you can convert a `Foo<T>` into a `Foo<U>` by applying a function `T -> Foo<U>`, it's a monad. Flattening is "some logic", but not any logic, it's inherent to `Foo<>` itself.
The greatest power of BEAM-based languages is the fully preemptive actor model. Nobody else supports it. This is a superpower, the solution of most problems with concurrent programming.
In Erland and Elixir, actors and actor-based concurrency hold the central place in the corresponding ecosystems, well supported by extensive documentation.
In Gleam, actors and OTP are an afterthought. They are there somewhere, but underdocumented and abandoned.
This is exactly what I want from Gleam. It does seem to be under documented and abandoned. Is there any understanding of why? Like you say, this seems like a super power. I see so much potential. A language that’s ergonomic, pragmatic as the author says, great performance, low-ish barrier to entry, etc. It seems like it could be an awesome tool for building highly reliable software that’s not so difficult to maintain.
Are there any articles that do a deeper dive into this? I ask because straight up I've been curious about Gleam, but not enough to do a really deep dive because Elixir is too good and, like Erlang, is a very special kind of dynamic language that doesn't leave me feel too lacking.
As I understand it, there have been a few "high profile" attempts to bring static typing to Erlang, all of which gave up when it came to typing messages. Your comment essentially confirms my bias, but is Gleam making real strides in solving this, or is it poised to merely cater to those who demand static-typing with curly braces--everything-else-be-dammed?
I understand things best by comparing across different languages so don’t take this the wrong way but I wonder if you can help me understand: If say I start a goroutine in Go and give it a channel to use as a mailbox, concurrency in Go is cooperative but it’ll automatically use OS threads and yield whenever it reads from the channel. Does Erlang/OTP do something different? If so what does it do and what are the advantages? Or is it more that the library and ecosystem are built around this model?
Gleam's 1.0 release was in May and it's still adding major features.
JavaScript support looks interesting. Browsing the package repo, I don't see how to tell which packages are supported on Erlang's VM, when compiling to JavaScript, or both. JavaScript-specific documentation seems pretty thin so far?
You're right about the lack of FFI-specific docs, but Gleam is such a simple language that it's very workable.
I wrote Vleam[0], which allows writing Gleam inside Vue SFCs, and the experience was pretty good even without the docs.
You do have to sometime read the source of other Gleam packages to understand how things work, but again -- Gleam is so simple it's not too bad of an experience.
This is a very concise overview! I have made a small example chat app [1] to explore two interesting aspects of gleam: BEAM OTP and compilation to javascript (typescript actually). If anyone is interested...
The `use` syntax is interesting - don't recall seeing anything similar before. But I'm struggling to understand how exactly it is executed and a glance at the Gleam docs didn't help.
Is the `use` statement blocking (in which case it doesn't seem that useful)? Or does it return immediately and then await at the point of use of the value it binds?
Everything after the line containing '<-' happens in a callback.
Since it's a callback, I assume it's up to the function whether to call it, when to call it, and how many times to call it, so this can implement control statements.
I would guess that it also allows it to be async (when the callback isn't called until after an I/O operation).
I converted the example on the Gleam home page [0] to F#:
let spawn_task i =
async {
let n = string i
printfn $"Hello from {n}"
}
// Run loads of threads, no problem
seq { 0..200_000 }
|> Seq.map spawn_task
|> Async.Parallel
|> Async.RunSynchronously
|> ignore
The two are pretty similar, but I would give F# the nod on this one example because it doesn't actually have to create a list of 200,000 elements, doesn't require an explicit "main" function, and requires fewer brackets/parens.
Wow, this is a great overview. I’ve been playing with Gleam a bit and this was really helpful. I’ll definitely refer to this later.
I’d like to dig into the OTP library (I’m curious if anyone has worked with it much?) and create a state chart library with it, but I’m still firmly in the “I don’t totally get it” camp with a few parts of Gleam. I don’t deny that it’s pragmatic. Maybe it’s more so that I’m not up to speed on functional patterns in general. I was for years, but took a hiatus to write code for a game engine and supporting infrastructure. It was so Wild West, but I kind of liked it in the end. Lots of impure, imperative code, haha.
I've tried to get my head around functional programming and also OTP but I also just never got my head around it.
Functional programming seems too limiting and OTP seems more complicated than I would have hoped for a supposedly distributed concurrency system.
I'm sure it's just a skill issue on my part. Right now I'm way too rust-brained. I've heard lots of things about gleam being good for productivity but I don't feel unproductive writing web apps in Rust but I felt every unproductive trying to write a non-trivial web app in gleam
Isn’t manual ser/de pretty common? I like it personally. Being explicit at program boundaries usually means far fewer bugs inside the program. In JS I can pile whatever JSON I want into an object, but eventually I need to throw Zod or something at it to tame the crazy.
Maybe a generic “pile this data into this value and pretend it’s safe” tool might be nice for prototyping.
I agree that the stdlib decoder functions aren't the most ergonomic, but I think people are aware it's a pain point and there is development in that are, these two packages for example:
This is the biggest reason I cooled a bit on Gleam and whenever I want to do some backend stuff I'd much rather use Rust (using serde to convert to structs) or Elixir (put it in dynamic maps).
I wish Gleam would implement some kind of macro system, making a serde-like package possible.
I understand why the `use` syntax is preferable for its generalizability to many different "callback style" things, but the whole construct of `use foo <- result.try(bar())` is so much worse than defining let* in ocaml and being able to write `let* foo = bar() in`...
> Running on the battle-tested Erlang virtual machine that powers planet-scale systems such as WhatsApp and Ericsson, Gleam is ready for workloads of any size.
Does a Gleam programmer in practice need to deal with Erlang? Do Erlang error messages leak through?
Pure Gleam will get you really far without having to touch any Erlang, I've done Gleam for almost a year now and there were very little cases where I needed to write Erlang code myself, usually there's already a library that deals with it for most common needs :)
Is there a way to implement matrix arithmetic with nice syntax (for instance, "A + B" to add two matrices A and B) in Gleam? The lack of ad-hoc polymorphism might paradoxically be a blessing.
It has some syntax similarities to Rust, but it has GC so there's no borrow checker (or any of the associated syntax). It is also fully immutable, unlike Rust. It leans heavily on sum types, just like Rust. Also expression-based syntax and some other things resemble Rust. However, it lacks Traits. Overall it looks Rust-ish but it's much simpler and has a functional focus.
With Go it shares a lazer focus on simplicity and preemptive channel-based concurrency. But of course for all the above reasons listed above it looks very different from Go in most other ways.
In many way its language choices are the opposite of Python (static types, immutability, massive concurrency is the norm).
Does the C# runtime or language or so have any of the abilities that a BeamVM language can make use of? Afaik Gleam can do the same things you can do with Erlang, easily making a cluster of machines and easily running code on any of the machines in the cluster, load balanced, pattern matching on binary ...
Otherwise I don't understand what C# has to do with Gleam.
I guess I don't entirely agree, but I do wonder why each import has to include 'gleam' in the path. Why can't it assume that the default path is 'gleam' and import libraries relative to that path. Like `import string` instead of having to do `import gleam/string`?
> I won’t fall into the trap of trying to define Monads in this post. Instead, let’s talk about monadic-style APIs – that is, APIs that allow you to do a bunch of things one after another, with the ability to use the result of a previous computation in the next computation, and also allows some logic to happen between steps.
Am I crazy, or did he just give a really good definition of monads in programming? I think that it benefits by not letting itself get bogged down in Category Theory nomenclature which doesn't actually matter when programming.
He described a problem people use monads to solve, not monads themselves.
Haskell people do talk about monadic vs. applicative combinators that are different by whether you can use the results of a previous step on the next ones. But that doesn't have a direct relation with the actual definition of those.
But yes, if you are teaching a programming language that uses monads to someone, you will probably want to explain the problem they solve, not the actual structures. As most things in math, the structures become obvious once you understand the problem.
It's a good description of one application of monads, which is often helpful to beginners if they have been thrown into real code without yet understanding the "why" of monads. If you look up "railway-oriented programming," you'll find more presentations of it.
I think it is a very practical place to start, especially for programmers who have been thrown into a codebase while still new with monads, because it helps them avoid a common mistake that plagues beginners: accidentally dropping error values on the non-success track. Often you simply want to drop values on the non-success track, and there are convenient idioms for doing so, but just as often, you need to examine those values so you can report failures, by returning metrics on validation failures, by providing the right status code in an HTTP response, etc. Railway-oriented programming is a vivid metaphor that reminds programmers that they need to make a decision about how to handle values on the other track.
No, this isn’t a good description of monads. It merely describes a case that shows up sometimes.
A monad is just a monoid in the category of endofunctors, what's the problem?
Yin the OOP world I’ve seen this pattern called chaining : usually either method or object chaining.
Not really. The big important part of monads is flattening/unnesting the output.
Basically, if you can convert a `Foo<T>` into a `Foo<U>` by applying a function `T -> U`, it's a monoid. Think `map` or `fold`.
But if you can convert a `Foo<T>` into a `Foo<U>` by applying a function `T -> Foo<U>`, it's a monad. Flattening is "some logic", but not any logic, it's inherent to `Foo<>` itself.
It's a good spit, some people used to describe them as "programmable semi colon" but while it's simple, it may be too short for most people to grasp.
I think you just fell into the trap.
The greatest power of BEAM-based languages is the fully preemptive actor model. Nobody else supports it. This is a superpower, the solution of most problems with concurrent programming.
In Erland and Elixir, actors and actor-based concurrency hold the central place in the corresponding ecosystems, well supported by extensive documentation.
In Gleam, actors and OTP are an afterthought. They are there somewhere, but underdocumented and abandoned.
This is exactly what I want from Gleam. It does seem to be under documented and abandoned. Is there any understanding of why? Like you say, this seems like a super power. I see so much potential. A language that’s ergonomic, pragmatic as the author says, great performance, low-ish barrier to entry, etc. It seems like it could be an awesome tool for building highly reliable software that’s not so difficult to maintain.
Are there any articles that do a deeper dive into this? I ask because straight up I've been curious about Gleam, but not enough to do a really deep dive because Elixir is too good and, like Erlang, is a very special kind of dynamic language that doesn't leave me feel too lacking.
As I understand it, there have been a few "high profile" attempts to bring static typing to Erlang, all of which gave up when it came to typing messages. Your comment essentially confirms my bias, but is Gleam making real strides in solving this, or is it poised to merely cater to those who demand static-typing with curly braces--everything-else-be-dammed?
this is Gleam OTP package https://github.com/gleam-lang/otp
I agree it's underdocumented but doesn't seem abandoned (has commits in last week)
Hello! I’m the maintainer of the Gleam OTP library. It is not abandoned or an afterthought.
I understand things best by comparing across different languages so don’t take this the wrong way but I wonder if you can help me understand: If say I start a goroutine in Go and give it a channel to use as a mailbox, concurrency in Go is cooperative but it’ll automatically use OS threads and yield whenever it reads from the channel. Does Erlang/OTP do something different? If so what does it do and what are the advantages? Or is it more that the library and ecosystem are built around this model?
Gleam runs on the BEAM
Gleam's 1.0 release was in May and it's still adding major features.
JavaScript support looks interesting. Browsing the package repo, I don't see how to tell which packages are supported on Erlang's VM, when compiling to JavaScript, or both. JavaScript-specific documentation seems pretty thin so far?
You're right about the lack of FFI-specific docs, but Gleam is such a simple language that it's very workable.
I wrote Vleam[0], which allows writing Gleam inside Vue SFCs, and the experience was pretty good even without the docs.
You do have to sometime read the source of other Gleam packages to understand how things work, but again -- Gleam is so simple it's not too bad of an experience.
[0]: https://github.com/vleam/vleam
Most of the work for this has been done, the main missing piece is surfacing it in the UI, which someone will hopefully pick up soon.
This is a very concise overview! I have made a small example chat app [1] to explore two interesting aspects of gleam: BEAM OTP and compilation to javascript (typescript actually). If anyone is interested...
[1]: https://github.com/patte/gleam-playground
The `use` syntax is interesting - don't recall seeing anything similar before. But I'm struggling to understand how exactly it is executed and a glance at the Gleam docs didn't help.
Is the `use` statement blocking (in which case it doesn't seem that useful)? Or does it return immediately and then await at the point of use of the value it binds?
It is syntax sugar for CPS [1].
[1]: https://en.wikipedia.org/wiki/Continuation-passing_style
EDIT: I believe prior art is Koka's with statement: https://koka-lang.github.io/koka/doc/book.html#sec-with
The equivalent in F# is let! (F# computation expressions are quite powerful); in rust the ? operator. Other languages have similar features.
It's syntactic sugar, but the readability is worth it
You can do something similar in OCaml (as an operator defined at the library level, not a specialized new syntax): https://github.com/yawaramin/letops/blob/6954adb65f115659740...
There's a great article by Erika on use, definitely recommended :) https://erikarow.land/notes/using-use-gleam
I think it's similar to koka's 'with'.
https://koka-lang.github.io/koka/doc/book.html#sec-with
Everything after the line containing '<-' happens in a callback.
Since it's a callback, I assume it's up to the function whether to call it, when to call it, and how many times to call it, so this can implement control statements.
I would guess that it also allows it to be async (when the callback isn't called until after an I/O operation).
It really reminds me of LiveScript's "back-calls" [1], which were a solution for callback hell in JS.
1: https://livescript.net/#:~:text=Backcalls%20are%20very%20use...
Gleam looks nice but if an F# comparisons was added, I think that would come out ahead based on the authors priorities.
One thing I dislike with erlang based languages (both gleam and elixir) is that they use “<>” for string concatenation.
In F#, “<>” is the equivalent of “!=“. Postgres also uses <> for inequality so my queries and f# code have that consistency.
The author links to a blog post talking about railway oriented programming in f#.. it might be fair to assume they are aware of f#
I converted the example on the Gleam home page [0] to F#:
The two are pretty similar, but I would give F# the nod on this one example because it doesn't actually have to create a list of 200,000 elements, doesn't require an explicit "main" function, and requires fewer brackets/parens.[0]: https://gleam.run/
[flagged]
Wow, this is a great overview. I’ve been playing with Gleam a bit and this was really helpful. I’ll definitely refer to this later.
I’d like to dig into the OTP library (I’m curious if anyone has worked with it much?) and create a state chart library with it, but I’m still firmly in the “I don’t totally get it” camp with a few parts of Gleam. I don’t deny that it’s pragmatic. Maybe it’s more so that I’m not up to speed on functional patterns in general. I was for years, but took a hiatus to write code for a game engine and supporting infrastructure. It was so Wild West, but I kind of liked it in the end. Lots of impure, imperative code, haha.
Most people use the OTP lib! There's this super useful intro repo: https://github.com/bcpeinhardt/learn_otp_with_gleam
I've tried to get my head around functional programming and also OTP but I also just never got my head around it.
Functional programming seems too limiting and OTP seems more complicated than I would have hoped for a supposedly distributed concurrency system.
I'm sure it's just a skill issue on my part. Right now I'm way too rust-brained. I've heard lots of things about gleam being good for productivity but I don't feel unproductive writing web apps in Rust but I felt every unproductive trying to write a non-trivial web app in gleam
tried gleam but the fact i have to manually serialize/deserialize things, pretty annoying, that doesn't seem very pragmatic
Isn’t manual ser/de pretty common? I like it personally. Being explicit at program boundaries usually means far fewer bugs inside the program. In JS I can pile whatever JSON I want into an object, but eventually I need to throw Zod or something at it to tame the crazy.
Maybe a generic “pile this data into this value and pretend it’s safe” tool might be nice for prototyping.
I agree that the stdlib decoder functions aren't the most ergonomic, but I think people are aware it's a pain point and there is development in that are, these two packages for example:
https://hexdocs.pm/decode
https://hexdocs.pm/toy/
This is the biggest reason I cooled a bit on Gleam and whenever I want to do some backend stuff I'd much rather use Rust (using serde to convert to structs) or Elixir (put it in dynamic maps).
I wish Gleam would implement some kind of macro system, making a serde-like package possible.
This is one of the complaints people have with Elm too. Json.Encode/Decode is a pain
I understand why the `use` syntax is preferable for its generalizability to many different "callback style" things, but the whole construct of `use foo <- result.try(bar())` is so much worse than defining let* in ocaml and being able to write `let* foo = bar() in`...
What would you say makes it much worse?
> Running on the battle-tested Erlang virtual machine that powers planet-scale systems such as WhatsApp and Ericsson, Gleam is ready for workloads of any size.
Does a Gleam programmer in practice need to deal with Erlang? Do Erlang error messages leak through?
Pure Gleam will get you really far without having to touch any Erlang, I've done Gleam for almost a year now and there were very little cases where I needed to write Erlang code myself, usually there's already a library that deals with it for most common needs :)
Doesn’t it compare mostly to F#, rather than Haskell or OCaml? The examples in the post really look like F# to me
Is there a way to implement matrix arithmetic with nice syntax (for instance, "A + B" to add two matrices A and B) in Gleam? The lack of ad-hoc polymorphism might paradoxically be a blessing.
newbie here, how does gleam compare to golang, rust and python?
It has some syntax similarities to Rust, but it has GC so there's no borrow checker (or any of the associated syntax). It is also fully immutable, unlike Rust. It leans heavily on sum types, just like Rust. Also expression-based syntax and some other things resemble Rust. However, it lacks Traits. Overall it looks Rust-ish but it's much simpler and has a functional focus.
With Go it shares a lazer focus on simplicity and preemptive channel-based concurrency. But of course for all the above reasons listed above it looks very different from Go in most other ways.
In many way its language choices are the opposite of Python (static types, immutability, massive concurrency is the norm).
[dead]
[flagged]
Does the C# runtime or language or so have any of the abilities that a BeamVM language can make use of? Afaik Gleam can do the same things you can do with Erlang, easily making a cluster of machines and easily running code on any of the machines in the cluster, load balanced, pattern matching on binary ...
Otherwise I don't understand what C# has to do with Gleam.
What has C# got to do with... anything in this article?
It's not pragmatic if you have to import these basic libs:
```
import gleam/dict.{type Dict}
import gleam/int
import gleam/io
import gleam/result
import gleam/string
```
Why not?
What's wrong with a standard library the bits of which you want you choose to import?
I guess I don't entirely agree, but I do wonder why each import has to include 'gleam' in the path. Why can't it assume that the default path is 'gleam' and import libraries relative to that path. Like `import string` instead of having to do `import gleam/string`?
The syntax doesn't look like it supports partial application? Big no-no. Also, no compilation to native code. Another big no-no.
https://tour.gleam.run/functions/function-captures/ https://tour.gleam.run/functions/pipelines/
These took me basically no time at all to find. Are you looking for something else for partial application?
Roc is a similar functional language that doesn't automatically curry functions and doesn't have partial application, the Roc FAQ has a few reasons: https://www.roc-lang.org/faq.html#curried-functions