Stream: ideas

Topic: conditional dbg


view this post on Zulip Oskar Hahn (Jan 12 2025 at 08:37):

This topic relates to #ideas > Calling style for special functions like `dbg` and `crash`

I often happens, that i want to do a dbg-statement, but only conditionally. For example, if I am deep inside a recursive function and printing the value on each step would be to much output. For example:

myfunc = \arg, deep ->
  if deep == 0 then
    arg
  else
      # dbg arg but only if deep%100 == 0
     myfunc(doStuff(arg), deep - 1)

My current workaround looks like this:

myfunc = \arg, deep ->
  if deep == 0 then
    arg
  else
      _ =
        if deep % 100 == 0 then
          dbg arg
          42
        else
          42
     myfunc(doStuff(arg), deep - 1)

This is not nice. I don't want to return a value, but I have to. I don't want to have an else branch, but I have to. And I get a compiler warning for not using the value.

It would be nice, if dbg arg could work like an effectful function that returns {}. In the thread linked above, the term debugged was used.

So what I would like to work is:

myfunc = \arg, deep ->
  if deep == 0 then
    arg
  else
      if deep % 100 == 0 then
        dbg arg
     myfunc(doStuff(arg), deep - 1)

Maybe the same should be possible for crash and return.

view this post on Zulip Sam Mohr (Jan 12 2025 at 08:44):

Oh, that's an idea! We could have crash <expr>, dbg <expr>, and return <expr> all be statements that evaluate to an unbound type

view this post on Zulip Sam Mohr (Jan 12 2025 at 08:45):

And then during type unification, they would solve to the unit type (used to be {}, will soon be ())

view this post on Zulip Sam Mohr (Jan 12 2025 at 08:45):

So dbg <expr> returns an unbound type, but method .dbg() returns the value it was called on

view this post on Zulip Sam Mohr (Jan 12 2025 at 08:46):

I think we should also consider making crash a statement like return instead of a keyword in an expression context

view this post on Zulip Sam Mohr (Jan 12 2025 at 08:47):

It would mean that the only "special functions" we have are all statements that are "word + space + single expr"

view this post on Zulip Sam Mohr (Jan 12 2025 at 08:47):

And as you point out, this pairs very well with else-less if

view this post on Zulip Anthony Bullard (Jan 12 2025 at 11:48):

I'm a fan of this

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

yeah I guess having a separate dbg keyword from .dbg() does simplify the types

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

I'm game! :+1:

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

regarding crash and return, I think as long as ?? crash ... and ?? return ... work, that sounds good

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

definitely foo(arg1, crash ...) doesn't make sense and the compiler should tell you it doesn't make sense, and making crash work like return seems like a good way to do that

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

after all, they both immediately exit the function :big_smile:

view this post on Zulip Brendan Hansknecht (Jan 12 2025 at 17:30):

Richard Feldman said:

definitely foo(arg1, crash ...) doesn't make sense and the compiler should tell you it doesn't make sense, and making crash work like return seems like a good way to do that

I could see someone doing that in practice short term. fn(arg1, crash "Todo: figure out this arg")

Not saying we need to support it though...can just do:

arg2 = crash "Todo: figure out this arg"
fn(arg1, arg2)

view this post on Zulip Richard Feldman (Jan 12 2025 at 17:52):

I think it's best for the compiler to tell you not to do it, because otherwise you might be unpleasantly surprised at what happens if you do .with_default(crash ...) :sweat_smile:

view this post on Zulip Brendan Hansknecht (Jan 12 2025 at 18:21):

Cause it isn't a lazy crash?

view this post on Zulip Sam Mohr (Jan 12 2025 at 20:50):

Cause the ... will crash before... crash crashes...

view this post on Zulip Richard Feldman (Jan 12 2025 at 20:58):

yeah since Roc is strictly evaluated, all arguments to functions get evaluated before the function does, so the fact that with_default only conditionally returns the value doesn't matter, because with_default never even gets called due to the crash happening first

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

Yep

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

In my mind that is the normal for essentially all languages, so it doesn't matter

view this post on Zulip Brendan Hansknecht (Jan 12 2025 at 21:03):

with_default would have to take a lambda for it to work otherwise

