Stream: beginners

Topic: What does the Roc community mean by "effect"?


view this post on Zulip Jake Brownson (Aug 11 2026 at 18:42):

As a Haskell thinker Maybe/Either/->/List/State (as in pure Haskell State)/ST/etc can all be effects in addition to IO. All of those effects are pure in the sense that you can "run" them w/o resulting in an IO. I get the sense from when Richard talks about effects as meaning specifically Haskell IO. Does the Roc community use the term side-effect differently than effect? (I use them interchangeably)

From a Haskell perspective I would say that Roc basically has a fixed monad stack that has Try and State (maybe others?), and each -> function has a built-in do for that monad stack to some degree. The Try monadic values are first class, but the state ones aren't (I think?). Making a function => adds a platform-limited version of IO to the monad stack. I'm speaking a bit loosely here, but that's the feeling I get working in Roc with a Haskell perspective.

view this post on Zulip Richard Feldman (Aug 11 2026 at 18:44):

I just mean "not pure"

view this post on Zulip Jake Brownson (Aug 11 2026 at 18:45):

hrm, what do you mean by "pure"? Is Try pure?

view this post on Zulip Jake Brownson (Aug 11 2026 at 18:45):

is pure a synonym for referentially transparent?

view this post on Zulip Jake Brownson (Aug 11 2026 at 18:46):

if I write a platform function that implements plus on floats I think it's still !, but is it not an effect?

view this post on Zulip Richard Feldman (Aug 11 2026 at 18:46):

yeah, sorry - pure as in "pure function" as in:

view this post on Zulip Richard Feldman (Aug 11 2026 at 18:46):

so Try is just data (same as Either in Haskell, except with the parameters in the opposite order)

view this post on Zulip Jake Brownson (Aug 11 2026 at 18:47):

to me Try is both data and an effect

view this post on Zulip Richard Feldman (Aug 11 2026 at 18:47):

hm I don't think it's reasonable to call it an effect

view this post on Zulip Richard Feldman (Aug 11 2026 at 18:47):

some languages with algebraic effects use effects instead of it

view this post on Zulip Richard Feldman (Aug 11 2026 at 18:47):

but effects definitely require execution, and Try is just memory

view this post on Zulip Richard Feldman (Aug 11 2026 at 18:47):

nothing gets executed

view this post on Zulip Jake Brownson (Aug 11 2026 at 18:47):

in Haskell all functions are pure, even if they return IO, the IO is a value that only has an effect when you return it from main, so even functions in IO are referentially transparent themselves

view this post on Zulip Richard Feldman (Aug 11 2026 at 18:47):

right

view this post on Zulip Jake Brownson (Aug 11 2026 at 18:48):

Richard Feldman said:

hm I don't think it's reasonable to call it an effect

Haskell programmers would pretty universally disagree

view this post on Zulip Richard Feldman (Aug 11 2026 at 18:48):

that's not true of Roc functions though

view this post on Zulip Jake Brownson (Aug 11 2026 at 18:49):

Do you understand why a Haskell programmer would say Try is an effect and disagree?

view this post on Zulip Richard Feldman (Aug 11 2026 at 18:49):

I know a lot of Haskell programmers, and I've written Haskell professionally, and this conversation is the first time I've ever heard anyone say Either is an effect :sweat_smile:

view this post on Zulip Jake Brownson (Aug 11 2026 at 18:49):

or agree it's an effect in Haskell, but isn't in Roc?

view this post on Zulip Richard Feldman (Aug 11 2026 at 18:49):

well it's named Either in Haskell

view this post on Zulip Richard Feldman (Aug 11 2026 at 18:49):

and I don't know why anyone would say Either is an effect

view this post on Zulip Richard Feldman (Aug 11 2026 at 18:49):

like it's a monad obviously

view this post on Zulip Richard Feldman (Aug 11 2026 at 18:49):

but monads and effects are of course different

view this post on Zulip Jake Brownson (Aug 11 2026 at 18:50):

yeah it models the effect of exceptions, just as in Roc, but Roc has language-level support for making them convenient, in Haskell you use do, just like you would when dealing w/ IO effects

view this post on Zulip Jake Brownson (Aug 11 2026 at 18:50):

Richard Feldman said:

but monads and effects are of course different

I don't agree w/ that

view this post on Zulip Jake Brownson (Aug 11 2026 at 18:51):

