jazzypants an hour ago

> 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.

patte 4 hours ago

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

fire_lake 3 hours ago

Gleam looks nice but if an F# comparisons was added, I think that would come out ahead based on the authors priorities.

  • devmunchies an hour ago

    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.

  • cipehr 2 hours ago

    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#

rossng 4 hours ago

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?

  • jyjasdfsssd 4 hours ago

    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

    • rossng 4 hours ago

      Hmm, it definitely looks more interesting in combination with effect handlers. Still not sure I find it super compelling in Gleam vs just not using callbacks.

      • jitl 3 hours ago

        It’s a generalization of async/await syntax in languages like JavaScript or Swift. I like that it provides a generalized syntax that could be used for coroutines, generators, or async/await without adding any of those specifically to the language syntactically.

        One level of callback nesting in a function is totally fine, two is a bit confusing, but if you have many async things going on do you really want 10, 15, 20 levels of nesting? What to do about loops?

        I certainly greatly prefer async programming with async/await languages that keep the appearance of linear function execution to stacking my callbacks and having a ton of nesting everywhere

        • vips7L 2 hours ago

          Sounds like the new “capabilities” stuff in Scala.

  • taberiand 3 hours ago

    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

steve_adams_86 5 hours ago

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.

  • conradludgate an hour ago

    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

beanjuiceII 5 hours ago

tried gleam but the fact i have to manually serialize/deserialize things, pretty annoying, that doesn't seem very pragmatic

  • steve_adams_86 5 hours ago

    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.

    • beanjuiceII 4 hours ago

      i dont think manual ser/de is common at all, and languages like dart where it was used is a massive pain point for people so much that they are adding macros to the language and the first macro they add is for serialization. whats not explicit about saying hey i have a struct this is the data i expect, serialize/deseralize in this shape, validation is a another but separate concern. in javascript you are not doing anything manually so i'm not sure why thats an example?

      • __MatrixMan__ 3 hours ago

        I'm a bit confused. How can you control how your data is serialized if not manually? Are there languages that use some kind of magically-figures-it-out layer that negotiates the appropriate serialization on the fly?

        • yawaramin 3 hours ago

          Many languages have some kind of macro or codegen system that allows serializing or deserializing based on type definitions. Eg (pseudocode):

              @deriving(json)
              class Person:
                id: int
                name: str
          
          Would give you something like:

              def parse(s: str): Person: ...
              def print(p: Person): str: ...
          • __MatrixMan__ 3 hours ago

            I see, thanks. I thought maybe we were talking about the choice of json vs something else being automatic and chosen at runtime.

        • googledocsftw an hour ago

          C# (or more precisely .NET libraries) does it using reflection. Attributes let you adjust the behaviour.

          • neonsunset an hour ago

            Or with build-time source generation (because this specific pattern of reflection is AOT-unfriendly). It's not as convenient if you are using default serializer options, but if you don't - it ties together JsonTypeInfo<T> and JsonSerializerOptions, so it ends up being a slightly terser way to write it. I do prefer the Rust-style serde annotations however.

              record User(string Name, DateOnly DoB);
            
              [JsonSerializable(typeof(User))]
              partial class SerializerContext: JsonSerializerContext;
            
              ...
              var user = new User("John", new(1984, 1, 1));
              var response = await http.PostAsJsonAsync(
                url, user, SerializerContext.Default.User);
      • steve_adams_86 3 hours ago

        Sorry I wasn’t clear; I meant to use JavaScript as an example where it isn’t manual.

        Despite it being easy to use, I find I inevitably wind up requiring a lot of ceremony and effort to ensure it’s safe. I’m not a huge fan of automatic serialization in that it appears to work fine when sometimes it shouldn’t/won’t. I agree that it’s a lot of effort though. I guess the question is if you want the effort up front or later on. I prefer up front, I guess.

  • lawn 3 hours ago

    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.