view this post on Zulip Brendan Hansknecht (Jan 12 2025 at 21:03):

So not really surprising.

view this post on Zulip Brendan Hansknecht (Jan 12 2025 at 21:03):

Oh, I guess surprising with ??

view this post on Zulip Brendan Hansknecht (Jan 12 2025 at 21:03):

But not with the raw function call

view this post on Zulip Brendan Hansknecht (Jan 12 2025 at 21:03):

Cause ?? feels like it should be lazy.

view this post on Zulip Brendan Hansknecht (Jan 12 2025 at 21:04):

Kinda like else unreachable or else panic in zig

view this post on Zulip Richard Feldman (Jan 12 2025 at 21:06):

yeah with ?? it works fine

view this post on Zulip Richard Feldman (Jan 12 2025 at 21:07):

because ?? is lazy

view this post on Zulip Richard Feldman (Jan 12 2025 at 21:07):

it desugars to a when, not to a function call

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

so both ?? crash ... and ?? return ... actually do work

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

because they're desugaring into putting the right hand side inside the desugared when's Err(_) -> branch

view this post on Zulip Brendan Hansknecht (Jan 12 2025 at 21:10):

Oh cool. Them everything works as I would expect including using crash as a function arg.

view this post on Zulip Richard Feldman (Jan 12 2025 at 21:25):

but yeah I think if someone did write that by mistake, the compiler telling them about it would be helpful

view this post on Zulip Brendan Hansknecht (Jan 12 2025 at 21:28):

Sure. Though, it would just be a warning, so I would expect the warning to pop up at the same time that I roc ... and hit the crash anyway.

view this post on Zulip Sam Mohr (Jan 12 2025 at 21:28):

I think if someone writes

email = user_emails.get_or(id, crash)

That should be a warning (but still run). They should need to write

email = user_emails.get(id) ??
    crash "reason"

view this post on Zulip Ayaz Hafiz (Jan 13 2025 at 02:53):

this shouldn't be a special case warning. it should just be an extension of the unreachable code checking to see that crash passed as an argument is unreachable.

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

@Ayaz Hafiz I'd go a step further and suggest that crash should be a keyword that can only be used as a special type of statement, not as an expression. And you'd get a runtime error that says "you can't use crash as an expression, it's for statements!" which would produce the desired effect of "crashing" early

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:07):

This would fall out of crash being in the KEYWORDS list

view this post on Zulip Ayaz Hafiz (Jan 13 2025 at 03:11):

im confused by this. what is the difference between a statement and expression? isn't foo ?? crash ... an expression including on the LHS and RHS of ???

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

before desugaring it would be an expression, yeah

view this post on Zulip Ayaz Hafiz (Jan 13 2025 at 03:21):

why is it not an expression after?

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:29):

I wasn't being pedantic enough, maybe

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:30):

Code blocks that are on the same line as the preceding operator or whatnot are just expressions

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:30):

But once they go to a newline, they become lists of statements

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:31):

Which I believe is called a block

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:31):

When you type foo ?? crash "message"

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:31):

That would not be valid in the Roc I'm thinking of

view this post on Zulip Ayaz Hafiz (Jan 13 2025 at 03:32):

why not? is that different from the warning you suggest above?

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:32):

Since it represents

binop:
    operator: "??"
    left: var "foo"
    right: function call:
        function: "crash"
        args: ["message"]

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:32):

Or something like that

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:33):

What I typed was

binop:
    operator: "??"
    left: var "foo"
    right: block:
        statements:
        - crash statement:
            message: "message"

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:34):

Meaning that there is a type of "statement" called a crash statement

view this post on Zulip Ayaz Hafiz (Jan 13 2025 at 03:34):

sorry, im confused on the semantic difference statements vs expressions provide

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:34):

Stmt {
    Crash(Loc<Expr>),
    Dbg(Loc<Expr>),
    Assignment(Loc<Pattern>, Loc<Expr>),
    Plain(Loc<Expr>),
}

view this post on Zulip Ayaz Hafiz (Jan 13 2025 at 03:35):

what is the purpose of that distinction though? Couldn't those all be expressions instead?

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:35):

I'll try to think of a good explanation, one second

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:36):

I think it helps us manage ambiguity in a Whitespace Significant Syntax