and I think most Haskell programmers wouldn't, or at least would understand why I disagree

view this post on Zulip Richard Feldman (Aug 11 2026 at 18:51):

I have certainly heard people say "monads represent effects" - which is totally reasonable

view this post on Zulip Richard Feldman (Aug 11 2026 at 18:51):

in that it's a value that represents an effect

view this post on Zulip Jake Brownson (Aug 11 2026 at 18:51):

not every instance of list is used to model effects, but you use lists to model the non-determinism effect

view this post on Zulip Richard Feldman (Aug 11 2026 at 18:52):

I dunno, I think we may be at an impasse here haha - I understand what you're saying, I just disagree both that "monads are effects" is reasonable and also separately that most Haskellers would agree that "monads are effects" (as opposed to e.g. "monads can represent effects")

view this post on Zulip Richard Feldman (Aug 11 2026 at 18:52):

anyway, to the original point - when I'm talking about effects in Roc I specifically mean "functions that are not pure functions"

view this post on Zulip Jake Brownson (Aug 11 2026 at 18:53):

I think on some level we're just arguing terms which isn't that constructive, but among the most beautiful things I've learned form Haskell is understanding the whole first class modeling of effects in general including IO

IO is basically the state monad, but with the entire universe

view this post on Zulip Richard Feldman (Aug 11 2026 at 18:53):

in other words, functions whose names end in ! and whose function types use => instead of -> before their return values

view this post on Zulip Jake Brownson (Aug 11 2026 at 18:53):

do you consider the State monad pure?

view this post on Zulip Richard Feldman (Aug 11 2026 at 18:53):

of course

view this post on Zulip Richard Feldman (Aug 11 2026 at 18:53):

all monads are pure haha

view this post on Zulip Jake Brownson (Aug 11 2026 at 18:53):

but IO isn't?

view this post on Zulip Richard Feldman (Aug 11 2026 at 18:53):

IO is pure too

view this post on Zulip Jake Brownson (Aug 11 2026 at 18:53):

a function in IO?

view this post on Zulip Jake Brownson (Aug 11 2026 at 18:53):

like is the putstrln Haskell function pure?

view this post on Zulip Richard Feldman (Aug 11 2026 at 18:54):

that's the whole point! before Wadler came up with IO, Haskell literally had to do I/O by having you build an entire program that took as input the output of the previous I/O operation and it was apparently miserable

view this post on Zulip Richard Feldman (Aug 11 2026 at 18:54):

and the whole reason they had to come up with a way to represent I/O using only pure functions was that nonstrict evaluation only works if all computations are pure

view this post on Zulip Richard Feldman (Aug 11 2026 at 18:55):

which is why unsafePerformIO is not something that's supposed to be used

view this post on Zulip Jake Brownson (Aug 11 2026 at 18:55):

if you consider a => Roc function to be an implicit do over a monad stack including IO is Roc's verison of putstrln different? is it impure?

view this post on Zulip Jake Brownson (Aug 11 2026 at 18:56):

or do you see it as fundamentally different than an implicit do over IO?

view this post on Zulip Richard Feldman (Aug 11 2026 at 18:56):

so, a previous version of Roc used Haskell's approach - all Roc functions were pure, and you could call any Roc function and be absolutely certain that no side effects were happening

view this post on Zulip Richard Feldman (Aug 11 2026 at 18:56):

one way this is obvious: if you call putStrLn 1,000 times, what happens?

view this post on Zulip Richard Feldman (Aug 11 2026 at 18:57):

in Haskell if you call it 1,000 times, nothing observable happens. You get some heap allocations and your CPU heats up a bit.

view this post on Zulip Richard Feldman (Aug 11 2026 at 18:57):

you have to actually hand the IO off to something else (e.g. main, or something that will ultimately hand it off to main) before you actually see anything in stdout

view this post on Zulip Richard Feldman (Aug 11 2026 at 18:57):

in Roc, if you call an equivalent put_str_ln! 1,000 times, you most definitely get 1,000 stdout printings

view this post on Zulip Jake Brownson (Aug 11 2026 at 18:57):

hrm, I'm failing to make a connection here, yeah totally on the same page that if IO is first class then holding an IO value is not a side effect, but a function returning IO in a do in main now produces side effects

view this post on Zulip Jake Brownson (Aug 11 2026 at 18:58):

so the IO value is a first class side effect aka effect

