Stream: beginners

Topic: How to inspect an opaque type


view this post on Zulip Oskar Hahn (Dec 26 2023 at 19:32):

I am playing around with opaque types. I was able to find out how to implement the Equal ability. But I can not figure out, how to implement the inspect ability.

Here is an example type for a rational number:

Rational a := (Int a, Int a)
    implements [
        Eq { isEq: qEq },
        # Inspect {
        #     toInspector: toInspectorRational,
        # },
    ]

qEq = \@Rational (a, b), @Rational (c, d) ->
    a * d == c * b

qToStr = \@Rational (a, b) ->
    "\(a |> Num.toStr) / \(b |> Num.toStr)"

How do I implement the toInspectorRational function, that it calls qToStr?

view this post on Zulip Brendan Hansknecht (Dec 26 2023 at 19:58):

It has to be defined based on the inspect primitives listed here: https://www.roc-lang.org/builtins/Inspect#InspectFormatter

view this post on Zulip Brendan Hansknecht (Dec 26 2023 at 20:00):

This is an example implementation: https://github.com/roc-lang/roc/blob/2e7343e857f6ab73793cef2ad6c8e4a20dba30cb/examples/Community.roc#L93

view this post on Zulip Brendan Hansknecht (Dec 26 2023 at 20:01):

I guess with autoderiving of inspect, we removed our only example of implementing this.

view this post on Zulip Oskar Hahn (Dec 26 2023 at 21:03):

Thank you. That was helpful.

Rational a := (Int a, Int a)
    implements [
        Eq { isEq: qEq },
        Inspect {
            toInspector: toInspectorRational,
        },
    ]

toInspectorRational = \@Rational (a, b) ->
    f0 <- Inspect.custom
    "\(a |> Num.toStr) / \(b |> Num.toStr)"
    |> Inspect.str
    |> Inspect.apply f0

This works. It prints the value as "5 / 1"

When I use the type in another type and inspect that, I get some not so nice quotation marks:

toInspectorOtherType = \@OtherType (x, y, z) ->
    f0 <- Inspect.custom
    "\(x |> Inspect.toStr), \(y |> Inspect.toStr), \(z |> Inspect.toStr)"
    |> Inspect.str
    |> Inspect.apply f0
v = ""1 / 1", "2 / 1", "3 / 1""

How would you do it?

view this post on Zulip Brendan Hansknecht (Dec 26 2023 at 21:54):

Yeah, we may need to expand primitives for inspect. One for escaped string and one for arbitrary strings

view this post on Zulip Brendan Hansknecht (Dec 26 2023 at 21:55):

An important note is that inspect is not guaranteed to output in a string format at all. So it is super flexible which makes defining the exact API a bit more nebulous

view this post on Zulip Brendan Hansknecht (Dec 26 2023 at 21:57):

The reason for this is that inspect can then generate a tree structure that can be understood more thoroughly than. Be used in generating a GUI with collapsible lists, etc.

view this post on Zulip Brendan Hansknecht (Dec 26 2023 at 23:11):

Aside, should other type render as a tuple or list?

view this post on Zulip Brendan Hansknecht (Dec 26 2023 at 23:12):

Not everything needs to be strings

view this post on Zulip Oskar Hahn (Dec 27 2023 at 20:25):

Could you give me an example with tuple?

when I write:

toInspectorOtherType= \@OtherType (x, y, z) ->
    f0 <- Inspect.custom
    [x, y, z]
    |> Inspect.tuple
    |> Inspect.apply f0

I get the error, that [x,y,z] is List (Rational a), and not List Inspector f where f implements InspectFormatter. But I don't know how to convert x, y and z to an Inspector. I tried Inspect.inspect an Inspect.apply f0, but this did not work.

view this post on Zulip Brendan Hansknecht (Dec 27 2023 at 20:33):

Inspect.inspect x

view this post on Zulip Brendan Hansknecht (Dec 27 2023 at 20:33):

Just do that to each value in the list

view this post on Zulip Oskar Hahn (Dec 27 2023 at 20:43):

