shahryarjb

shahryarjb

Need a suggestion to create a pattern match by accepting optional parameters

Hello, I have been developing a simple macro and now I want to improve it and let it accept nested option.

For example:

guardedstruct do
  field(:id, String.t(), derive: "sanitize(trim, strip_tags) validate(string)")
  field(:url, String.t())
  field(:published, map()) do
    field(:type, String.t(), enforce: true)
    field(:updated, DateTime.t())
    field(:like, map()) do
      field(:id, String.t(), derive: "sanitize(trim, strip_tags) validate(string)")
      field(:updated, DateTime.t())
    end
  end
end

If you see the code, I have 2 diffrent field macro, one of them is without do and another has do.

So I tried some thing like this, but these have conflict multiple clauses

First way

  defmacro field(name, type, [do: block] = opts) do
  end

  defmacro field(name, type, opts \\ []) do
  end

Second way:

  defmacro field(name, type, opts \\ [], do: block) do
  end

  defmacro field(name, type, opts \\ []) do
  end

It has compiler error, so I want to find it is possible to create pattern instead of using Keyword.has_key?

Thanks, you can see my project here: https://github.com/mishka-group/mishka_developer_tools/blob/master/lib/macros/guarded_struct.ex

Most Liked

Eiji

Eiji

Here is a working version of yours first way:

defmodule Example do
	# macro head used to avoid warnings and allows fast preview of default argument values
	# when field/2 is called the mepty list is passed as 3rd agument
	# since it does not matches first function clause it's goes to 2nd one
  defmacro field(_name, _type, _opts \\ [])

  # allows only a Keyword with single :do key and any value
  defmacro field(_name, _type, do: _block) do
    quote do
      IO.puts("With do … block notation")
    end
  end

  # allows any list including empty ones
  defmacro field(_name, _type, opts) when is_list(opts) do
    quote do
      IO.puts("Without do … block notation")
    end
  end
end

What you have missed here is function/macro head which you should use to handle default argument values in multi clause function/macro.

The second way is not possible, because there is a conflict for do … end block as it matches with both clauses. You can:

  1. Name field macro with do … end differently (like ecto is doing)
  2. Require passing empty list []
  3. Handle @opts module attribute instead of function/macro argument
Eiji

Eiji

This is not possible. There are few reserved opts like :do or :else that you can use. You cannot combine for example: {a: 5] with do … end notation. :do and :else are part of keyword which is then passed as last macro argument.

macro_call :a, :b, c: 10 do
end
# is translated to:
macro_call(:b, :b, [c: 10], [do: …])

Also second way is not possible as macro_call(:a, :b) do … end may be seen as same macro_call(:a, :b, [], [do: …] as macro_call(:a, :b, [do: …]).

Yes, as you have tried my code with other case (i.e. field/4).

Why you can’t follow ecto’s way?

  guardedstruct do
    embeds :name, String.t(), enforce: true, derive: "sanitize(trim, upcase)" do
      field :title, String.t()
    end
  end

This way implementation is much simpler:

defmodule Example do
  # since we have a separate macro the `do … end` notation is always used
  # and therefore we don't even need function/macro head
  defmacro embeds(_name, _type, opts \\ [], do: block) when is_list(opts) do
  end

  # same here, both field/2 and field/3 are easy to pattern match in one clause
  defmacro field(_name, _type, opts \\ []) when is_list(opts) do
  end
end
Eiji

Eiji

embeds does not magically solves pattern matching. :joy:

Naming is up to you. I just used the existing one from ecto. Also not sure what you mean about style? :thinking:

Eiji

Eiji

That’s a typical smoker excuse. :joy:

There is always another way. Not seeing it does not mean that it does not exist. Well … if you would watch a lot of anime then you would be sure about it and also meanwhile you would you would doubt if drugs are really illegal in Japan or if police really do anything about it. :see_no_evil:

There is no answer for it. Naming is something which may be specific to country, culture, community or even single companies or groups of people. Nobody would choose a naming for you. This is important to say as Elixir and Phoenix (which is called framework, but in fact it’s a library) does not force anything for you. While English language in development is at least in “West world” the most popular it does not mean you are forced to use it …

# in Polish "coś" means "something"
iex> coś = 5
5
iex> coś
5

Do you know kino hex package? The one José Valim announced? As he said he choose it, because in polish it means cinema. You are really free to name everything as you wish.

As a single person I can only recommend what I saw in past commonly, for example:

  1. tree - often used around sql and self-referencing tables i.e. categories have many sub categories → category tree
  2. nested - often used in forum questions when asking about nested lists or maps
  3. has_many - ecto association type
  4. many - short version
  5. child - parent-child relations naming is often used in ecto’s schema
  6. children - plural form
  7. sub - similarily to nested, many times I saw people using words like sublist
  8. struct or map - Elixir’s naming for data structures
  9. sub_field
  10. child_field

Where Next?

Popular in Questions Top

JDanielMartinez
Hi! May someone helps me, please! I have two apps into an umbrella project: the first one is Database, which manages queries, and the se...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 record...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: https://hexdocs.pm/ecto/Ecto.Schema.html#module-...
New
pgiesin
This should be a simple problem but I just can’t seem to figure it out. I have a standalone Elixir app that won’t find the database. Dep...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
chensan
I have a User schema with a :from_id field set to type :string: defmodule TweetBot.Repo.Migrations.CreateUsers do use Ecto.Migration ...
New
ovidiubadita
Hey all, I discovered Elixir and I love it. I always wanted to learn a functional programming and I intended to go for Haskell, but afte...
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New

Other popular topics Top

Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
quazar
How to set Jason to encode all fields in ecto schema, I don’t care about security and implementing only is taking long list of attributes...
New
chrismccord
As promised, the first release candidate of Phoenix 1.3.0 is out! This release focuses on code generators with improved project structure...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? https://hexdocs.pm/ecto/Ecto.Repo.h...
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 42633 214
New
ovidiubadita
Hey all, I discovered Elixir and I love it. I always wanted to learn a functional programming and I intended to go for Haskell, but afte...
New
Fl4m3Ph03n1x
About me? ( if you have nothing better to do than reading about some random guy in the internet :stuck_out_tongue: ) Hello all, this is ...
New
aesmail
Hello guys, I have finally made it. I created an admin interface for a framework. It’s been on my todo list for years and with the curre...
New

We're in Beta

About us Mission Statement