view this post on Zulip Richard Feldman (Aug 11 2026 at 18:58):

right, but main is what's doing that (behind the scenes, traversing the inert IO value and maybe calling pure function continuations inside it), not the function

view this post on Zulip Jake Brownson (Aug 11 2026 at 18:58):

in the same way I'd say Maybe a value is a first class effect

view this post on Zulip Jake Brownson (Aug 11 2026 at 18:58):

if you fill in a default and get rid of the maybe you've now run the effect

view this post on Zulip Richard Feldman (Aug 11 2026 at 18:58):

the distinction matters because the entire point of having the term "side effect" is to describe what happens when you call a function

view this post on Zulip Jake Brownson (Aug 11 2026 at 18:59):

this is why Haskell names things in little DSL monads run

view this post on Zulip Richard Feldman (Aug 11 2026 at 18:59):

if you call the function and the I/O happens, then a side effect happened

if you call the function and no I/O happens, then that side effect didn't happen

view this post on Zulip Richard Feldman (Aug 11 2026 at 18:59):

describing that precise distinction is the entire point of the term!

view this post on Zulip Richard Feldman (Aug 11 2026 at 18:59):

so collapsing them to say they are equivalent is to miss the point of the term imo

view this post on Zulip Jake Brownson (Aug 11 2026 at 18:59):

yes, so a function returning Maybe can have the effect of failing, if you use it in a do that failure automatically propagates like Rust/Roc's ?

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:00):

Jake Brownson said:

or do you see it as fundamentally different than an implicit do over IO?

I think you're saying you do see it as fundamentally different

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:00):

yeah for sure!

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:01):

This is where we part I guess, I don't, I don't see the IO effect as fundamentally different than Maybe/Try, all effects

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:01):

I even see a C++ function as an implicit do, my brain thinks in Haskell

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:01):

one reason I don't think it's useful to see Maybe and Either as effects is that I think it reduces to "everything is an effect, therefore the term is useless"

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:02):

like Maybe is just two alternatives, Just and Nothing, and Just holds a payload

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:02):

same with Either, except it holds 2 payloads

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:02):

no, effects ~= monad, so like Num is not an effect

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:02):

same with Bool except it holds 0 payloads

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:02):

so is Bool an effect?

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:02):

why not? it can represent failure

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:02):

some effects can be applicatives, but all applicatives are monads

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:03):

for example in my puri I have a Canvas type, basically a free "monad" that defines like "draw circle", etc. but since none of the effects I'm modeling are monadic it can just be applicative

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:03):

Bool is not a monad

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:03):

why is Bool not an effect to you?

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:03):

because it's not a -> a

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:03):

Maybe is a monad

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:04):

so your definition of effect is just another word for monad? :thinking:

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:04):

I would use those terms interchangably, it's possible there is some nuance I'm not aware of

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:04):

so since there's an identity monad, there's an "identity effect" that's the same thing?

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:04):

and technically effect is a value of a monad, not the monad itself

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:04):

to me, "identity effect" is an oxymoron :sweat_smile:

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:05):

that's an interesting question, identity things often feel pretty cheap, but the identity monad is useful if you have a thing that is parametric on monads, and you don't need anything monadic

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:05):

I could stipulate that it isn't and I don't think it changes my overall conversation here

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:06):

Is State () an effect?

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:06):

() as in unit

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:06):

again, to me values are never effects haha

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:06):

effects are performed by the CPU, they're not bytes in memory

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:06):

an IO a is a value that represents an effect in the way 2::uint is a value that represents 2, I'd say 2::uint is a number and IO a is an effect

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:07):

so I would say "writing to standard out" is an effect

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:07):

but an IO value that represents writing to standard out is not an effect, it's a value

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:07):

pure functions are also computed by the CPU

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:07):

yeah it's necessary but not sufficient

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:07):

I'm not saying all CPU activity is effects, but rather that all effects involve CPU execution

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:08):

that is fairly totalogical

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:08):

What I'm saying is a pretty standard Haskell perspective I think, I think I have the answer to my question, Roc just doesn't see things that way

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:08):

Not necessarily as an intentional deviation from Haskell

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:09):

I think this is at the core of why I'm not jumping full force onto Roc

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:10):

Richard Feldman said:

I know a lot of Haskell programmers, and I've written Haskell professionally, and this conversation is the first time I've ever heard anyone say Either is an effect :sweat_smile:

