Stream: ideas

Topic: Calling style for special functions like `dbg` and `crash`


view this post on Zulip Sam Mohr (Jan 02 2025 at 15:33):

With the move to parentheses-based function calls (let's say PNC), we now have a potential schism between how we call user-defined functions and our special keywords: dbg, crash, try. It's likely that try gets removed once PNC is ready, but the other 2 will still be around. They are not currently visually distinct from function calls (unless syntax highlighting is aware of them), but it would be nice to know when your special keywords (dbg logs to the console) were visually distinct for scanning code.

We have a few options as to how we can call them, given these type signatures:

dbg : a -> a where a.to_str() -> Str
crash : Str -> a # an unbound variable

Named normally, using spaces

dbg "abc"
func.call(123, dbg)

Maintain the current syntax, but allow for passing them around as functions.

Named normally, using parens

dbg("abc")
func.call(123, dbg)

With an @ prefix, using parens

@dbg("abc")
func.call(123, @dbg)

With an ! suffix, using parens

dbg!("abc")
func.call(123, dbg!)

Of these options, I'd vote for @ prefixes since they're unique in Roc and don't confuse users about !, but are visually distinct. Close second is using parens and relying on syntax highlighting. Thoughts?

view this post on Zulip Brendan Hansknecht (Jan 02 2025 at 15:38):

I see no reason to make them special. I would vote for just treating them like normal functions

view this post on Zulip Richard Feldman (Jan 02 2025 at 16:04):

keep in mind return doesn't do this

view this post on Zulip Richard Feldman (Jan 02 2025 at 16:04):

and it doesn't in mainstream languages either

view this post on Zulip Sam Mohr (Jan 02 2025 at 16:04):

Yep

view this post on Zulip Richard Feldman (Jan 02 2025 at 16:04):

so there's precedent both ways

view this post on Zulip Richard Feldman (Jan 02 2025 at 16:04):

same with stuff like throw or raise

view this post on Zulip Sam Mohr (Jan 02 2025 at 16:05):

Roc has been good at annotating control flow subversion with purity inference, however

view this post on Zulip Sam Mohr (Jan 02 2025 at 16:05):

You're right that this would be exceptional

view this post on Zulip Brendan Hansknecht (Jan 02 2025 at 16:21):

Personally, they just feel like normal functions to me. So I really don't feel there is any need to make them stick out more than a slightly different syntax coloring. They should be usable where a lambda is usable.

view this post on Zulip Brendan Hansknecht (Jan 02 2025 at 16:23):

Oh, just realized that today crash and dbg can't be used as lambdas and passed into other functions

view this post on Zulip Brendan Hansknecht (Jan 02 2025 at 16:24):

In my opinion, if we allow them to be passed into other functions like lambdas (which I think makes sense), they should just be normal functions. If instead they are not allowed to be passed into other functions, it makes sense to keep them extra special

view this post on Zulip Richard Feldman (Jan 02 2025 at 16:26):

yeah I don't think they should be passed into other functions :big_smile:

view this post on Zulip Brendan Hansknecht (Jan 02 2025 at 16:29):

I mean they are already trivial to pass into other functions \x -> crash "unreachable"

view this post on Zulip Brendan Hansknecht (Jan 02 2025 at 16:30):

And I could see someone using dbg for a logger function in a simple platform.

view this post on Zulip Brendan Hansknecht (Jan 02 2025 at 16:30):

So might pass it into a module param for a logger in another module

view this post on Zulip Sam Mohr (Jan 02 2025 at 16:30):

dbg is weird because it's currently either a statement or an expression. We could probably make it just an expr of type a -> a where a.to_str() -> Str but then we'd need to treat it specially for dbg(x) statements, since pure statements need to return {} and this returns x.

view this post on Zulip Sam Mohr (Jan 02 2025 at 16:31):

So at least dbg can't just be a normal function all the time, even if it is most of the time

view this post on Zulip Brendan Hansknecht (Jan 02 2025 at 16:32):

Fair enough

view this post on Zulip Brendan Hansknecht (Jan 02 2025 at 16:32):

I think that means it makes most sense to leave these fully special like return

view this post on Zulip Sam Mohr (Jan 02 2025 at 16:35):

It'd be nice to remove whitespace calling within expressions, but we have that for math operators

view this post on Zulip Sam Mohr (Jan 02 2025 at 16:36):

Like func.call(123, crash "an error occurred") would be nice to deprecate in favor of func.call(123, crash("an error occurred"))

view this post on Zulip Sam Mohr (Jan 02 2025 at 16:39):

Then assuming there's no prefix/suffix for these special functions, I'd vote for calling them like normal functions without spaces

view this post on Zulip Sam Mohr (Jan 02 2025 at 16:40):

Since it removes all whitespace delimiting from expressions

view this post on Zulip Anthony Bullard (Jan 02 2025 at 16:45):

I'd like dbg to have the signature a -> a where a.to-str: (a -> Str) -> a

view this post on Zulip Anthony Bullard (Jan 02 2025 at 16:45):

I love that Elm debug functions the passed in value

view this post on Zulip Richard Feldman (Jan 02 2025 at 16:45):

Sam Mohr said:

Like func.call(123, crash "an error occurred") would be nice to deprecate in favor of func.call(123, crash("an error occurred"))

in general I'd actually like to give a warning for using crash as an expression like this

view this post on Zulip Sam Mohr (Jan 02 2025 at 16:45):

That'd be nice

view this post on Zulip Anthony Bullard (Jan 02 2025 at 16:46):

So much so that I implemented it in the formula language we use dynamic configuration of components in the product I work on at work

view this post on Zulip Brendan Hansknecht (Jan 02 2025 at 16:46):

Anthony Bullard said:

a -> a where a.to-str: (a -> Str) -> a

dbg has two signatures a -> a where a.to-str: (a -> Str) -> a and a -> a where a.to-str: (a -> Str) -> {}

view this post on Zulip Richard Feldman (Jan 02 2025 at 16:46):

it's reasonable to use crash as an expression being given to a short-circuiting operator

view this post on Zulip Richard Feldman (Jan 02 2025 at 16:46):

but foo(bar, baz, crash ...) never makes sense

view this post on Zulip Richard Feldman (Jan 02 2025 at 16:46):

because it's just going to crash, so you might as well delete the foo(bar, baz part

view this post on Zulip Brendan Hansknecht (Jan 02 2025 at 16:47):

it's reasonable to use crash as an expression being given to a short-circuiting operator

We could still suggest using a if ... then in those cases.

view this post on Zulip Anthony Bullard (Jan 02 2025 at 16:47):

Yeah, so if we keep that our statements are:

?

view this post on Zulip Richard Feldman (Jan 02 2025 at 16:47):

Brendan Hansknecht said:

Anthony Bullard said:

a -> a where a.to-str: (a -> Str) -> a

dbg has two signatures a -> a where a.to-str: (a -> Str) -> a and a -> a where a.to-str: (a -> Str) -> {}

and worth noting that in Elm, the equivalent of dbg only has the expression version, and it leads to people doing things like _ = dbg ... all the time

view this post on Zulip Brendan Hansknecht (Jan 02 2025 at 16:48):

Yeah, so if we keep that our statements are:

and dbg

view this post on Zulip Anthony Bullard (Jan 02 2025 at 16:48):

I wonder if you could just have a UnitIgnoreExpr statement?

view this post on Zulip Anthony Bullard (Jan 02 2025 at 16:48):

That's just an Expr that's followed by other statements, and in can we find out if the expr has the type {}?

view this post on Zulip Anthony Bullard (Jan 02 2025 at 16:49):

Sorry for using the name Unit but that's how my functional brain thinks of that type

view this post on Zulip Sam Mohr (Jan 02 2025 at 16:49):

I presumed the long term plan for parsing output is that a module is:

function bodies are just lists of type or value statements as well

view this post on Zulip Anthony Bullard (Jan 02 2025 at 16:51):

I would hate it if we enforced a separation of types and values long-term

view this post on Zulip Sam Mohr (Jan 02 2025 at 16:54):

it would be

pub enum Statement {
    Alias(Pattern, Type),
    Import(Module, Vec<Exposes>),
    ImportFile(Path, Type),
    Assignment(Pattern, Expression),
    Return(Expression),
    Dbg(Expression),
    Bare(Expression),
}

pub struct Module {
    header: Header,
    defs: Vec<Statement>,
}

pub enum Expression {
    Function {
        args: Vec<Args>,
        body: Vec<Statement>,
    },
    Value ...
}

view this post on Zulip Sam Mohr (Jan 02 2025 at 16:58):

And then canonicalization would block Dbg and Return outside of functions

view this post on Zulip Anthony Bullard (Jan 02 2025 at 16:59):

What about annotated defs?

view this post on Zulip Anthony Bullard (Jan 02 2025 at 17:00):

Is that just an Alias followed by a Bare?

view this post on Zulip Sam Mohr (Jan 02 2025 at 17:00):

You either during parsing combine Alias followed by Bare without anything between (like comments or newlines) into a AnnotatedAssignment

view this post on Zulip Sam Mohr (Jan 02 2025 at 17:01):

Or you do that in can

view this post on Zulip Anthony Bullard (Jan 02 2025 at 17:02):

Yeah, that's reasonble

view this post on Zulip Sam Mohr (Jan 02 2025 at 17:02):

All that said, it means that generic "statements" are like 99% of a module

view this post on Zulip Anthony Bullard (Jan 02 2025 at 17:03):

I'd probably collapse Return and Dbg into a single case where the first item would be a enum of {Return, Dbg} but yeah, this is good

view this post on Zulip Anthony Bullard (Jan 02 2025 at 17:04):

And we'd also have to enforce imports at the top

view this post on Zulip Anthony Bullard (Jan 02 2025 at 17:04):

Unless we want to parse the entire file to find dependencies

view this post on Zulip Sam Mohr (Jan 02 2025 at 17:04):

I don't think so, but I'd be okay with that

view this post on Zulip Anthony Bullard (Jan 02 2025 at 17:04):

I'd be ok with it if we get local imports and import destructures :wink:

view this post on Zulip Anthony Bullard (Jan 02 2025 at 17:05):

But I don't think that works with ImportFile

view this post on Zulip Anthony Bullard (Jan 02 2025 at 17:05):

Unless it's replaced with a binding of a local variable to a module-level constant

view this post on Zulip Sam Mohr (Jan 02 2025 at 17:07):

I'm working on the can rewrite, and it entails us first compiling modules in silos for maximum cache friendliness. We need to go find all imports even within blocks anyway for that to do its job, so I don't think it's a problem if we allow nested (file) imports

view this post on Zulip Sam Mohr (Jan 08 2025 at 22:57):

It seems like @Anthony Bullard took away that we were gonna do dbg func(arg1, arg2), but I thought the takeaway was that we'd do dbg(func(arg1, arg2)). Which are we going for? The latter seems more consistent

view this post on Zulip Sam Mohr (Jan 08 2025 at 22:57):

And I presume Anthony and Josh would agree that it makes parsing simpler in addition

view this post on Zulip Luke Boswell (Jan 08 2025 at 22:58):

I thought it was the PNC version too

view this post on Zulip Sam Mohr (Jan 08 2025 at 22:58):

We are making crash a function, so only dbg is left as a "function" that uses whitespace. return uses whitespace, but it's different since it can only start an expression

view this post on Zulip Luke Boswell (Jan 08 2025 at 22:59):

Is there like a statement version that uses whitespace, and an inline expression version that is a function and just passes the value through?

view this post on Zulip Brendan Hansknecht (Jan 08 2025 at 23:01):

I would argue at this point that they are clearly keywords/intrinsics and not functions

view this post on Zulip Brendan Hansknecht (Jan 08 2025 at 23:01):

As such, none of the should use function calling syntax

view this post on Zulip Brendan Hansknecht (Jan 08 2025 at 23:01):

Instead they should use the space calling syntax and be distinct.

view this post on Zulip Brendan Hansknecht (Jan 08 2025 at 23:02):

So crash "my str" return abc and dbg some_fn(123)

view this post on Zulip Sam Mohr (Jan 08 2025 at 23:02):

A few points against that:

view this post on Zulip Sam Mohr (Jan 08 2025 at 23:03):

But I'm okay with it so long as crash and dbg act the same

view this post on Zulip Richard Feldman (Jan 08 2025 at 23:03):

hm, why are we having crash use parens? :face_with_raised_eyebrow:

view this post on Zulip Sam Mohr (Jan 08 2025 at 23:03):

Maybe I misread the code

view this post on Zulip Richard Feldman (Jan 08 2025 at 23:03):

I agree with Brendan, I think all of these should be like return or try or catch or throw in other languages

view this post on Zulip Richard Feldman (Jan 08 2025 at 23:03):

as in, no parens

view this post on Zulip Brendan Hansknecht (Jan 08 2025 at 23:04):

For me, the main issue is that they don't fully act like functions. You have to wrap them in a lambda to pass them into another function.

view this post on Zulip Anthony Bullard (Jan 08 2025 at 23:04):

I just wish they were parsed solely as expressions

view this post on Zulip Richard Feldman (Jan 08 2025 at 23:04):

I think it's ok if we have both dbg foo and foo.dbg()

view this post on Zulip Anthony Bullard (Jan 08 2025 at 23:04):

That would simplify the grammar a lot

view this post on Zulip Richard Feldman (Jan 08 2025 at 23:04):

:thinking: although...if we have static dispatch, is there a reason we couldn't just always make it be foo.dbg()?

view this post on Zulip Anthony Bullard (Jan 08 2025 at 23:05):

What about for non-nominal types?

view this post on Zulip Richard Feldman (Jan 08 2025 at 23:05):

we infer it

view this post on Zulip Anthony Bullard (Jan 08 2025 at 23:05):

That would REALLY lead people to Nominal-land

view this post on Zulip Richard Feldman (Jan 08 2025 at 23:05):

just like we do for .equals

view this post on Zulip Anthony Bullard (Jan 08 2025 at 23:05):

Ah

view this post on Zulip Richard Feldman (Jan 08 2025 at 23:05):

yeah we already infer a ton of things :big_smile:

view this post on Zulip Richard Feldman (Jan 08 2025 at 23:05):

.inspect too

view this post on Zulip Anthony Bullard (Jan 08 2025 at 23:05):

Man, I just don't love the magical intrinsic methods

view this post on Zulip Anthony Bullard (Jan 08 2025 at 23:05):

But i'm just one human

view this post on Zulip Richard Feldman (Jan 08 2025 at 23:06):

well I think it's essential that { ... } == { ... } works

view this post on Zulip Richard Feldman (Jan 08 2025 at 23:06):

among other things

view this post on Zulip Richard Feldman (Jan 08 2025 at 23:06):

and inferred methods seems like the obvious way to achieve that, especially if we want nominal types to be able to customize how == works on them

view this post on Zulip Richard Feldman (Jan 08 2025 at 23:07):

and it's not just equality, but also hashing (e.g. wanting to be able to use structural records, tags, and tuples as keys in dictionaries and sets)

view this post on Zulip Sam Mohr (Jan 08 2025 at 23:07):

and encoding/decoding

view this post on Zulip Anthony Bullard (Jan 08 2025 at 23:08):

I guess that I don't like that now I have to know "if i see .equals() it is a compiler inferred implementation.....or maybe not...."

view this post on Zulip Richard Feldman (Jan 08 2025 at 23:09):

:thinking: what's the difference between that and any other method?

view this post on Zulip Richard Feldman (Jan 08 2025 at 23:10):

like anytime you see .equals() you have to know the type of the thing before the . to know where its implementation lives

view this post on Zulip Richard Feldman (Jan 08 2025 at 23:10):

and if you know its type is nominal, then you know it's in a module somewhere, but if you know it's structural, then you know it has to be compiler-inferred because there's no module where arbitrary structural records are defined :big_smile:

view this post on Zulip Anthony Bullard (Jan 08 2025 at 23:11):

But if it's compiler derived normally, I might just assume there is no custom implementation

view this post on Zulip Richard Feldman (Jan 08 2025 at 23:12):

I see

view this post on Zulip Richard Feldman (Jan 08 2025 at 23:12):

I think that would only be true of dbg

view this post on Zulip Anthony Bullard (Jan 08 2025 at 23:13):

And equals

view this post on Zulip Richard Feldman (Jan 08 2025 at 23:13):

oh equals you can for sure override!

view this post on Zulip Richard Feldman (Jan 08 2025 at 23:13):

so that you can customize how equality works e.g. for a user-defined collection like a tree data structure where you don't want equality to care about insertion order

view this post on Zulip Agus Zubiaga (Jan 08 2025 at 23:47):

I vote for only having .dbg()— that works everywhere.
The only caveat is that we have to allow it as a statement even if the value is not {}/().

So you can do:


result.dbg()

view this post on Zulip Agus Zubiaga (Jan 08 2025 at 23:48):

Currently that’s only allowed if the function is effectful

view this post on Zulip Agus Zubiaga (Jan 08 2025 at 23:48):

I mean, it’s allowed, but it triggers a warning

view this post on Zulip Sam Mohr (Jan 08 2025 at 23:51):

Presumably, we can desugar that to dbg(expr), and for top-level pure stmts check if it's an Apply(Dbg, ...) and not emit the warning. Should be simple to support

view this post on Zulip Sam Mohr (Jan 08 2025 at 23:52):

I like the idea of only having one way to do things, but in this case dbg expr seems like it reads forward a lot better

view this post on Zulip Sam Mohr (Jan 08 2025 at 23:53):

So it would be a downgrade to always read the dbg after the expression we're debugging

view this post on Zulip Anthony Bullard (Jan 08 2025 at 23:58):

I think the warning should only be if the return type is NOT Unit

view this post on Zulip Anthony Bullard (Jan 08 2025 at 23:58):

And then problem solved

view this post on Zulip Sam Mohr (Jan 08 2025 at 23:58):

Not quite for dbg

view this post on Zulip Sam Mohr (Jan 08 2025 at 23:58):

That's true for everything else

view this post on Zulip Anthony Bullard (Jan 08 2025 at 23:59):

Or can’t we have an exception if the func is Dbg?

view this post on Zulip Sam Mohr (Jan 08 2025 at 23:59):

That's the idea, yep

view this post on Zulip Anthony Bullard (Jan 08 2025 at 23:59):

dbg returns a?

view this post on Zulip Sam Mohr (Jan 09 2025 at 00:00):

Yep, so you can do func(dbg arg)

view this post on Zulip Anthony Bullard (Jan 09 2025 at 00:00):

I think right now I’ve worked on the compiler for more hours than I’ve written Roc code :cry:

view this post on Zulip Sam Mohr (Jan 09 2025 at 00:00):

I'm DEFINITELY in that boat

view this post on Zulip Sam Mohr (Jan 09 2025 at 00:00):

It's a living

view this post on Zulip Sam Mohr (Jan 09 2025 at 00:00):

You can work on libraries any time, we need plenty of those

view this post on Zulip Sam Mohr (Jan 09 2025 at 00:01):

It would be a two step check for whether a pure statement is allowed:

view this post on Zulip Agus Zubiaga (Jan 09 2025 at 00:04):

Yeah, so for the purposes of the warning, we would treat dbg as effectful

view this post on Zulip Agus Zubiaga (Jan 09 2025 at 00:04):

because it kind of is

view this post on Zulip Sam Mohr (Jan 09 2025 at 00:06):

But only at the top-level, I presume. Since Str.repeat("abc", 3.dbg()) would be doing work outside of the thing it debug printed

view this post on Zulip Agus Zubiaga (Jan 09 2025 at 00:12):

Yeah, but that’s the same rule for effectful fns

view this post on Zulip Sam Mohr (Jan 09 2025 at 00:16):

I don't think I agree. I thought that:

  1. any effectful function call in an expression made the whole expression effectful
  2. any effectful function call in a function body required the containing function to be effectful
  3. any effectful function call in a statement made the statement effectful

view this post on Zulip Sam Mohr (Jan 09 2025 at 00:17):

But func(arg1, val.dbg()) wouldn't work like 3 because the func(arg1, part isn't "debugful"

view this post on Zulip Sam Mohr (Jan 09 2025 at 00:17):

I'm sure I'm missing something here

view this post on Zulip Sam Mohr (Jan 09 2025 at 00:17):

Not sure how important it is for you to explain it to me as long as the implementation works :smile:

view this post on Zulip Brendan Hansknecht (Jan 09 2025 at 00:26):

Personally I prefer dbg to generally be at the beginning of the line so I don't have to see:

"some really long message that feels out of place with the quote at the beginning of the line: ${var.to_str()}".dbg()

view this post on Zulip Sam Mohr (Jan 09 2025 at 00:27):

Same

view this post on Zulip Sam Mohr (Jan 09 2025 at 00:27):

It's why we at least need dbg expr if not also expr.dbg()

view this post on Zulip Agus Zubiaga (Jan 09 2025 at 00:33):

oh, I didn’t mean we would actually consider dbg effectful like in the type system, but only for the purposes of the discarded result in statement warning where I think would work the same.

view this post on Zulip Sam Mohr (Jan 09 2025 at 00:35):

I agree with that, I just think that you can't make all dbg calls as effectful for that to work since only the top one should get that tracking

view this post on Zulip Sam Mohr (Jan 09 2025 at 00:36):

Since I don't think we should allow Str.repeat("abc", 3.dbg()) as a pure statement unless the top-level value was debugged

view this post on Zulip Agus Zubiaga (Jan 09 2025 at 00:37):

Str.repeat("abc”, get_count!()) is effectful but would still produce the warning

view this post on Zulip Agus Zubiaga (Jan 09 2025 at 00:37):

Because you’re discarding the result of repeat

view this post on Zulip Sam Mohr (Jan 09 2025 at 00:37):

Oh, great!

view this post on Zulip Agus Zubiaga (Jan 09 2025 at 00:37):

So I think it’s the same

view this post on Zulip Sam Mohr (Jan 09 2025 at 00:37):

Didn't know that was the case

view this post on Zulip Sam Mohr (Jan 09 2025 at 00:37):

If that's the case, then we're good

view this post on Zulip Richard Feldman (Jan 09 2025 at 02:46):

Brendan Hansknecht said:

Personally I prefer dbg to generally be at the beginning of the line so I don't have to see:

"some really long message that feels out of place with the quote at the beginning of the line: ${var.to_str()}".dbg()

I dunno, I'm kinda skeptical that this would come up a lot in practice :sweat_smile:

view this post on Zulip Richard Feldman (Jan 09 2025 at 02:46):

I'd like to try it as just the method style and see how it goes

view this post on Zulip Richard Feldman (Jan 09 2025 at 02:46):

because we know we want that style so it can be used in pipelines

view this post on Zulip Richard Feldman (Jan 09 2025 at 02:46):

and maybe we want both, but let's see how much demand there is in practice if we have only the one way to do it

view this post on Zulip Joshua Warner (Jan 09 2025 at 05:49):

What should the precedence of dbg be?

In particular, how should dbg 4 / 2 be parsed?

That's currently (dbg 4) / 2, but if dbg is a keyword like return, I feel it should be parsed like dbg (4/2).

view this post on Zulip Joshua Warner (Jan 09 2025 at 05:51):

I ask, because I'm troubleshooting a bug the fuzzer found, where this:

dbg(a/a)
d

is formatted as this:

dbg a / a
d

... which of course is parsed differently.

view this post on Zulip Joshua Warner (Jan 09 2025 at 05:52):

I can have the formatter add parens like this fairly easily:

dbg (a / a)
d

... but when I read the above thread, it occurred to me that we may want to change the precedence of dbg anyway.

view this post on Zulip Sam Mohr (Jan 09 2025 at 06:04):

Secret option: we parse dbg in two ways

view this post on Zulip Sam Mohr (Jan 09 2025 at 06:04):

As the start of a statement, or as a method call

view this post on Zulip Sam Mohr (Jan 09 2025 at 06:04):

Then the precedence question goes away

view this post on Zulip Sam Mohr (Jan 09 2025 at 06:05):

I guess for now we can't do that yet, so we can maybe just treat dbg as a normal function call for simplicity

view this post on Zulip Joshua Warner (Jan 09 2025 at 06:06):

We absolutely can do that now. (unless I'm missing something?)

view this post on Zulip Joshua Warner (Jan 09 2025 at 06:06):

And that's roughly the direction I'm thinking here

view this post on Zulip Sam Mohr (Jan 09 2025 at 06:06):

I don't think we should focus on keeping it a space call even within expressions at this time

view this post on Zulip Joshua Warner (Jan 09 2025 at 06:07):

Hmm, can you more explicitly state what you think we _should_ focus on?

view this post on Zulip Sam Mohr (Jan 09 2025 at 06:07):

Sure

view this post on Zulip Joshua Warner (Jan 09 2025 at 06:07):

(or just, what you think we should do?)

view this post on Zulip Sam Mohr (Jan 09 2025 at 06:08):

My goal is to subvert complexity arising from an intermediate state that we don't care about

view this post on Zulip Sam Mohr (Jan 09 2025 at 06:08):

It seems like this question is effort we don't need to put in

view this post on Zulip Sam Mohr (Jan 09 2025 at 06:08):

Unless you really think so

view this post on Zulip Sam Mohr (Jan 09 2025 at 06:09):

My vote would be:

view this post on Zulip Sam Mohr (Jan 09 2025 at 06:09):

Copy the return <expr> parser

view this post on Zulip Sam Mohr (Jan 09 2025 at 06:09):

Make a dbg one

view this post on Zulip Sam Mohr (Jan 09 2025 at 06:09):

And otherwise allow treating dbg like a function for now

view this post on Zulip Sam Mohr (Jan 09 2025 at 06:09):

With parens

view this post on Zulip Joshua Warner (Jan 09 2025 at 06:10):

I think we're way more on the same page than you think we are

view this post on Zulip Sam Mohr (Jan 09 2025 at 06:10):

Oh, great

view this post on Zulip Sam Mohr (Jan 09 2025 at 06:11):

I'm distracted with choir stuff right now, I'll stop trying to multitask

view this post on Zulip Joshua Warner (Jan 09 2025 at 06:11):

Haha np

view this post on Zulip Brendan Hansknecht (Jan 09 2025 at 18:10):

Richard Feldman said:

Brendan Hansknecht said:

Personally I prefer dbg to generally be at the beginning of the line so I don't have to see:

"some really long message that feels out of place with the quote at the beginning of the line: ${var.to_str()}".dbg()

I dunno, I'm kinda skeptical that this would come up a lot in practice :sweat_smile:

I do this fairly often when working on roc code. Sometimes I need more context than just the variable. Or want to print multiple variables in a single dbg statement.


Last updated: Jun 16 2026 at 16:19 UTC