atemerev 5 hours ago

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.

  • steve_adams_86 5 hours ago

    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.

    • cassepipe 4 hours ago

      It is a very young language that may explain the why

  • dullcrisp 3 hours ago

    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?

    • throwawaymaths an hour ago

      I believe go yields after every function exit. Erlang does the same, but there are no loops (you must use tailcall) so you can't lock up the CPU with a while(true).

      • Jtsummers an hour ago

        Erlang gives a reductions budget to processes. After a certain number of reductions, or if a process hits a yield point (like waiting to receive a message), the process will yield allowing another process to run.

        Go uses preemption now (since 1.14), but it didn't always. It used to be that you could use a busy loop and that goroutine would never yield. Yield points include things like function entries, syscalls, and a few other points.

      • samatman an hour ago

        That used to be true, but no longer, goroutines are truly preëmptive, in 10ms time slices.

  • vereis 5 hours ago

    Gleam runs on the BEAM

    • atemerev 5 hours ago

      It does. However, its actor implementation is not built upon Erlang/OTP, and currently is “experimental” and not even mentioned on the main site.

      • lolinder 4 hours ago

        > its actor implementation is not built upon Erlang/OTP

        This seems to be the opposite of pragmatic.

        The most pragmatic approach to actors when you're building a BEAM language would be to write bindings for OTP and be done with it. This sounds kind of like building a JVM language with no intention of providing interop with the JVM ecosystem—yeah, the VM is good, but the ecosystem is what we're actually there for.

        If you're building a BEAM language, why would you attempt to reimplement OTP?

        • okkdev 4 hours ago

          Because of type safety. The OTP lib is already great, but there are still some things missing, most requested being named processes. But there is work being done to figure out how to best make it work for gleam.

          • lolinder 3 hours ago

            The question of type safety has come up so often here that I guess it's worth replying:

            That's exactly what I mean by this not seeming pragmatic. Pragmatic would be making do with partial type safety in order to be fully compatible with OTP. That's the much-maligned TypeScript approach, and it worked for TypeScript because it was pragmatic.

            Now, maybe Gleam feels the need to take this approach because Elixir is already planning on filling the pragmatic gradually-typed BEAM language niche. That's fine if so!

        • arcanemachiner 4 hours ago

          I believe their implementation was written to support static typing (since Gleam is a statically-typed language).

        • pmontra 3 hours ago

          I agree with the part about reusing OTP but some of the server syntax of Erlang and Elixir is not good IMHO. I never liked using those handle_* functions. Give them proper names and you cover nearly all the normal usage, which is mutating the internal state of a process (an object in other families of languages.) That would be the pragmatic choice, to lure Java, C++ programmers.

          • throwawaymaths an hour ago

            Elixir gives you Agent, which is what you want, but for reasons, Agent is a bad choice.

            What you're not seeing with the handle_* functions is all the extra stuff in there that deals with, for example, "what if the thing you want to access is unavailable?". That's not really something that for example go is able to handle so easily.

        • H12 3 hours ago

          IIRC the re-implementation was necessary for type-safety.

behnamoh 4 hours ago

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