https://chatgpt.com/share/6a7b737d-43b4-83e8-a70c-2c6de0646bfc

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:11):

It took me like 4 years of trying to learn Haskell to get to appreciating the category theory stuff

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:12):

for those first years I wouldn't have described Maybe a as an effect

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:15):

Also all that said, I think it would be a reasonable design decision to say "we're just not doing first class effects", possibly for perf reasons, but also just to avoid the whole Scala-style drama and keeping things accessible. I've actually wondered what it would look like to specifically design a language that is Haskell w/ an implicit do basically, but w/ more flexibility in the monad stack than how Roc is doing it

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:16):

a lot of smart people have been working on effect modeling and all of them seem unsatisfied when you get to scale for reasons I don't understand despite having done some reading on it, seems like a hard problem

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:20):

Richard Feldman said:

so I would say "writing to standard out" is an effect

but an IO value that represents writing to standard out is not an effect, it's a value

is 2::uint a number?

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:21):

making something first class means making them values, if effects are first class as in Haskell then they are values I'd say

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:23):

regarding how Haskellers view this, I just rewatched this clip of SPJ where he uses a bunch of effects terminology and frankly I think he across the board agrees with my view:

by effects I mean "side effects"

when you call a method ... the sole reason for calling it is to have an effect on the world. It is fundamentally effectful.

In the end, a program with no effects - there's no point to it, is there? ... The reason to run a program is to have an effect.

We figured out how to combine effectful computations and effect-free ones without polluting each other.

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:23):

I don't think there's a reasonable way to interpret SPJ saying "the reason to run a program is to have an effect" as him thinking about Maybe and List when he's saying "effect" - the point he's making is that interacting with the outside world (which is how he and I are using the term "effects") is where programs become fundamentally more useful than calculators.

and then when he says "after many years of embarrassment, we figured out how to combine effectful computations and effect-free ones" in Haskell - again, they had Maybe and List and other monads long before Wadler figured out monadic I/O. I don't think the reason he's talking like this is that he's ignorant of the history of Haskell, since he participated in all of it. :smile:

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:23):

a Java programmer in the 90s wouldn't have thought of a function as a value

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:25):

Richard Feldman said:

I don't think there's a reasonable way to interpret SPJ saying...

link the clip?

I don't find that phrasing inconsistent w/ what I'm saying, in Haskell we cause external IO effects by modeling them as first class values and then returning them in main

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:25):

Jake Brownson said:

What I'm saying is a pretty standard Haskell perspective I think, I think I have the answer to my question, Roc just doesn't see things that way

again I think we'll have to agree to disagree on whether it's a standard Haskell perspective :sweat_smile:

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:25):

oh sorry - the links in the quoted sections are links to parts of the clip

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:26):

ahh duh

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:27):

Did you see my ChatGPT link? Not that it's god, and possible it's like influenced by my chat history, but it agrees that Haskellers would describe a Maybe as an effect, interestingly it separates "side effect" which I could agree w/, side effect is a loose term I think

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:27):

he also mentions "any effects, anywhere" as being in the "unsafe" part of the graph - so again, if Maybe and List are effects, then it's really hard to understand why he would possibly be thinking "any effects, anywhere, such as Maybe and List, is unsafe, and C# is an example of this unsafety because it has monads like List, whereas Haskell is on the other side of this axis because it...does not have Maybe and List"

view this post on Zulip Aurélien Geron (Aug 11 2026 at 19:27):

For people (like me) who are new to functional programming, Richard's definition of purity is easy to grasp, but I humbly don't understand Jake's. Could you please explain it like I'm 5? Also, what are the practical consequences, is it mainly agreeing on vocabulary?

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:31):

Jake Brownson said:

Did you see my ChatGPT link? Not that it's god, and possible it's like influenced by my chat history, but it agrees that Haskellers would describe a Maybe as an effect, interestingly it separates "side effect" which I could agree w/, side effect is a loose term I think

I just looked at it and it seems to agree with me: :smile:

More precisely, a careful Haskeller might say:

Maybe is a monad that models the failure effect.

rather than:

Maybe is an effect.

that's exactly the distinction I'm drawing!

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:31):

Richard Feldman said:

regarding how Haskellers view this, I just rewatched this clip of SPJ where he uses a bunch of effects terminology and frankly I think he across the board agrees with my view:

by effects I mean "side effects"

when you call a method ... the sole reason for

