roc icon indicating copy to clipboard operation
roc copied to clipboard

`roc check` panics on incorrect opaque destructure

Open rtfeldman opened this issue 3 years ago • 0 comments

Needs minimization, but the crash here is because the next function is destructuring its first argument with @Task when it should be @SimTask instead.

roc check on this hello world file panics with:

'assertion failed: is_recursion_var(env.subs, *recursion_var)', /Users/rtfeldman/code/roc/crates/compiler/unify/src/unify.rs:2609:13

app "helloWorld"
    packages { pf: "cli/cli-platform/main.roc" }
    imports [pf.Stdout, pf.Program.{ Program }]
    provides [main] to pf

main = Program.noArgs mainTask

mainTask =
    Stdout.line "Hello, World!"
    |> Program.exit 0

Task ok err fx := [
    Always (Result ok err),
    Run (Effect (Task ok err fx)),
]

Effect a : [
    FileWriteUtf8 Str Str (Result {} [NotFound, Malformed] -> a),
    FileReadUtf8 Str (Result Str [NotFound, Malformed] -> a),
    StdoutLine Str a,
    StdinLine (Str -> a),
]

# Task API

succeed : ok -> Task ok * *
succeed = \ok -> @Task (Always (Ok ok))

fail : err -> Task * err *
fail = \err -> @Task (Always (Err err))

await : Task a err fx, (a -> Task b err fx) -> Task b err fx
await = \@Task task, aToTaskB ->
    when task is
        Always (Ok a) -> aToTaskB a
        Always (Err err) -> @Task (Always (Err err))
        Run effect ->
            effect
            |> mapEffect \effectTask -> await effectTask aToTaskB
            |> Run
            |> @Task

# Specific tasks (would be exposed by various different modules as normal)

stdoutLine : Str -> Task {} * [Write [Stdout]*]*
stdoutLine = \line ->
    StdoutLine line (@Task (Always (Ok {})))
    |> Run
    |> @Task

stdinLine : Task Str * [Read [Stdin]*]*
stdinLine =
    StdinLine \line -> @Task (Always (Ok line))
    |> Run
    |> @Task

fileWriteUtf8 : Str, Str -> Task {} [FileWriteErr [NotFound, Malformed]]* [Write [File]*]*
fileWriteUtf8 = \path, contents ->
    FileWriteUtf8 path contents \result ->
        @Task (Always (Result.mapErr result FileWriteErr))
    |> Run
    |> @Task

fileReadUtf8 : Str -> Task Str [FileReadErr [NotFound, Malformed]]* [Read [File]*]*
fileReadUtf8 = \path ->
    FileReadUtf8 path \result ->
        @Task (Always (Result.mapErr result FileReadErr))
    |> Run
    |> @Task

mapEffect : Effect a, (a -> b) -> Effect b
mapEffect = \effect, aToB ->
    when effect is
        FileWriteUtf8 path str resultToA -> FileWriteUtf8 path str \result ->
            aToB (resultToA result)
        FileReadUtf8 path resultToA -> FileReadUtf8 path \result ->
            aToB (resultToA result)
        StdoutLine line a -> StdoutLine line (aToB a)
        StdinLine strToA -> StdinLine \str -> aToB (strToA str)

# Simulation

SimTask ok err fx := [
    Always (Result ok err),
    Run (SimEffect (SimTask ok err fx)),
]

SimEffect a : [
    FileWriteUtf8 (Str, Str -> Result {} [NotFound, Malformed]) a,
    FileReadUtf8 (Str -> Result Str [NotFound, Malformed]) a,
    StdoutLine (Str -> a),
    StdinLine Str a,
]

andThen : SimTask a err fx, (a -> SimTask b err fx) -> SimTask b err fx
andThen = \@Task task, aToTaskB ->
    when task is
        Always (Ok a) -> aToTaskB a
        Always (Err err) -> @SimTask (Always (Err err))
        Run effect ->
            effect
            |> mapSimEffect \effectTask -> andThen effectTask aToTaskB
            |> Run
            |> @SimTask

mapSimEffect : SimEffect a, (a -> b) -> SimEffect b
mapSimEffect = \effect, aToB ->
    when effect is
        FileWriteUtf8 fn a -> FileWriteUtf8 fn (aToB a)
        FileReadUtf8 fn a -> FileReadUtf8 fn (aToB a)
        StdoutLine strToA -> StdoutLine \str -> aToB (strToA str)
        StdinLine line a -> StdinLine line (aToB a)

simulate : SimTask a err fx, Task a err fx -> Result {} Str
simulate = \@SimTask sim, @Task task ->
    when T sim task is
        T (Always simResult) (Always actualResult) ->
            if simResult == actualResult then
                Ok {}
            else
                Err "Always results were different"

        T (Run simEffect) (Run actualEffect) ->
            when T simEffect actualEffect is
                T (StdoutLine lineToA) (StdoutLine line a) ->
                    simulate (lineToA line) a

                T (StdinLine line a) (StdinLine lineToA) ->
                    simulate a (lineToA line)

                T (FileReadUtf8 pathToResult a) (FileReadUtf8 path resultToA) ->
                    simulate a (resultToA (pathToResult path))

                T (FileWriteUtf8 pathAndContentsToResult a) (FileWriteUtf8 path contents resultToA) ->
                    simulate a (resultToA (pathAndContentsToResult path contents))

                T _ _ ->
                    Err "The simulation expected [TODO] to run next but the actual task ran [TODO]"

        T (Always (Ok _)) (Run _) ->
            Err "Simulation expected (Task.succeed [TODO]) but the actual task was a [TODO]"

        T (Always (Err _)) (Run _) ->
            Err "Simulation expected (Task.fail [TODO]) but the actual task was a [TODO]"

        T (Run _) (Always (Ok _)) ->
            Err "Simulation expected a [TODO] but the actual task was a (Task.succeed [TODO])"

        T (Run _) (Always (Err _)) ->
            Err "Simulation expected a [TODO] but the actual task was a (Task.fail [TODO])"

rtfeldman avatar Oct 14 '22 01:10 rtfeldman