```

  • eterm 4 hours ago

    Why not?

    What's wrong with a standard library the bits of which you want you choose to import?

    • orthoxerox 2 hours ago

      I can understand having to import the "dirty" parts of the stdlib, like I/O, or the "heavy" parts, like Unicode or timezones. But why force someone to import every single type? Most functional languages have a prelude that covers the types every non-trivial program uses: booleans, numbers, strings, collections.

      • Jtsummers 2 hours ago

        > But why force someone to import every single type?

        That's not importing the types, it's importing a suite of functions related to the types.

        https://hexdocs.pm/gleam_stdlib/gleam/int.html - gleam/int for example. The int type is already in the language and usable, this import brings in some specific functions that are related to operations on int.

    • reikonomusha 4 hours ago

      It's not that it's wrong—at least I don't think so. It's that it's an example of a choice that is not pragmatic.

      I suppose we should agree on what "pragmatic" even means, since it has become something of a cliché term in software engineering. To me, it roughly means "reflective of common and realistic use as opposed to possible or theoretical considerations".

      So is having to import basic functionality a pragmatic design? I would argue no. Having to import basic functionality for integers, strings, and IO is not pragmatic in the sense that most realistic programs will use these things. As such, the vast majority of ordinary programs are burdened by extra steps that don't appear to net some other benefit.

      Importing these very basic functionalities appeals to a more abstract or theoretical need for fine-grained control or minimalism. Maybe we don't want to use integers or strings in a certain code module. Maybe we want to compile Gleam to a microcontroller where the code needs to be spartan and freestanding.

      These aren't pragmatic concerns in the context of the types of problems Gleam is designed to address.

      To give a point of comparison, the Haskell prelude might be considered a pragmatic design choice, as can be seen from the article. It is a bundle of common or useful functionality that one expects to use in a majority of ordinary Haskell programs. One doesn't need to "import" the prelude; it's just there.

      I don't personally find Gleam's design choice a bad one, and while GP was a bit flippant, I do agree that it is not an example of a pragmatic design choice.

stonethrowaway 5 hours ago

I don’t think we’ve had anyone drop the hammer on the fact that C# is right around the corner (in part thanks to adopting a bit of this a bit of that from F#) with some of this outstanding periphery. Certainly I haven’t come across an article that’s bold enough to attempt it (bonus points for meme’ing it with “C# is all you need”). I’m assuming it’s a mindshare thing and nerds would rather flex on each other by conjuring up unconventional languages. And perhaps I’m stating the obvious in this regard.

  • zelphirkalt 5 hours ago

    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.

    • conradludgate an hour ago

      I'd rather use an external job scheduler and message queue, eg in kubernetes, rather than build it into the language runtime. With a message queue and horizontal pod autoscaling you can very quickly build an easy distributed workload with (at least to me) little effort in any language

      • throwawaymaths an hour ago

        Or you could do with almost zero effort in a BEAM language. And sometimes setting up a kuberbetes cluster is not easy. Suppose you want to have one part that is hosted on a cloud service and another that is on-prem.

        It's certainly possible with kuberbetes, but you'll be fighting the kuberbetes paradigm. Plus you're checking in to this kuberbetes middleman for part A to understand the availability of part B, for example.

        Kuberbetes is highly devex optimized for stateless services that are kinda cattle. Not all use cases fit so neatly into that rubric.

        • conradludgate an hour ago

          I would not say it's zero effort in BEAM. I wasn't able to figure out how to get it to work at all (I spent a weekend trying to build a distributed auth system, failed). But I would be able to spin up kubernetes, rabbitmq and a non BEAM language in not very long.

          > Suppose you want to have one part that is hosted on a cloud service and another that is on-prem.

          Is this hard with kubernetes? Seems pretty simple to add node taints and set up the appropriate VPN to me.

          BEAM is not magic. It will be doing a lot of the same stuff that any other scheduler or message queue will do, so I would personally rather choose the more composable solution rather than have to manage BEAM and be forced to use one family of languages only.

          I'm thinking if your company is already running kubernetes, adding BEAM on top is probably more effort than adding a message queue (given my limited understanding of BEAM)

          • throwawaymaths 25 minutes ago

            Never said it was zero effort. It's almost zero effort. But you have to have deep knowledge of BEAM primitives if you intend on doing something crazy (still almost zero effort). If you don't know what you're doing or don't have experience in the BEAM you will probably get it wrong. Sometimes libraries haven't gotten it right so composability might not be there and you might have to write a sidecar genserver and supervisor (~40 LOC)

            Good news is in elixir, though it is not easy to write (still low effort) most of it is pretty straightforward to read so a junior could probably successfully work through and understand a CR.

            The other very nice thing about doing it all in the BEAM is that you can make distribution and availability guarantees part of your testing suite very easy, because its even part of you localdev flow. That also means your devs will write that test, instead of of assuming they understand how that kuberbetes operator works (they don't).

    • neonsunset an hour ago

      I believe the comparison to C# is incorrect. Given what the article highlights, the direct competitor to Gleam is going to be F#. By virtue of using .NET, it offers an order of magnitude better performance, significantly cheaper per-process/task cost and equally capable overall concurrency primitives. A much bigger ecosystem with many polished libraries too.

  • flooow 5 hours ago

    What has C# got to do with... anything in this article?