Jake: calling it is to have an effect on the world. It is fundamentally effectful.
he's talking about methods in C# here right?

In the end, a program with no effects - there's no point to it, is there? ... The reason to run a program is to have an effect.

Jake: yes, as it sounds like you're aware, Haskell decided to be non-strict which required being pure, and they didn't have a good answer for how to do effects for a long time until they made them first class and composed them monadically, Haskell has effects, they're first class.

We figured out how to combine effectful computations and effect-free ones without polluting each other.

Jake: Yes, we combine them by modeling effects as monadic IO values.

I'm not sure how the quoting is going to turn out on this, I'll edit it if it's strange

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:32):

ugh I don't know how to fix the quoting

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:32):

he's talking about C# yeah

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:32):

right, but the distinction here is "monads model effects" (or, equivalently to me, "monads describe effects") versus "monads are effects"

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:33):

it's like the difference between writing down a list of 5 things to do vs. actually doing those 5 things

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:33):

doing them is the effect, the list just models them

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:34):

yeah, there are kind of two layer to Haskell, there's like the Lisp with types layer, then there's like the category theory layer, this way of thinking is the category theory layer, it's possible to use Haskell w/o thinking this way, but I find it to be very beauitful and useful to bring back to other languages even if they can't express it directly. It's why there's the trope of everyone finally understanding Monad and wanting to write a Monad tutorial.

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:36):

Aurélien Geron said:

For people (like me) who are new to functional programming, Richard's definition of purity is easy to grasp, but I humbly don't understand Jake's. Could you please explain it like I'm 5? Also, what are the practical consequences, is it mainly agreeing on vocabulary?

It's not really easy to concisely address your question, I've been attempting to at least provide some contradictions here that can open a crack to the way of thinking, but it's the kind of thing you need to like build new brain circuits to really grok. There is a trope in Haskell that there are too many Monad tutorials because when someone finally has the epiphany they see how simple and beautiful it is and want to share it w/ the world. It's hard not because it's complex but because it's abstract.

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:37):

It goes deeper than agreeing on vocab though, it's about seeing a larger abstraction that unifies "side" effects, non-determinism, exceptions, and more. In most languages you see those things as distinct and features of the langauge, but in Haskell the Monad abstraction unifies them and more in a beautiful way.

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:39):

Richard Feldman said:

right, but the distinction here is "monads model effects" (or, equivalently to me, "monads describe effects") versus "monads are effects"

Anyway, we're kind of looping here, but I do promise you what I'm sharing is a perspective that you get in Haskell once you really grok Monad. Gaining this perspective felt the same as gaining the perspective that functions can be first class which I got from Clojure.

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:41):

I hope I'm not coming off as condescending or anything, obviously you're super well read and Roc is super impressive, just trying to wrap my head around the part of it that I'm not resonating w/

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:41):

understood! :smile:

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:42):

I'm not saying you should change how you're thinking about those things, I just disagree with the factual claim about most Hakellers using "effect" and "monad" interchangeably

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:42):

but regardless, I suppose it's not that big of a deal as long as we're clear about how we're using it here :smile:

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:42):

I may underestimate the folks that use Haskell as a typed Lisp, so it may be factually untrue that over 50% think that way, but it's very much a Haskell way of thinking

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:43):

I do think it would be a super beneficial perspective to understand when building something like Roc, I'm not sure you would want to go full HKT and do, but it might help inform the platform design

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:44):

yeah that was actually the original design! :smiley:

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:44):

I would want you to go full HKT and do, but I think it is a design tradeoff, probably for perf, that seems to be an area of active research still, but definitely from which audience you attract

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:45):

Task (aka IO) was a data structure you'd pass to the host, which would then interpret it

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:45):

but we ultimately moved away from it for various reasons

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:45):

there will still be a form of that when we get to concurrency though

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:45):

I'm still curious if 2::uint is a number ;)

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:46):

er sorry, what syntax is that?

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:46):

I mean the number 2 is a number haha

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:46):

pseudosyntax

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:46):

then sure haha

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:46):

but Task isn't an effect?

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:46):

wasn't*

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:46):

right

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:46):

to me, only functions can be effects

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:47):

or rather

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:47):

only calling functions can produce effects

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:47):

(hence "effectful function" and not just "effect")

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:47):

Richard Feldman said:

right

okay, I think that's the core of the disagreement

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:48):

assuming Task was very similar to IO

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:48):