view this post on Zulip Ayaz Hafiz (Jan 13 2025 at 03:37):

gotcha that makes sense during parsing

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:37):

func = |left, right|
    x = Stdin.line_with_prompt(left)!
    y =
    Stdin.line_with_prompt(right)!

    Ok(x.concat(y))

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:37):

Yeah, it's all for parsing

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:39):

Is that second line_with_prompt! call supposed to be assigned to y?

view this post on Zulip Ayaz Hafiz (Jan 13 2025 at 03:39):

it might be simpler to get rid of the distinction after parsing. im not sure this affords much after that. im not sure there is a good semantic difference between an expression and statement in the language like there is in most.

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:39):

After parsing we get rid of the distinction for sure

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:39):

It just becomes either Let { definitions: List<Definition>, result: Expr } or just Expr

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:40):

Including Expr::Crash { message: Expr } and Expr::Return { result: Expr } for early returns

view this post on Zulip Ayaz Hafiz (Jan 13 2025 at 03:40):

okay that makes sense to me

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:41):

So I've been ignoring you type value ?? crash "message" because it'd need to be

value ??
    crash "message"

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:41):

Minor parsing thing

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:41):

Same page

view this post on Zulip Ayaz Hafiz (Jan 13 2025 at 03:42):

sorry now im confused again

view this post on Zulip Ayaz Hafiz (Jan 13 2025 at 03:42):

why are those different

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:42):

Because we parse expressions and statements differently

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:42):

A statement can be { x, y } = EXPR

view this post on Zulip Ayaz Hafiz (Jan 13 2025 at 03:43):

ok so its an implementation bug that they're different? or intentionally

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:43):

But an expression can't start with an assignment, for one example

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:43):

I'd say it's a feature that comes from preferring a simpler grammar

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:44):

Though it could make our language more expressive to allow crash "message" as an expression,

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:44):

It would mean that with the new style of PNC, there are random things that are space-delimited instead

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:44):

Like maybe dbg and crash

view this post on Zulip Ayaz Hafiz (Jan 13 2025 at 03:44):

does this not work? i feel like i have written this code before

when foo is
  Ok x -> x
  Err _ -> crash "unreachable"

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:45):

That still works because we currently support two syntaxes at the cost of complexity

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:45):

To put it bluntly, I want our language to become less expressive so that it's easier to parse, and to understand as a user how it's parsed

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:45):

We currently allow parens AND spaces for function (and keyword) calls

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:45):

But it'd be nice to say only PNC is used for everything

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:46):

EXCEPT

view this post on Zulip Ayaz Hafiz (Jan 13 2025 at 03:46):

sure, i get that and it seems prudent to remove that

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:46):

For statements, which only have to say "check for these 3 keywords, otherwise do the same basic stuff"

view this post on Zulip Ayaz Hafiz (Jan 13 2025 at 03:46):

i don't understand why

value ??
    crash "message"

and

value ?? crash "message"

are different - it seems like a different in formatting. Like does

value ??
  someOtherValue

not parse then?

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:48):

The first one is a ?? binop expression where the left arg is a var, and the right arg is a block with one "crash statement"

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:48):

(in my proposed Roc)

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:49):

The second one is a ?? binop expression where the left arg is a var, and the right arg is an expression with a weird looking space-delimited function call

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:49):

To a function called crash that takes a single Str for an arg

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:49):

The third parses today and in the future as a ?? binop expression where the left arg is the value var and the right arg is a block with a single expression that is a plain someOtherValue var

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:50):

And as an aside, the last plain statement in a block is what's returned from the block

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:51):

let x = if value.is_ok() then value else {
    Ok(someValue)
}

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:51):

Is another way to conceive of that last one

view this post on Zulip Ayaz Hafiz (Jan 13 2025 at 03:52):

i think i understand in principle the advantage of "block" so that you can easily interpret a sequence like

dbg foo
crash ""
1

that makes sense to me. But I don't follow why the indentation of crash should be semantically significant. Would it easier to make a block a list of expressions (no statement concept in the AST) and then crash is an expression you handle like everything else?

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:53):

In WSS languages like Roc, indentation generally means the same as a brace block in a C-style language. It isn't crash, it's the indentation

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:54):