If I write:

toInspectorOtherType = \@OtherType (x, y, z) ->
    f0 <- Inspect.custom
    [x, y, z]
    |> List.map \v -> Inspect.inspect v
    |> Inspect.tuple
    |> Inspect.apply f0

I get the error:

This expression has a type that does not implement the abilities it's expected to:

341│>      [x, y, z]
342│>      |> List.map \v -> Inspect.inspect v

The type Inspector does not fully implement the ability
InspectFormatter.

view this post on Zulip Brian Carroll (Dec 27 2023 at 20:52):

Does it work if you leave out the redundant lambda around Inspect.inspect?

|> List.map Inspect.inspect

view this post on Zulip Oskar Hahn (Dec 27 2023 at 20:55):

No. Its the same error.

I also get the same error, if I write:

f0 <- Inspect.custom
Inspect.tuple [Inspect.inspect 5]
|> Inspect.apply f0

view this post on Zulip Oskar Hahn (Dec 27 2023 at 20:55):

This expression has a type that does not implement the abilities it's expected to:

354│      Inspect.tuple [Inspect.inspect 5]
                        ^^^^^^^^^^^^^^^^^^^

The type Inspector does not fully implement the ability
InspectFormatter.

view this post on Zulip Brendan Hansknecht (Dec 27 2023 at 20:58):

I must have something mixed up...one second

view this post on Zulip Oskar Hahn (Dec 27 2023 at 21:00):

If I use Inspect.inspect in the repl, it crashes:

roc repl

  The rockin' roc repl
────────────────────────

Enter an expression, or :help, or :q to quit.

» Inspect.inspect 5
thread 'main' panicked at '[Abilities] not yet implemented. Tracking issue: https://github.com/roc-lang/roc/issues/2463.
Additional information: Not reachable yet', crates/compiler/mono/src/layout.rs:511:55
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

view this post on Zulip Oskar Hahn (Dec 27 2023 at 21:01):

The error seems outdated. The issue is closed.

I have build roc from source at commit 613c6c4e29ac37f2adcf724fe31af3557eadcdc4 (two days ago)

view this post on Zulip Brendan Hansknecht (Dec 27 2023 at 21:01):

Try Inspect.toInspector

view this post on Zulip Oskar Hahn (Dec 27 2023 at 21:05):

That worked! Thanks!

Here is the working code, if other need it:

toInspectorOtherType = \@OtherType (x, y, z) ->
    f0 <- Inspect.custom
    [x, y, z]
    |> List.map Inspect.toInspector
    |> Inspect.tuple
    |> Inspect.apply f0

view this post on Zulip Brendan Hansknecht (Dec 27 2023 at 21:11):

Note, if x, y, and z are not the same type, you won't be able to generate the original list and will have to manually call toInspector on each arg.

view this post on Zulip Brendan Hansknecht (Dec 27 2023 at 21:12):

Cause tuples can be heterogenous

view this post on Zulip Brendan Hansknecht (Dec 27 2023 at 21:15):

Also, if you can autoderive the inspect implementation of opaque types. They will print out as @OpaqueName innerPrintout

So in this case, something like:
@OtherType ("1 / 1", "2 / 1", "3 / 1")

view this post on Zulip Brendan Hansknecht (Dec 27 2023 at 21:17):

Autoderive is like:

OtherType a := (Rational a, Rational a, Rational a)
    implements [Inspect]

view this post on Zulip Oskar Hahn (Dec 28 2023 at 00:03):

Brendan Hansknecht said:

Also, if you can autoderive the inspect implementation of opaque types. They will print out as @OpaqueName innerPrintout

So in this case, something like:
@OtherType ("1 / 1", "2 / 1", "3 / 1")

Ahh. I did not know this. This is useful.

view this post on Zulip Brendan Hansknecht (Dec 28 2023 at 02:57):

Yeah, can also autoderive other abilities (like Eq and Hash). Probably we need to put that in docs somewhere


Last updated: Jul 26 2025 at 12:14 UTC