The cool things of Gleam
20 points by giacomo_cavalieri
20 points by giacomo_cavalieri
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.
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
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.
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.
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"
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.: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.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.