Inspect protocol for recursive data types

Hello! I have a question how to properly implement Inspect protocol for custom recursive data type in Elixir. For example I implemented singly linked list here: https://github.com/timCF/ex_list/blob/master/lib/ex_list/backends/struct.ex

Implementation itself and few protocol implementations like

Are working perfectly, here is example of String.Chars test https://github.com/timCF/ex_list/blob/master/test/ex_list/protocols/string_chars_test.exs

And this is important, because this implementation is very similar to Inspect protocol implementation https://github.com/timCF/ex_list/blob/master/lib/ex_list/protocols/inspect.ex

But because of some reason one is working good when other not:

iex(1)> use ExList
ExList.Backends.Struct.Utils
iex(2)> list(:hello, list(:world, list())) |> to_string
"#ExList<hello, world>"
iex(3)> list(:hello, list(:world, list())) |> inspect
"%Inspect.Error{message: \"got FunctionClauseError with message \\\"no function clause matching in Kernel.inspect/2\\\" while inspecting %{__struct__: ExList.Backends.Struct, head: :hello, tail: %{__struct__: ExList.Backends.Struct, head: :world, tail: #ExList<>}}\"}"

Also I have a second question related to protocols topic. Let’s say I have data type and implementations for standard Elixir protocols (like examples above). Are there any standard test cases for standard protocols - to check that protocol implementation is valid? Something like property-based testing - you should provide generator for custom type, and automated test cases should test generic protocol functions with given generators somehow?

2 Likes

I’m guessing it’s failing in one of those color/container_doc or whatever helper functions, however calling it ‘through’ the protocol is hiding the error, you should call it straight instead in a test, like via Inspect.ExList.Backends.Struct.inspect(list(:hello, list(:world, list())), %Inspect.Opts{}) as that will show the full and proper stacktrace and tell you what’s happening. :slight_smile:

You can always loop over the implementations after compilation and run some tests on it, I even have that functionality baked into Protocol replacement ProtocolEx, the functionality is via the deftest call but you can still do it manually via Elixir’s normal Protocols. :slight_smile:

1 Like