Stream: contributing

Topic: Dbg expression


view this post on Zulip Elias Mulhall (Dec 05 2023 at 22:11):

@Richard Feldman with regard to https://roc.zulipchat.com/#narrow/stream/304641-ideas/topic/Dbg.20expression/near/405449931 and https://github.com/roc-lang/roc/issues/5894, where should I start with this change?

I made a syntax snapshot test which tries to parse f (dbg 1) and fails with The source code for this test did not successfully parse!: "Expr(InParens(End(@3), @2), @0)". That tells me that there's parsing work to do before we can desugar the expr. Where's a good place to start tracing the parser code?

view this post on Zulip Richard Feldman (Dec 05 2023 at 22:15):

good question! I'm on mobile so don't have an exact link, but I'd look for where we do if parsing

view this post on Zulip Richard Feldman (Dec 05 2023 at 22:16):

because that's also a case where we have a special keyword (if rather than dbg) followed by an expression, and then the whole thing ends up being an expression

view this post on Zulip Elias Mulhall (Dec 05 2023 at 22:18):

yes this guy

fn expr_start<'a>(options: ExprParseOptions) -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>> {
    one_of![
        loc!(specialize(EExpr::If, if_expr_help(options))),
        loc!(specialize(EExpr::When, when::expr_help(options))),
        loc!(specialize(EExpr::Expect, expect_help(options))),
        loc!(specialize(EExpr::Dbg, dbg_help(options))),
        loc!(specialize(EExpr::Closure, closure_help(options))),
        loc!(expr_operator_chain(options)),
        fail_expr_start_e()
    ]
    .trace("expr_start")
}

So you think we should be able to update dbg_help to handle both statements and expressions?

view this post on Zulip Richard Feldman (Dec 05 2023 at 22:20):

I'm not sure what the code in that function looks like offhand, but seems totally fine to make it separate if you like

view this post on Zulip Elias Mulhall (Aug 26 2024 at 03:19):

Hello 9 month old thread!

I picked this up again recently and made some progress. I've hacked together a working solution by parsing dbg in expression position and then using the expr body for both the message and the continuation part of LowLevelDbg. This works in most cases, but there's a bug where nested debugs like dbg (dbg (dbg x) print to the terminal an exponential number of times. This is because the print side effect is doubled for each nested dbg, since we print once in the message code and again in the continuation.

The simplest solution I can think of would be to desugar the expression dbg x into

(
    v0 = x
    dbg v0
    v0
)

Which means dbg (dbg x) would be

(
    v0 = (
        v1 = x
        dbg v1
        v1
    )
    dbg v0
    v0
)

which will only print twice.

Unfortunate desugar_expr doesn't have access to a VarStore so I can't generate new variables while desugaring. Is there a good reason for that? Should I try to pass the var store in during desugaring? Should I move some of the desugar code to canonicalization instead? Should I try a different solution?

view this post on Zulip Brendan Hansknecht (Aug 26 2024 at 03:42):

but there's a bug where nested debugs like dbg (dbg (dbg x) print to the terminal an exponential number of times

Idk, that might be a feature.

view this post on Zulip Brendan Hansknecht (Aug 26 2024 at 03:44):

jk...

And I don't see why desugaring couldn't make a variable. That sounds like it should be a fine solution

view this post on Zulip Elias Mulhall (Aug 29 2024 at 18:00):

I got some great PR feedback from @Joshua Warner about handling dbg similar to crash, where we just parse the dbg keyword and then in canonicalization we pattern match on Apply(Dbg, args, called_via) and desugar that into a LowLevelDbg. Doing it this way means the parser can be more lenient, and then in canonicalization we can report if dbg was given too many or no args.

Question: When crash is called with too many or no args we continue compiling and fall-back to crash "hit a crash!". What would be an appropriate equivalent for dbg? I'm leaning towards dbg {}.

There's an argument to be made for hard-failing instead of using a default, since no matter what fallback value we use it will likely produce a type error.

Here's an example with {} as the default

main =
    x = 1 + dbg + 2

    Stdout.line! "test"
── TYPE MISMATCH in day_01/main.roc ────────────────────────────────────────────

This 2nd argument to + has an unexpected type:

11│      x = 1 + dbg + 2
                 ^^^

This 63 value is a:

    {}

But + needs its 2nd argument to be:

    Num *


── UNAPPLIED DBG in day_01/main.roc ────────────────────────────────────────────

This dbg doesn't have a value given to it:

11│      x = 1 + dbg + 2
                 ^^^

dbg must be passed a value to print at the exact place it's used. dbg
can't be used as a value that's passed around, like functions can be -
it must be applied immediately!

────────────────────────────────────────────────────────────────────────────────

2 errors and 5 warnings found in 239 ms
.

Running program anyway…

────────────────────────────────────────────────────────────────────────────────
Roc crashed with:

    Hit an erroneous type when creating a layout for `4.IdentId(19)`

view this post on Zulip Brendan Hansknecht (Aug 29 2024 at 19:56):

"this 63 value"?

view this post on Zulip Brendan Hansknecht (Aug 29 2024 at 19:57):

Also, any way we can hide the type error and just print the debug error?

view this post on Zulip Brendan Hansknecht (Aug 29 2024 at 19:57):

Otherwise, having a default seems fine

view this post on Zulip Elias Mulhall (Aug 29 2024 at 19:59):

"this 63 value"?

Ohhhh yeah, I figured that was an existing bug but it's actually my b, I'll fix that.

view this post on Zulip Elias Mulhall (Aug 29 2024 at 20:14):

Fixed so it will now read "The argument is a record of type:" instead of "This 63 value is a:"

I'm not sure if there's a non-hacky way to hide the type error. It would involve suppressing the error for invalid calls to dbg, but leaving it if the user actually calls dbg {} and that's a type mismatch. I think that having these two errors next to each other isn't the worst.

view this post on Zulip Brendan Hansknecht (Aug 29 2024 at 22:08):

Ok

view this post on Zulip Brendan Hansknecht (Aug 29 2024 at 22:08):

No worries


Last updated: Jul 06 2025 at 12:14 UTC