The reason why I think crash should only be a statement and not an expression is so that the parser doesn't have to check for spaces after the first word of each statement

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:54):

Fewer cases to handle

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:54):

We can do it, but if we don't have to, then Roc is simpler

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:56):

This is literally all a simplify parsing thing that affects nothing once we get to and get past canonicalization

view this post on Zulip Sam Mohr (Jan 13 2025 at 03:58):

My perception here is also aided by a familiarity with how our parser currently works

view this post on Zulip Ayaz Hafiz (Jan 13 2025 at 03:59):

does it not need to check for spaces regardless for error tolerance? the thing that confuses me is that crash "" is semantically an expression (at least to my understanding) - it has a result value, unlike dbg "" or x = .... so it's hard for me to understand why crash is different

view this post on Zulip Ayaz Hafiz (Jan 13 2025 at 03:59):

yeah i understand that this just might be implementation stuff

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

Where this is an Expr and this is a ValueDef, which is roughly a statement

view this post on Zulip Sam Mohr (Jan 13 2025 at 04:01):

When you say "semantically"

view this post on Zulip Ayaz Hafiz (Jan 13 2025 at 04:01):

right, crash is an expression there

view this post on Zulip Sam Mohr (Jan 13 2025 at 04:01):

But in the future, I want it to only be part of ValueDef and not in Expr, until can::Expr

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

I think dbg should also not be in parse::Expr

view this post on Zulip Ayaz Hafiz (Jan 13 2025 at 04:02):

if the concept of statements/ValueDef has "plain" expressions, what is the value of the difference?

view this post on Zulip Sam Mohr (Jan 13 2025 at 04:03):

Without getting too into the details of why, I'd say it helps us avoid wasting work in parsing

view this post on Zulip Ayaz Hafiz (Jan 13 2025 at 04:03):

gothca

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

Go team

view this post on Zulip Ayaz Hafiz (Jan 13 2025 at 04:05):

do you have a diff or something that shows the wasted work? just as a user i would find it confusing that i have to write

when x is
  Ok y -> y
  Err ->
    crash ""

or in general force indentation without understanding this "blocks" concept, which is an implementation detail iiuc

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

Yeah, I guess it is an implementation detail

view this post on Zulip Ayaz Hafiz (Jan 13 2025 at 04:06):

i get it for x = y because that has to be followed by a value and there is no way to have multple values on a single line in Roc but I don't get it for crash

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

I think that

when x is
    Ok y -> y
    Err _err -> crash "message"

view this post on Zulip Sam Mohr (Jan 13 2025 at 04:07):

Would be nice to allow

view this post on Zulip Sam Mohr (Jan 13 2025 at 04:07):

Same with return and dbg

view this post on Zulip Sam Mohr (Jan 13 2025 at 04:07):

I'm thinking about

view this post on Zulip Sam Mohr (Jan 13 2025 at 04:08):

func.call(arg1, func2(|arg3| arg3 + 1, crash "abc", arg4))

view this post on Zulip Sam Mohr (Jan 13 2025 at 04:08):

Roc used to be a very strongly whitespace-important language

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

But now it has two general camps: whitespace and brackets

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

And I think whitespace is still the right tool for demarking control flow

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

But parens are the right tool in modern Roc for demarking prodecure usage

view this post on Zulip Sam Mohr (Jan 13 2025 at 04:10):

So crash being "shaped" like a procedure is bad to me

view this post on Zulip Ayaz Hafiz (Jan 13 2025 at 04:10):

right, in that func.call case it seems strange that i have to break it up into multiple lines and force indentation - i get it now with the blocks concept but that seems like a hard thing to learn. Like especially if I want to write crash "todo" while i figure out what to put there

view this post on Zulip Sam Mohr (Jan 13 2025 at 04:10):

Since it's reeeeally control flow in my eyes, being an early return of sorts

view this post on Zulip Ayaz Hafiz (Jan 13 2025 at 04:11):

i don't think crash is control flow

view this post on Zulip Sam Mohr (Jan 13 2025 at 04:11):

It is and it isn't

view this post on Zulip Sam Mohr (Jan 13 2025 at 04:12):

It acts like a break

