Lots of good feedback and things to unwrap (no pun intended), thank you!
We’d like to avoid to add more keywords unless we really have to. let? is good like that because it doesn’t interfere with anything existing. Using maybe would mean that it’d become a keyword, which means you couldn’t use it anywhere else.
Another naming that came up was let.unwrap. It would make the example:
/* returns: string => promise<result<decodedUser, 'errors>> */
let getUser = async (id) => {
/* if fetchUser returns Error e, the function returns Error e here. Same for all other `maybe` */
let.unwrap Ok(user) = await fetchUser(id)
let.unwrap Ok(decodedUser) = decodeUser(user)
Console.log(`Got user ${decodedUser.name}!`)
let.unwrap Ok() = await ensureUserActive(decodedUser)
Ok(decodedUser)
}
But it’s also a lot more verbose than let?.
Funny, the feeling was the exact opposite when we came up with it - “this will be familiar to most people - ? for unwrapping in Rust, ? for optional chaining (a form of unwrapping) in JS”
The real world is not always what one thinks.
let* would be confused with OCaml letops (this proposal is not the same as OCaml letops), so that would probably be quite difficult. Plus at least personally I think that let* isn’t intuitive and honestly quite confusing of a syntax.
Effect is cool and all, but the syntax to use it is alien to pretty much whoever looks at it. So while it would give familiarity to the small subset of people interested in both Effect and ReScript, it would be the wrong way of going about it honestly (taking a “bad” syntax just for familiarity).
One good way of thinking about it is that if the Effect people could choose freely for syntax, would they keep yield*? Pretty sure they would not.
In theory, yes, it would be possible to have just let. But it’d require trade offs.
This is an interesting point - there’s actually a bunch of things we’ve added here that’s “superflous” and not strictly needed, but we’ve added them anyway to make the code clearer (and the implementation simpler of course).
- We have
let? Ok(user) = ...but theoretically it could be justlet? user = ...and we could look up the type of what holdsuserduring typechecking, so you wouldn’t need to hint about it being a result viaOk. But, needing to wrap it inOkorSomeis good because it’s immediately clear what’s going on when you look at the code. This is really important. Together with marking theletbinding with?, it make it possible for the entire feature to be a ~60 LoC internal compiler transform. This is good. - Continuing on the above, we actually wouldn’t need the
?either… Most people probably don’t know, butlet Ok(user) = ...is already perfectly valid syntax today. It’s just that it’ll yield a “partial pattern match is missing cases” warning (you can’t handle theErrorcase in a single let binding, and a let bindings left hand side is actually a pattern match). But, we could change that type of pattern match in let-binding to always trigger this feature we’re talking about, so that you wouldn’t needlet?and always get that behavior. But, going by the same standards as detailed above, that would make the code unclear and magic at a glance - you wouldn’t know what type wrappeduser, and via that you also wouldn’t know what type the entire function would have. Here, you can immediately see it’s either a result or an option by looking at what constructors is used for the unwrapping.
What point 2 above means is that we want something by the let-binding to signal that unwrapping will happen, since you won’t see that in the code by just looking at it. let? was the most liked and most clear alternative we could come up with.
Keep the feedback coming and the discussion going! It’s important we get these details right and exhaust all of our alternatives.