Stream: show and tell

Topic: draft article about ! syntax


view this post on Zulip Richard Feldman (Apr 21 2024 at 04:26):

I wrote a draft of an article about the new ! suffix - any feedback welcome! https://docs.google.com/document/d/1XKZEtOg5efJFnzXxwY2rPRE64H796lCrt7H_j0340P4/edit?usp=sharing

view this post on Zulip Richard Feldman (Apr 21 2024 at 04:27):

as previously discussed, I plan to post this on a personal site rather than as any kind of official announcement on roc-lang.org

view this post on Zulip Karl (Apr 21 2024 at 04:39):

Your javascript syntax example has a then instead of an afterwards for the last callback operation

view this post on Zulip Richard Feldman (Apr 21 2024 at 04:40):

thanks, fixed!

view this post on Zulip Karl (Apr 21 2024 at 04:55):

Somewhat off topic but the discussion in the article about the output format jogged my memory:

The best async primitive I've run across in any language is let-flow in Clojure's aleph library. In Clojure the let form lets you make a series of bindings and then use it within the body works a lot like let...in in Elm. Aleph uses some macro magic to sort the bindings topologically and dispatches independent tasks in parallel. I think of it basically every time I read about an async await primitive which generally have semantics that precludes this type of optimization.

I understood that Roc desugared into callbacks and from that assumed it wouldn't be possible to get let-flow style optimizations but in reading this it occurs to me that the output is a data structure so this sort of optimization might be possible.

view this post on Zulip Richard Feldman (Apr 21 2024 at 04:58):

sort the bindings topologically and dispatches independent tasks in parallel

that's cool! we have record builder syntax for this use case - the syntax is implemented, but we don't have any parallel-capable platform runtimes to use it on yet :big_smile:

view this post on Zulip Richard Feldman (Apr 21 2024 at 04:58):

(that's because we need to make Task a builtin to enable that, which hasn't happened yet)

view this post on Zulip Luke Boswell (Apr 21 2024 at 05:22):

Are there any blockers for making Task a builtin? Does it just need implementation?

view this post on Zulip Karl (Apr 21 2024 at 05:22):

I could see how it could be done with that syntax but it'd be nicer if it worked everywhere without having to think about it. If Task is a builtin that the compiler knows about then the compiler could also know where task values are used. Extending the example syntax to include an id per op and dependencies: id[] would give platforms the information needed to dispatch in parallel but they could just head of line block like they currently do if they ignore the information.

view this post on Zulip Norbert Hajagos (Apr 21 2024 at 05:32):

That sounds very cool for a library, but not so much as the default behaviour for a language. Based on a sofware unscripted podcast episode (i think it was "making jit-ed code run faster") this seems like it would work a nice number of times, but when it wouldn't, it would be really bad, because you need to get around it somehow. I dont know how often that comes up in clojure though, since it doesn't have the same goals as parralelising c++ code. For Roc, I think Task builtins would be better for concurrency (ofc the platform would need to declare wheather it supports parralelism or not)

view this post on Zulip Richard Feldman (Apr 21 2024 at 05:48):

Luke Boswell said:

Are there any blockers for making Task a builtin? Does it just need implementation?

no blockers! No one is working on it yet though

view this post on Zulip Richard Feldman (Apr 21 2024 at 05:49):

yeah regarding parallelization, I think it's really important to be able to have confidence that your code is running in parallel vs not

view this post on Zulip Richard Feldman (Apr 21 2024 at 05:50):

one of the reasons records were chosen explicitly for that syntax is because it's really obvious syntactically that when you're building a record, the fields can't depend on each other (because the record hasn't been built yet)

view this post on Zulip Richard Feldman (Apr 21 2024 at 05:50):

and for them to run in parallel, they can't depend on each other

view this post on Zulip Richard Feldman (Apr 21 2024 at 05:51):

Haskell's applicative do also does it automatically based on compiler analysis, but I prefer it to be explicit in syntax

view this post on Zulip Brendan Hansknecht (Apr 21 2024 at 14:36):

I thought there was a blocker related to effects and dependences. Though maybe that is only an issue once we have the effect interpreter version of task?

view this post on Zulip Richard Feldman (Apr 21 2024 at 14:44):

yeah basically task as builtin (on its own) just requires:

view this post on Zulip Richard Feldman (Apr 21 2024 at 14:45):

making Task.map2 able to parallelize has other blockers, but at a minimum it requires task being a builtin, which doesn't have blockers

view this post on Zulip Richard Feldman (Apr 21 2024 at 14:46):

separately, once we have module params we can (and should) replace hosted modules with declarations in the platform module, but that's blocked on task being a builtin

view this post on Zulip Richard Feldman (Apr 21 2024 at 14:47):

that change might be the dependencies thing you're thinking of; we can't make that change without module params or else the ergonomics would be terrible :sweat_smile:

view this post on Zulip Brendan Hansknecht (Apr 21 2024 at 15:22):

Instead of generating Effect in hosted modules we generate Task

Ah yeah, that would definitely fix any Effect and Task related dependency issues.

view this post on Zulip Brendan Hansknecht (Apr 21 2024 at 15:22):

Can't have a dependency on Effect if Effect doesn't exist


Last updated: Jul 05 2025 at 12:14 UTC