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?
It has to be defined based on the inspect primitives listed here: https://www.roc-lang.org/builtins/Inspect#InspectFormatter
This is an example implementation: https://github.com/roc-lang/roc/blob/2e7343e857f6ab73793cef2ad6c8e4a20dba30cb/examples/Community.roc#L93
I guess with autoderiving of inspect, we removed our only example of implementing this.
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?
Yeah, we may need to expand primitives for inspect. One for escaped string and one for arbitrary strings
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
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.
Aside, should other type render as a tuple or list?
Not everything needs to be strings
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.
Inspect.inspect x
Just do that to each value in the list
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.
Does it work if you leave out the redundant lambda around Inspect.inspect?
|> List.map Inspect.inspect
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
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.
I must have something mixed up...one second
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
The error seems outdated. The issue is closed.
I have build roc from source at commit 613c6c4e29ac37f2adcf724fe31af3557eadcdc4 (two days ago)
Try Inspect.toInspector
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
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.
Cause tuples can be heterogenous
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")
Autoderive is like:
OtherType a := (Rational a, Rational a, Rational a)
implements [Inspect]
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.
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