view this post on Zulip Sam Mohr (Jan 13 2025 at 04:12):

But with TNT

view this post on Zulip Ayaz Hafiz (Jan 13 2025 at 04:14):

yeah but you don't really use it for control flow - like using for control flow would be exceptions, which this is not. I think in practice crash is saying "there is a value here", in the same way unwrap or assert is saying "this value exists, my logic is cannot be expressed in the type system". Or you treat it as a placeholder for a value

view this post on Zulip Sam Mohr (Jan 13 2025 at 04:15):

You're right that you don't use it as a tool that subverts control flow for the purpose of running your application

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

But you do use it to say "I want to fail early by pulling the plug as soon as we get here, since we should never be here in the first place"

view this post on Zulip Ayaz Hafiz (Jan 13 2025 at 04:16):

yeah agreed

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

I want stuff that acts special to look special

view this post on Zulip Ayaz Hafiz (Jan 13 2025 at 04:17):

i think it would be really unfortunate if

func.call(arg1, func2(|arg3| arg3 + 1, crash "abc", arg4))

was not legal. it just seems like a user burden

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

Function calls should look boring

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

Well, if I'm reading over code, I want to see the crashes obviously

view this post on Zulip Sam Mohr (Jan 13 2025 at 04:18):

That looks hidden to me

view this post on Zulip Sam Mohr (Jan 13 2025 at 04:18):

Especially in a safe language like Roc, you should never need it

view this post on Zulip Sam Mohr (Jan 13 2025 at 04:18):

So if you need it, it should be obvious that you're using it

view this post on Zulip Sam Mohr (Jan 13 2025 at 04:18):

Though if you don't agree with that principle, then I see your argument

view this post on Zulip Sam Mohr (Jan 13 2025 at 04:19):

We had a discussion on this in #ideas > Calling style for special functions like `dbg` and `crash`

view this post on Zulip Ayaz Hafiz (Jan 13 2025 at 04:20):

if you never needed it the feature shouldn't exist - but it was introduced because there was a user need. maybe it should be removed then?

view this post on Zulip Ayaz Hafiz (Jan 13 2025 at 04:20):

actually wait maybe here's a better example of what i'm getting at

view this post on Zulip Ayaz Hafiz (Jan 13 2025 at 04:21):

my_crash = |s|
  crash s

foo(my_crash(s))

foo(
  crash s
)

it seems weird to me that one of these doesn't require indentation but the other does

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

Ayaz Hafiz said:

i think it would be really unfortunate if

func.call(arg1, func2(|arg3| arg3 + 1, crash "abc", arg4))

was not legal. it just seems like a user burden

legal as in accepted by the parser?

(but still gives you a warning in a later phase)

view this post on Zulip Ayaz Hafiz (Jan 13 2025 at 04:23):

yes, accepted by the parser

view this post on Zulip Ayaz Hafiz (Jan 13 2025 at 04:23):

should definitely give a warning, but the warning should be semantic (determined by the typechecker) not syntactic, IMO

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

that makes sense to me

view this post on Zulip Richard Feldman (Jan 13 2025 at 04:24):

I think the same argument could be made for return

view this post on Zulip Sam Mohr (Jan 13 2025 at 04:24):

I mean, that's the beauty of purity inference, isn't it?

view this post on Zulip Richard Feldman (Jan 13 2025 at 04:24):

like "hey I understand what you're trying to do here, but this isn't the right place to do it"

view this post on Zulip Richard Feldman (Jan 13 2025 at 04:24):

as opposed to "I don't even understand what you're trying to do" (parsing error)

view this post on Zulip Ayaz Hafiz (Jan 13 2025 at 04:24):

yeah makes sense for return too. or even standalone dbg

view this post on Zulip Sam Mohr (Jan 13 2025 at 04:24):

It's amazing to know when someone somewhere in the call stack is doing something effectful. I wish we had that for other stuff as well, but we already have ! for effectfulness

view this post on Zulip Sam Mohr (Jan 13 2025 at 04:25):

Something something algebraic effects...

view this post on Zulip Sam Mohr (Jan 13 2025 at 04:25):

But if we can get by with just PI, that'd be much simpler to read and learn


Last updated: Jun 16 2026 at 16:19 UTC