The cool things of Gleam

20 points by giacomo_cavalieri 16 hours ago on lobsters | 10 comments

ryan-duve | 12 hours ago

I've never used Gleam and vaguely understand it as "Elixir with first class types", before Elixir had types, anyway. Some of those features seem awesome and drop dead obvious (like built-in TODOs/deprecations) but then I got to this:

In Gleam, the language takes the approach of being meticulous on doing things one simple way. Which means, case expressions are your everything:

Oh no. Did Gleam throw away pattern matching for function heads? I understand how it's redundant with case statements, but that seems like a giant step backwards.

[OP] giacomo_cavalieri | 12 hours ago

Yeah there's no pattern matching in function heads but only through case expressions. For example:

pub fn wibble(a: Int, b: List(a)) {
  case a, b {
    1, [] -> todo
    _, [_, ..] -> todo
    _, _ -> todo
  }
}

Pattern matching in function heads doesn't enable anything that case expression can't achieve already, so it makes sense (for Gleam at least!) to not have that

ryan-duve | 12 hours ago

I'm actively at a point where I naturally start functions with a case statement and have to force myself to go back in my Erlang/Elixir code to rewrite separate function heads, so you can imaging how dizzy this makes me!

One thing I would miss is conditional destructuring. Like, I'm imagining when I've got complex inputs to a function and want to take some action based on a minimal condition, first:

% "Super complex" business calculation: (A + B) / C.
ratio(_, _, 0) -> denominator_zero;
ratio(_, _, C) when C > 1000 -> denominator_too_large;
ratio(#{price := A}, #{price := B}, C) -> (A + B) / C.

I think the only way this can be done with case statements is to first destructure all the inputs and then run a check to see if the third input is invalid? Maybe there's a reason that's strictly better, but I would miss the ability to, at a glance, know the result of the first clause without having to consider the structure of reason anything about the first two parameters.

Here's one implementation of that in Gleam:

pub fn ratio(a: Item, b: Item, c: Int) -> Result(Int, RatioError) {
  case a, b, c {
    _, _, 0 -> Error(DenominatorZero)
    _, _, c if c > 1000 -> Error(DenominatorTooLarge)
    Item(price: a_price), Item(price: b_price), c -> Ok({a_price + b_price} / c)
  }
}

Sure, you have the function head there with the types, but the match itself doesn't really look that much different. Personally I think not repeating the function name is an improvement.

nick4 | 10 hours ago

I actually haven't missed pattern matching on function heads too much, even as someone who's done a lot of Haskell. I think I actually miss ifs more.

ryan-duve | 9 hours ago

There are no if statements? Point Gleam. This morning is a roller coaster.

Fun bit of trivia: the Erlang VM doesn’t support multiple function heads, so one is written in Erlang or Elixir the Erlang compiler will desugar them into a single function with a case expression.

patryk | 10 hours ago

I know it's not direct Gleam vs Elixir comparison, but I'd appreciate notes like "Elixir has this too!" and "This is different from elixir"

E.g. @deprecated works in Elixir just fine

There’s no reason one would need to always compare Gleam to Elixir. Judging by their own posts the author is a Go programmer, so they’d be more likely to compare it to Go.

My notes:

  • todo is not strictly needed in Elixir, as there's no need to make the program pass type checking to be compiled. You'd generally use raise "Not implemented: should do something" if you wanted to crash.
  • Elixir has generic types in the typespec syntax, but those are only for dialyzer and there are varying opinions of its usefulness. There's no syntax yet for the new type system.
  • You can write Elixir by using option and result types, and I personally wish the ecosystem would have embraced that more. But there's no support from the tooling and most of the ecosystem uses varying types for ok/error results (:ok, {:ok, result}, {:ok, result, result2} and so on), making this more difficult. The stdlib doesn't have one set style either. OTOH, while frowned upon in libraries, it's also technically possible to assert in Gleam code.
  • The last point about nulls is kind of the same and not the same in Elixir. The same in that there's the atom nil but it's not really special and it's a separate type from others. But since the language is dynamically typed, that nil may appear anywhere unless guarded against.