[HN Gopher] Gleam Is Pragmatic
       ___________________________________________________________________
        
       Gleam Is Pragmatic
        
       Author : crowdhailer
       Score  : 121 points
       Date   : 2024-10-06 18:15 UTC (4 hours ago)
        
 (HTM) web link (blog.drewolson.org)
 (TXT) w3m dump (blog.drewolson.org)
        
       | stonethrowaway wrote:
       | 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.
        
         | flooow wrote:
         | What has C# got to do with... anything in this article?
        
         | zelphirkalt wrote:
         | 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.
        
           | neonsunset wrote:
           | 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.
        
           | conradludgate wrote:
           | 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 wrote:
             | 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 wrote:
               | 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 wrote:
               | It's not 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.
               | 
               | Good news is in elixir, most of it is pretty
               | straightforward to read so a junior could probably
               | successfully work through and understand a CR.
        
       | beanjuiceII wrote:
       | tried gleam but the fact i have to manually serialize/deserialize
       | things, pretty annoying, that doesn't seem very pragmatic
        
         | steve_adams_86 wrote:
         | 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 wrote:
           | 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__ wrote:
             | 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 wrote:
               | 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__ wrote:
               | I see, thanks. I thought maybe we were talking about the
               | choice of json vs something else being automatic and
               | chosen at runtime.
        
               | googledocsftw wrote:
               | C# (or more precisely .NET libraries) does it using
               | reflection. Attributes let you adjust the behaviour.
        
               | neonsunset wrote:
               | 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 wrote:
             | 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 wrote:
         | 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.
        
         | __jonas wrote:
         | 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/
        
       | steve_adams_86 wrote:
       | 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.
        
         | okkdev wrote:
         | Most people use the OTP lib! There's this super useful intro
         | repo: https://github.com/bcpeinhardt/learn_otp_with_gleam
        
           | steve_adams_86 wrote:
           | Incredible, thank you so much! This is exactly what I need.
        
         | conradludgate wrote:
         | 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
        
       | atemerev wrote:
       | 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.
        
         | vereis wrote:
         | Gleam runs on the BEAM
        
           | atemerev wrote:
           | 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 wrote:
             | > 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?
        
               | arcanemachiner wrote:
               | I believe their implementation was written to support
               | static typing (since Gleam is a statically-typed
               | language).
        
               | okkdev wrote:
               | 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 wrote:
               | 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!
        
               | H12 wrote:
               | IIRC the re-implementation was necessary for type-safety.
        
               | pmontra wrote:
               | 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 wrote:
               | 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.
        
         | steve_adams_86 wrote:
         | 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 wrote:
           | It is a very young language that may explain the why
        
         | jatins wrote:
         | 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)
        
         | dullcrisp wrote:
         | 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 wrote:
           | 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).
        
             | samatman wrote:
             | That used to be true, but no longer, goroutines are truly
             | preemptive, in 10ms time slices.
        
             | Jtsummers wrote:
             | 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.
        
       | behnamoh wrote:
       | 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 wrote:
         | Why not?
         | 
         | What's wrong with a standard library the bits of which you want
         | you choose to import?
        
           | reikonomusha wrote:
           | 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 cliche 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.
        
           | orthoxerox wrote:
           | 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 wrote:
             | > 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.
        
       | rossng wrote:
       | 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 wrote:
         | 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 wrote:
           | 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 wrote:
             | 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 wrote:
               | Sounds like the new "capabilities" stuff in Scala.
        
         | yawaramin wrote:
         | 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...
        
         | taberiand wrote:
         | 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
        
         | eddd-ddde wrote:
         | I think it's similar to koka's 'with'.
         | 
         | https://koka-lang.github.io/koka/doc/book.html#sec-with
        
       | patte wrote:
       | 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 wrote:
       | Gleam looks nice but if an F# comparisons was added, I think that
       | would come out ahead based on the authors priorities.
        
         | cipehr wrote:
         | 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#
        
         | devmunchies wrote:
         | 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.
        
       | jazzypants wrote:
       | > 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.
        
       ___________________________________________________________________
       (page generated 2024-10-06 23:00 UTC)