Stream: beginners

Topic: Overzealous shadowing rules?


view this post on Zulip Daniel Rivas (Feb 21 2024 at 11:14):

Hi all,

I'm going through the Roc tutorial and running into an issue with the step on destructuring. Following the tutorial fairly closely, I've ended up with this program:

birds = 3
iguanas = 2
total = addAndStringify {birds, iguanas}

addAndStringify = \{birds, iguanas} ->
  sum = birds + iguanas
  if sum == 0 then
    ""
  else
    Num.toStr sum

main = Stdout.line "There are $(total) animals."

This fails to compile with the error that the birds and iguanas identifiers in the destructured record are duplicates of the original birds and iguanas variables.

The pattern of naming some variables, placing them in a record via punning and then pulling them out in a function parameter via destructuring seems like it would be a common one (I certainly expected it to just work}. I also notice that this happens with "normal" parameter names (not just destructured field names), and also if I create birds or iguanas variables inside the function body itself.

Is the no-shadow rule supposed to be this strict, in which case I think there is a rough edge here in the tutorial, or is this a bug?

(Related-ish: I'd be interesting in knowing the rationale around banning underscores in identifiers? My first instinct for fixing my compile error was to rename the parameters iguanas_ and birds_, but that refused to compile as well.)

view this post on Zulip Anton (Feb 21 2024 at 12:12):

Is the no-shadow rule supposed to be this strict

Currently, yes, but we're working on allowing shadowing.

view this post on Zulip Anton (Feb 21 2024 at 12:13):

in which case I think there is a rough edge here

I agree but I don't know how we can avoid it here :thinking:

view this post on Zulip Anton (Feb 21 2024 at 12:15):

Richard has talked about underscores in identifiers here.

view this post on Zulip Daniel Rivas (Feb 21 2024 at 12:58):

Aha, thanks for the reply. It seems like this is a pretty common complaint--I'm definitely in the pro-shadowing camp! My general feeling is that it's hard to come up with variable names, and it's really annoying to have to think of a new one just because it happens to have been used before in some other context. This is exacerbated by my being one of those annoying people who tends to name short-lived variables things like 'x' or 'y'.

WRT the tutorial: I suppose if the plan is to enable shadowing of some sort there's no reason for the tutorial to change in the meantime. I can't immediately think of a case where destructuring a parameter record would be useful _without_ allowing shadowing.

view this post on Zulip Brendan Hansknecht (Feb 21 2024 at 17:17):

To be fair, this example is something that is super rare and I have essentially never run into in practice. Having a global with a name and record fields with the same name. In this specific case you would just reference the globals directly.

view this post on Zulip Brendan Hansknecht (Feb 21 2024 at 17:18):

Generally you also don't put a value into a record and destructure in the same or a nested scope. You just use the variables directly in those cases. You would only build the record if sending the record to a totally unrelated scope. Which would be totally allowed to destructure.

view this post on Zulip Brendan Hansknecht (Feb 21 2024 at 17:18):

Of course there are other reasons to want shadowing.

view this post on Zulip Daniel Rivas (Feb 21 2024 at 19:48):

Hmm, that's interesting! This is a case that I run into all the time, but I'm primarily a Rust and TypeScript developer, so it might just be that I'm holding the language wrong. Anecdotally, a colleague of mine activated the equivalent no-shadow rule in our ESLint a few months ago, and it added hundreds of errors. But I understand that the no-shadow rule is inherited from Elm, and while I haven't used Elm very much personally I can't remember hearing anyone complaining about that rule, so maybe I'm just not using Roc correctly.

Still though, I feel like it's going to be an annoyance for me. In JavaScript it's very common to use a destructured object as a function parameter, as a workaround for the lack of named arguments. But more generally, there are cases where there just aren't many good names for a certain thing. I'm thinking of, e.g., a Point { x: I64, y: I64 } type -- I really don't want to have to think of new names for x and y if I happen to pull them out into variables.

Anyway, if you say it's not really an issue in practice, I'll take your word for it for now! I definitely don't have enough Roc experience to judge -- it's just something that definitely added friction as a beginner.


Last updated: Jul 06 2025 at 12:14 UTC