Stream: beginners

Topic: Non-Functions in Abilities


view this post on Zulip batyoullfly (Aug 03 2024 at 01:10):

Are regular non-function values allowed in abilities? This example type checks but fails to compile with this compiler error

thread '<unnamed>' panicked at crates/compiler/mono/src/ir.rs:6175:10:
Ability specialization is unknown - code generation cannot proceed!: NoTypeImplementingSpecialization
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" }

import pf.Stdout
import pf.Task

main =
    _ = zero
    Stdout.line ""

Arithmetic implements zero : a where a implements Arithmetic

wrapperZero = @NumWrapper 0
NumWrapper a := Num a implements [
        Arithmetic {
            zero: wrapperZero,
        },
    ]

view this post on Zulip batyoullfly (Aug 03 2024 at 01:11):

I feel that I may be running up against the limits of my attempt to hack abilities into being able to represent all of the numeric operations I care about

view this post on Zulip Richard Feldman (Aug 03 2024 at 01:16):

ah yeah we need a nicer error message for that, but basically every ability member needs to be not only a function, but it also needs to involve an argument which implements that ability

view this post on Zulip Richard Feldman (Aug 03 2024 at 01:17):

I think a thunk would work though:

Arithmetic implements
    zero : {} -> a where a implements Arithmetic

view this post on Zulip Richard Feldman (Aug 03 2024 at 01:17):

then you just always call it passing {}

view this post on Zulip Brendan Hansknecht (Aug 03 2024 at 01:27):

Yeah, that should work. Inspect does that with an init method

view this post on Zulip batyoullfly (Aug 03 2024 at 01:29):

Oh okay nice. Is this a sign that this is a terrible idea or should I continue and see how it turns out?

view this post on Zulip batyoullfly (Aug 03 2024 at 01:32):

And also is there any particular reason why this type checks if it isn’t allowed?

view this post on Zulip Brendan Hansknecht (Aug 03 2024 at 01:57):

Is this a sign that this is a terrible idea or should I continue and see how it turns out?

Seems fine to try

view this post on Zulip Brendan Hansknecht (Aug 03 2024 at 01:57):

And also is there any particular reason why this type checks if it isn’t allowed?

Probably cause abilities were added later and no one ever updated the type checker for this.

view this post on Zulip batyoullfly (Aug 03 2024 at 14:33):

So it seems like this approach is still causing issues. If I have zero be a function from a -> a where a implements Arithmetic then it seems to work fine but I have to pass some dummy value as the argument. But if I have zero be {} -> a where a implements Arithmetic I get this error:

thread '<unnamed>' panicked at crates/compiler/mono/src/ir.rs:6164:56:
called `Option::unwrap()` on a `None` value
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Example:

app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" }

import pf.Stdout
import pf.Task

main =
    _ = zero {}
    Stdout.line ""

Arithmetic implements zero : {} -> a where a implements Arithmetic

wrapperZero = \{} -> @NumWrapper 0
NumWrapper a := Num a implements [
        Arithmetic {
            zero: wrapperZero,
        },
    ]

view this post on Zulip batyoullfly (Aug 03 2024 at 14:49):

Hmmm looks like this is an issue with not specifying which concrete type I want the zero call to produce. Doing this fixes it:

app [main] { pf: platform "https://github.com/roc-lang/basic-cli/releases/download/0.12.0/Lb8EgiejTUzbggO2HVVuPJFkwvvsfW6LojkLR20kTVE.tar.br" }

import pf.Stdout
import pf.Task

main =
    wrappedZero : NumWrapper a
    wrappedZero = zero {}
    Stdout.line ""

Arithmetic implements zero : {} -> a where a implements Arithmetic

wrapperZero = \{} -> @NumWrapper 0
NumWrapper a := Num a implements [
        Arithmetic {
            zero: wrapperZero,
        },
    ]

Not sure how to fix this in the actual context of my usage of this ability though.

view this post on Zulip batyoullfly (Aug 03 2024 at 14:56):

Fixed it! The issue was this List, zeroes, where the compiler wasn't able to infer what implementation of Arithmetic to use for this list (a implements Arithmetic here):

mul : Poly a, Poly a -> Poly a
mul = \@Poly a, @Poly b ->
    resultLen = (List.len a) + (List.len b) - 1
    result = List.withCapacity resultLen
    zeroes : List a
    zeroes = List.repeat (Arithmetic.zero {}) ((List.len a) + (List.len b) - 1)
    List.walkWithIndex zeroes result \acc, _, index ->
        inner = List.walkWithIndex (List.range { start: At 0, end: At index }) (Arithmetic.zero {}) \coeff, _, p1Index ->
            p2Index = index - p1Index
            aCoeff = List.get a p1Index |> Result.withDefault (Arithmetic.zero {})
            bCoeff = List.get b p2Index |> Result.withDefault (Arithmetic.zero {})
            Arithmetic.add coeff (Arithmetic.add aCoeff bCoeff)
        List.append acc inner
    |> @Poly

Last updated: Jul 05 2025 at 12:14 UTC