Stream: ideas

Topic: Await as a task operation


view this post on Zulip Ayaz Hafiz (Nov 06 2023 at 05:11):

Right now Task.await is a function over Tasks. But consider the proposed new model of Task effects, which allows composing effects arbitrarily (https://gist.github.com/ayazhafiz/4fbb9cdc284ba5cdfabb53e0c39a7b4d):

interface A exposes [main] imports []

# Task.roc
Task v op := (v -> op) -> op

await : Task a op, (a -> Task b op) -> Task b op
await = \@Task fromResult, next ->
    @Task \continue ->
        fromResult \result ->
            @Task inner = next result
            inner continue

# StdinEffect.roc
OpIn a b : [
    StdinLine (Str -> OpIn a b),
    Done a,
]b

# Stdin.roc
lineIn : Task Str (OpIn * *)
lineIn = @Task \toNext -> StdinLine \s -> (toNext s)

# StdoutEffect.roc
OpOut a b : [
    StdoutLine Str ({} -> OpOut a b),
    Done a,
]b

# Stdout.roc
lineOut : Str -> Task {} (OpOut * *)
lineOut = \s -> @Task \toNext -> StdoutLine s \{} -> (toNext {})

# Platform
# really, we want a syntax like [Done a](OpIn a)(OpOut a) here
Op a : [
    StdinLine (Str -> Op a),
    StdoutLine Str ({} -> Op a),
    Done a,
]

main : Task {} (Op *)
main =
    lineIn
    |> await \s -> lineOut s

In this model, we can. compose StdoutLine/StdinLine into a single effect despite declaring them separately, and moreover the definition of the StdinLine/StdoutLine effects do not depend at all on Task.

view this post on Zulip Ayaz Hafiz (Nov 06 2023 at 05:11):

Is there any way to express Await as an effect in the same way, as a tag in an Op type? I feel like this must be possible, but unfortunately I do not see a way to do so immediately.

view this post on Zulip Ayaz Hafiz (Nov 06 2023 at 05:12):

That is, have await be defined via an Await tag in an Op type that can be evaluated by a platform, in the same way StdinLine/StdoutLine are, rather than a Task -> Task transformation function in Roc

view this post on Zulip Brendan Hansknecht (Nov 06 2023 at 05:27):

Could it be done with something like: Await ((OpIn x b), (x -> OpIn a b)). The platform would run OpIn x b to completion, then used the returned x to continue the x -> OpIn a b closure.

view this post on Zulip Brendan Hansknecht (Nov 06 2023 at 05:30):

I guess that wouldn't work cause I making a type variable out of nowhere. Not sure how you can specific the intermediate await type

view this post on Zulip Ayaz Hafiz (Nov 06 2023 at 14:44):

Right. I guess we could use Erased there so that the type variable can disappear, but then the task needs to be boxed

view this post on Zulip Ayaz Hafiz (Nov 06 2023 at 14:44):

I wonder if there's a way to do it without type erasure


Last updated: Jun 16 2026 at 16:19 UTC