yeah it's basically IO (Either _ _)

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:48):

so it has 2 type parameters, one for success and one for failure

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:48):

baked in

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:48):

anyway, thanks for the convo, seems like an agree to disagree thing :)

view this post on Zulip Richard Feldman (Aug 11 2026 at 19:50):

fair enough!

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:50):

I switched my focus back to my main project in Rust, but I keep thinking about Roc

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:51):

I'd probably be switching my project to it except for the stability and awkwardness of the effects/platform stuff, which I don't mean to say you should change it for me, just a data point, but I will def stick around to keep an eye on it and play here and there

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:52):

I love the community

view this post on Zulip Jake Brownson (Aug 11 2026 at 19:52):

and it blows me away how quickly bugs get fixed

view this post on Zulip Jake Brownson (Aug 11 2026 at 20:28):

Btw, I'm totally behind the idea of not allowing unsafeIO and having the guarantees behind exposing only certain external effects, what bugs me w/ platforms is mostly the lack of composability, and the awkwardness around how you can't write package libraries against types in a platform for example. I suspect they would feel less awkward w/ more mature platforms that really do include an API for _everything_ you might want on a CLI/desktop app.

Huh, I suppose there's nothing stopping someone from doing an IO platform that just has a single "do this IO value" like Task used to be? Are platforms a generalization of Task?

I would be curious to know more about why the departure from Task, was perf a big concern? Is it documented somewhere?

Setting aside debate on if monads are effects, would Roc be open to a do construct that works for types with the equivalent of bind and return (maybe call them something less mathy like and then and wrap)? I don't think this would require HKT. Maybe on or with would be a better keyword than do. I can mock up a pseudocode example if it's not clear what I mean.

view this post on Zulip Jake Brownson (Aug 11 2026 at 20:30):

hrm, maybe it would require HKT...

view this post on Zulip Niclas Ahden (Aug 11 2026 at 20:33):

I would also question whether the mainstream Haskell perspective is that Maybe is an effect... I think most Haskellers would agree that an effect is something observable that happens (e.g. printing to the console), not that an effect == monad. It's all splitting hairs/terms, but that's my view. Note: I've only done Haskell professionally for two years.

view this post on Zulip Jake Brownson (Aug 11 2026 at 20:34):

Interesting, to me that's the most important insight of Haskell, using it w/o that idea is just using a typed Lisp, which is also neat of course

view this post on Zulip Richard Feldman (Aug 11 2026 at 20:35):

Jake Brownson said:

would Roc be open to a do construct that works for types with the equivalent of bind and return (maybe call them something less mathy like and then and wrap)? I don't think this would require HKT. Maybe on or with would be a better keyword than do. I can mock up a pseudocode example if it's not clear what I mean.

we used to have it, but we took that out too :smile:

it was called "backpassing" - you can find discussions of it on Zulip.

view this post on Zulip Jake Brownson (Aug 11 2026 at 20:35):

hah okay, I take that as a "no"

view this post on Zulip Niclas Ahden (Aug 11 2026 at 20:39):

Jake Brownson said:

Interesting, to me that's the most important insight of Haskell, using it w/o that idea is just using a typed Lisp, which is also neat of course

I may just never have reached that insight, that's entirely plausible! I think you need a lot of time with a tool to really understand it, and perhaps I didn't have enough yet.

