What I've Learned Writing Gleam
33 points by briankung
33 points by briankung
The use construct is still so interesting to me.
https://tour.gleam.run/advanced-features/use/
I first thought it was like "do notation" in Haskell, but it actually has quite a different desugaring. Also, I think it's not as "generic" semantically (please correct me if I'm wrong).
Here's how I understood the two desugar:
Haskell:
do { x <- a ; b x }
a >>= \x -> b x
Gleam:
x <- use a; b(x)
a(fn(x) { b(x) })
So it's actually inverted in a way!
I don't think it's inverted in any sense.
\x -> b x is the same as fn(x) { b(x) }.
a >>= means look up some function called >>= associated with the type of a, and call that with arguments a and the lambda. Currying it with the a, it is a single argument function taking the following lambda. Making the user construct that function explicitly (like with result.try) rather than typeclass magic seems the same in spirit to me.
If anything, the Gleam version is more semantically generic than the Haskell version. It doesn't require a monad instance, and it allows mixing bind operators across a sequence of binds.
Def more generic; it trades requiring a Monad instance for specifying the "bind" function. But that function doesn't need monad/bind semantics at all, as long as it takes some kind of callback as its last argument; you can do use el <- list.map(some_func()) for instance, which obviously is shaped like a functor, not a monad.
The bool.guard stdlib function mentioned in OP is another great example that doesn't even put you in a new functor/monad "context" – its return type is just the return type of its callback, so useing it doesn't "color" your function at all like result.try would.
The one thing it can't do that Haskell's do can is the ApplicativeDo extension – I'm not sure how you'd use use in places where you have strictly applicative semantics, but idiomatic Gleam code is not generally applicative style.
I think the use construct is one of the most interesting syntax sugars I've seen in languages recently. I think it could be a huge footgun in large teams with varying degrees of comfort with the language since it's so unusual, but it is undeniably useful. At least you can declare its scope with parentheses, manually, though that kind of counteracts its aesthetic appeal. I think the blog post's code snippet could look like this (I'm not a daily Gleam user, so please forgive any mistakes):
fn process_user(id: String) -> Result(User, Error) {
use user <- result.try(fetch_user(id)) {
use valid_user <- result.try(validate_user(user)) {
use enriched <- result.try(enrich_user(valid_user)) {
Ok(enriched)
}
}
}
}
Yeah. I think you're right. I was kinda confused by the fact that in Gleam a is "called", but I think it's indeed the same in spirit, as you say. Thanks!
Interesting article. However I think the section on "effect types" was misleading. Based on that wording I was expecting an actual type that decades any side effects the function may have; but the examples only showed the Result type.
Sure, if a function signature says it returns a Result with potential for a DBError, that implies to us that the function may write to a database. But the same signature could still be true even if the function only reads. Or, imagine some impure operation that can't actually fail -- then how do we denote the effect in the signature?
In summary, i don't believe that understanding the Result type as "a type that encodes the effects of the function" is a helpful way to look at it
Keep Code Flat with Early Returns
Very situational. Early returns are cool if you are returning a single value and that has all the information. If, however, you are mutating something outside your function (pointers, references, static memory, zero-copy, etc.) it is very common to forget to set or initialize something because you bailed out before the code that does that.
When a failure isn’t fatal and you have a sensible default, result.unwrap keeps things simple
Elvis would like a word here.
For reference, since someone obviously did not get the obvious reference: https://en.wikipedia.org/wiki/Elvis_operator