Map vs keyword list in library api

Hi,
I am working on a small library. The goal of the library is to have a native LiveView data table.
I am a beginner in Elixir but I managed to do everything I’ve planned so far. But there is one thing that I am struggling with and that is API design. Now my API of the library looks like this:

DefaultLiveTable.render(
      data: &get_data/1,
      columns: [
        %{field: :name, filtering: %{enable: true, variant: :multiple_select, options: %{a: "a", b: "b"}}},
        %{field: :surname, filtering: %{enable: true, variant: :string}}
      ],
      actions: [
        %{
          value: "Edit",
          patch: Routes.default_path(socket, :index)
        }
      ]
    )

But I am not sure if it wouldn’t be better to use keyword lists instead of maps like this:

DefaultLiveTable.render(
      data: &get_data/1,
      columns: [
        [field: :name, filtering: [enable: true, variant: :multiple_select, options: [a: "a", b: "b"]]],
        [field: :surname, filtering: [enable: true, variant: :string]]
      ],
      actions: [
        [
          value: "Edit",
          patch: Routes.default_path(socket, :index)
        ]
      ]
    )

I found working with maps easier because they work better with pattern matching and elements are accessible by dot syntax. But keyword lists are easier to write, and users wouldn’t need to wonder where to use maps and where lists.

What do you think?

I think the library should accept both and deal with maps internally by calling Map.new/1 on the argument when a keyword list has been supplied.

One tip is you can take your options that you get from the user, pass it to Map.new/1, and then you can pattern match knowing it’s a map for sure. This way users can pass either a keyword list or a map and either will work.

Enum.into is also a great way to merge user provided opts into e.g. a map of defaults.

1 Like

So I implemented Map.new/1 and it works perfectly. Thanks for the tip :grinning:

Perhaps a bit unsolicited but here’s my two cents, I would create a %DataTable{} struct, and pass that into DataTable.render, to me render should only do one thing - convert a known data struct into a HTML, by separating out the data layer I think it’d be a lot more flexible.

I think that structs are not suitable for my use case because users need to be able to pass custom data into the render function. It’s because users can write custom parts of the table and these parts may require other data than the ones that I would define in the struct.