roc-lang / roc

A fast, friendly, functional language.
https://roc-lang.org
Universal Permissive License v1.0
4.46k stars 313 forks source link

Abilities failing to compile #5584

Open bhansconnect opened 1 year ago

bhansconnect commented 1 year ago

The below ability works with simpler functions, like appendStr, but appendRecord will not compile. I am assuming it is related to taking an Inspector f as part of the input type. Don't really know though. I tried to minimize what I could.

Just run roc test on:

interface Repro
    exposes []
    imports []

## Abilities

Formatter has
    init : {} -> f | f has Formatter
    appendRecord : List { key : Inspector f, value : Inspector f } -> Inspector f | f has Formatter
    appendStr : Str -> Inspector f | f has Formatter

Inspector f := f -> f | f has Formatter

custom = @Inspector

apply : Inspector f, f -> f | f has Formatter
apply = \@Inspector fn, fmt -> fn fmt

Inspect has
    inspectValue : val -> Inspector f | val has Inspect, f has Formatter

inspect : val -> f | val has Inspect, f has Formatter
inspect = \val ->
    @Inspector valFn = inspectValue val
    valFn (init {})

## Formatter impl

LogFormatter := { bytes : List U8 }
     has [
         Formatter {
             init: initLF,
             appendRecord: appendRecordLF,
             appendStr: appendStrLF,
         }
     ]

initLF : {} -> LogFormatter
initLF = \{} -> @LogFormatter { bytes: [] }

appendRecordLF : List { key : Inspector LogFormatter, value : Inspector LogFormatter } -> Inspector LogFormatter
appendRecordLF = \fields ->
    f0 <- custom
    f0
    |> \x -> apply (appendBytesLF (Str.toUtf8 "{ ")) x
    |> \f1 ->
        (f2, prependSep), { key, value } <- List.walk fields (f1, Bool.false)
        f3 =
            if prependSep then
                f2
                |> \x -> apply (appendBytesLF (Str.toUtf8 ", ")) x
            else
                f2

        apply key f3
        |> \x -> apply (appendBytesLF (Str.toUtf8 " : ")) x
        |> \x -> apply value x
        |> \f4 -> (f4, Bool.true)
    |> .0
    |> \x -> apply (appendBytesLF (Str.toUtf8 " }")) x

appendStrLF : Str -> Inspector LogFormatter
appendStrLF = \str ->
    f0 <- custom
    f0
    |> \x -> apply (appendBytesLF (Str.toUtf8 "\"")) x
    |> \x -> apply (appendBytesLF (Str.toUtf8 str)) x
    |> \x -> apply (appendBytesLF (Str.toUtf8 "\"")) x

appendBytesLF : List U8 -> Inspector LogFormatter
appendBytesLF = \added ->
    (@LogFormatter { bytes }) <- custom
    @LogFormatter { bytes: List.concat bytes added }

toBytesLF : LogFormatter -> List U8
toBytesLF = \@LogFormatter { bytes } -> bytes

## Specific Impl

Person := {
    firstName : Str,
}
     has [
         Inspect {
             inspectValue: inspectPerson,
         },
     ]

inspectPerson : Person -> Inspector f | f has Formatter
inspectPerson = \@Person { firstName } ->
    # This fails, seems to be directly caused by using `appendRecord` with an actual list of fields.
    fields : List {key: Inspector f, value: Inspector f}
    fields = [
        { key: appendStr "firstName", value: appendStr firstName },
    ]
    appendRecord fields

    # This works but of course would fail the test.
    # appendStr "Testing123"

expect
    person = @Person { firstName: "John" }

    out = inspect person |> toBytesLF |> Str.fromUtf8

    out == Ok "{ \"firstName\": \"John\" }"

The specific error is:

thread 'main' panicked at 'Error in alias analysis: error in module ModName("UserApp"), function definition FuncName("\x0b\x00\x00\x00\x10\x00\x00\x00\x06\xac\x00\x82\xa2\xd5\xd93"), definition of value binding ValueId(3): could not find func in module ModName("UserApp") with name FuncName("\x11\x00\x00\x00\x10\x00\x00\x00\xa4W\x08Y;6\xdd8")', crates/compiler/gen_llvm/src/llvm/build.rs:4904:19

As an extra notes, as I was testing, I also hit some stack overflows. So there may be more than one issue/error.

bhansconnect commented 1 year ago

@ayazhafiz I thought I would ping you directly to point you to this issue. This is work specifically related to inspect (Richard wants to look into giving inspect a more powerful api). Currently, the api isn't fully possible because it is causing the compiler to crash. Any ideas/workarounds?

ayazhafiz commented 1 year ago

let's discuss on Zulip. I have some thoughts on this.

bhansconnect commented 1 year ago

So as a workaround, just inspectPerson needs to be modified. Instead of just returning appendRecord, it needs to wrap everything in a lambda and apply it.

inspectPerson = \@Person { firstName } ->
    f <- custom
    fields : List {key: Inspector f, value: Inspector f}
    fields = [
        {
            key:  appendStr "firstName",
            value: appendStr firstName,
        },
    ]
    apply (appendRecord fields) f