If I look at it from the outside, I think "effect is something observable that happens" makes a lot of sense, both for Roc programmers and for people coming from the outside (whether they're JavaScript developers or Haskellers). Hopefully it's something that can appeal to a lot of people, because it's such a clear definition!

view this post on Zulip Jake Brownson (Aug 11 2026 at 20:43):

yeah, first class effects is not a super accessible concept in the same way lambdas used to be for like Java programmers, but it's neat that lambdas are now basically mainstream, maybe first class effects are next

view this post on Zulip Jake Brownson (Aug 11 2026 at 20:43):

probably not, we'll hit the singularity first haha

view this post on Zulip Jake Brownson (Aug 11 2026 at 21:09):

I searched "backpassing" in Zulip and yeah that's exactly what I was picturing. It would def be hard to explain to folks that aren't coming from Haskell, so it does seem like a reasonable choice not to include it.

I actually do think Richard and my disagreement may come down to just definitional stubbornness one one of our parts. He obviously completely understands modeling what I would call effects monadically, but just objects to calling the values effects.

I don't see calling a value of type IO () an effect, or even in this case side-effect, as any different than a value of type Num a number. I would agree on everything but IO if we replace Richards use of "effect" with "side-effect". i.e. I would not call Just 42 a side-effect, but I would call it an effect. It's more obvious that Nothing is an effect (a thrown exception), but succeeding is just as effectful as failing to me.

Hah okay I'm going to go work on something else for real this time.

view this post on Zulip Jake Brownson (Aug 11 2026 at 21:39):

Here is a convo I had w/ ChatGPT analyzing this thread, just as evidence I'm not completely crazy ;): https://chatgpt.com/share/6a7b964e-72b4-83e8-a45b-442534e29572

view this post on Zulip Aurélien Geron (Aug 11 2026 at 21:48):

ChatGPT is quite helpful there, thanks for sharing

view this post on Zulip Richard Feldman (Aug 11 2026 at 22:00):

yeah! This part in particular is the distinction I was trying to get across:

Screenshot 2026-08-11 at 5.59.58 PM.png

view this post on Zulip Jake Brownson (Aug 11 2026 at 22:00):

I thought you'd like that part, but you'll see down later the arguments i'm presenting here convince it

view this post on Zulip Jake Brownson (Aug 11 2026 at 22:01):

I do like its distinction of computational effects vs side effects, though the boundary is fuzzy if you think about it, like IO being a State monad over the universe.

view this post on Zulip Jake Brownson (Aug 11 2026 at 22:01):

but if you draw a line somewhere, then effects outside that line are side effects

view this post on Zulip Jake Brownson (Aug 11 2026 at 22:01):

Haskell/Roc have reasonably clear lines for that

view this post on Zulip Jake Brownson (Aug 11 2026 at 22:06):

I hadn't really thought about a distinction between effect and side-effect before, but that does seem like a useful distinction, and might bring our perspectives together

view this post on Zulip Jake Brownson (Aug 11 2026 at 22:07):

though I would still call the value putsrtln "Hello" a side effect _and_ an effect

view this post on Zulip Jake Brownson (Aug 11 2026 at 22:07):

even if you never pass it through main

view this post on Zulip Jake Brownson (Aug 11 2026 at 22:07):

in the same way I call the value 2 a number and a lambda a function

view this post on Zulip Jake Brownson (Aug 11 2026 at 22:07):

they're all first-class values in Haskell

view this post on Zulip Jake Brownson (Aug 11 2026 at 22:29):

Maybe there is a distinct disagreement for non-IO monads, I think of a monad as a little programming language with effects available particular to the specific monad, so in the Maybe monad you have all the normal pure Haskell things, but you also have “null”, I.e. your functions can be partial, aka they can fail. Functions that return Maybe are written in that monad.

view this post on Zulip Jake Brownson (Aug 11 2026 at 22:35):

You could go the other way of course and say that 2 as a uint is not a number, it’s a bunch of voltages in a register or something, but that convo requires a hot tub
image_4CE9D086-9FA3-4E16-9338-8BBB5A870FBC_1786487729.jpeg

view this post on Zulip Kilian Vounckx (Aug 12 2026 at 04:58):

I don't really want to heat this discussion or anything. I feel like from a practical standpoint it is quite pointless by now. I feel like we understand each other's points but just disagree

However from a philosophical standpoint I find it quite interesting. As someone with no industrial, only academical experience with Haskell, I've never met anyone who said monad equals effect. I've written a thesis about type inference and using a custom monad type to build constraints and the AST. I'd not call it an effect. I'd call the handler or whatever unwraps the monad actually doing the effect. Like in case of Either that might be unwrapping both cases. But in case of IO, that happens by the Haskell runtime even after main is constructed, when the actual console logs/http calls/file writes/... are happening

view this post on Zulip Jake Brownson (Aug 12 2026 at 06:14):

From the abstract of Wadler's Monads for Functional Programming:

Monads provide a convenient framework for simulating effects found in other languages, such as global state, exception handling, output, or non-determinism.

global state -> state monad
exception handling -> Maybe/Either aka Try
output -> Writer
non-determinism -> list

view this post on Zulip Jake Brownson (Aug 12 2026 at 06:42):

Here's a more contemporary source from a Haskell keynote: https://youtu.be/m821Vz8N_bo?si=gxDbuRMXNIKAqohR&t=1616


Last updated: Aug 12 2026 at 12:35 UTC