Proposal: Inspect for records

65 views
Skip to first unread message

wojte...@plataformatec.com.br

unread,
Dec 15, 2018, 11:08:13 AM12/15/18
to elixir-lang-core
Hello,
I'd like to discuss support for Inspect protocol for records. I created a proof-of-concept here: https://github.com/wojtekmach/record_inspect

Basically, for these records:

defrecord :resultset [
 
:column_count,
 
:column_definitions,
 
:row_count,
 
:rows,
 
:warning_count,
 
:status_flags
]

defrecord
:column_definition41, [:name, :type]

Instead of:

{:resultset, 2,
 
[{:column_definition41, "2*3", 8}, {:column_definition41, "4*5", 8}], 1,
 
[[6, 20]], 0, 2}

We could get this:

#resultset([
  column_count
: 2,
  column_definitions
: [
   
#column_definition41([name: "2*3", type: 8]),
   
#column_definition41([name: "4*5", type: 8])
 
],
  row_count
: 1,
  rows
: [[6, 20]],
  warning_count
: 0,
  status_flags
: 2
])

My proof-of-concept uses the new `:persistent_term` facility in OTP 21.2 just to get something working. Managing the state is challenging for potentially including this in Elixir.

Feedback appreciated!

wojte...@plataformatec.com.br

unread,
Dec 15, 2018, 11:49:42 AM12/15/18
to elixir-lang-core
One way to avoid issues with state is to avoid... state. Maybe we'd configure it like this:

records = [
 
# explicit
 
{:column_definition, [:name, :type]},

 
# grabs record fields from record macro in that module, e.g: resultset(resultset()) |> Keyword.keys()
 
{Records, :resultset}
]

IEx.configure(inspect: [records: records])
ExUnit.configure(inspect: [records: records])

This is less convenient, but I guess sometimes we may prefer the "tuple" representation so by making this opt-in we cater to that use case as well.

Allen Madsen

unread,
Dec 16, 2018, 8:55:58 AM12/16/18
to elixir-l...@googlegroups.com
Couldn't you define a method like __record_keys__/0 on the atom in the defrecord call?

--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/8e936469-e806-46f9-b4a4-3aa3c577d205%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Louis Pilfold

unread,
Dec 16, 2018, 9:47:57 AM12/16/18
to elixir-l...@googlegroups.com
Hey

Unlike structs there's no guarantee that two records won't be defined with the same name, they are scoped within modules.

Cheers,
Louis

wojte...@plataformatec.com.br

unread,
Dec 16, 2018, 3:14:22 PM12/16/18
to elixir-lang-core
> Couldn't you define a method like __record_keys__/0 on the atom in the defrecord call?

Yes. To be concrete, this is how it could work:

```
defmodule Records do
  import Record
  defrecord :a, [:x, :y]
  defrecord :b, [:x, :y]
end

iex> Records.__records__()
[
  a: [:x, :y],
  b: [:x, :y]
]
```

Alternatively, this could become `Records.__info__(:records)`. In either case, to make this work I think we'd need `use Record`. Or, we'd have a new `defrecords/1` that defines both multiple records as well as this reflection function.

> Unlike structs there's no guarantee that two records won't be defined with the same name, they are scoped within modules.

That's a good point. That's something I was aware of but I guess I haven't thought through the consequences which I think are: if by doing `defrecord` we'd store the fields somewhere (like the :persistent_term solution mentioned) we'll need to handle duplicates by erroring or at least warning.

For this reason, perhaps the other, "stateless", approach mentioned above is more suitable? Users will opt-in to use records so they'll handle conflicts themselves.

I mentioned we can configure inspect behaviour with `IEx.configure`; the `ExUnit.configure(inspect: ...) is not possible (I honestly thought it is) so in order to format records in ExUnit diffs we'd need that.
Even if we configure both IEx and ExUnit to use record definitions, anytime we do `IO.inspect(record)` we'd get non-formatted records output and so to format it we'd have to keep inspect record options everywhere. To take advantage of this feature we'd have to configure it in IEx, ExUnit and all IO.inspect calls; that's explicit but doesn't sound practical.

The only solutions I can think of at the moment are:
1. Going back to magically keeping record fields
2. Currently, we have essentially `Kernel.inspect(term, opts \\ [])`, `IO.inspect(term, opts \\ [])`; if we change it something along the lines of `Kernel.inspect(term, opts \\ default_opts())` and make these defaults configurable we could configure our records just once. So we're back to global state after all.

Neither option is perfect. I'll continue experimenting with this as a standalone library but it seems less likely it could be part of Elixir. What do you think?

José Valim

unread,
Dec 16, 2018, 3:22:20 PM12/16/18
to elixir-l...@googlegroups.com
Note that both IEx and ExUnit already keep a wrapper around inspect as they do provide default inspecting values, so the explicit approach sounds like the way to go IMO.

José Valim
Skype: jv.ptec
Founder and Director of R&D


Reply all
Reply to author
Forward
0 new messages