Matching with constants

in a controller i have this funciton:

def create(conn, %{ "type" => "7", "photo_id" => photo_id }, current_user, _claims) do

I want use a constant instead of “7” value, but if i define:

def type_vote, do: "7"

def create(conn, %{ "type" => type_vote, "photo_id" => photo_id }, current_user, _claims) do

I receive an error because type_vote is a function.
How can i do?
Thanks

I don’t think you can put functions in other functions’ heads. So for something like this you might want to use guards.

In your case I think it would be something like this

def type_vote, do: ...

def action(%{params: params, assigns: assigns} = conn, _) do
  args = [conn, params, assigns[:current_user], assigns[:claims], type_vote()]
  apply(__MODULE__, action_name(conn), args)
end

def create(conn, %{"type" => type, "photo_id" => photo_id}, current_user, _claims, type_vote)
  when type == type_vote do
  ...
end

Or maybe (it probably won’t work though)

def create(conn, %{"type" => type_vote, "photo_id" => photo_id}, current_user, _claims, ^type_vote) do
  ...
end

The way to emulate in-module constants in Elixir is by using module attributes:

@type_vote "7"
def create(conn, %{"type" => @type_vote, "photo_id" => photo_id}, current_user, _claims) do
3 Likes

i have it but is defined in another module.

defmodule Myapp.Action.Activity do
..
  @vote "7"
  @follow_user "8"
  def type_follow_user, do: @follow_user
  def type_vote, do: @vote

and the controller:

def create(conn, %{ "type" => Activity.type_vote, "photo_id" => photo_id }, current_user, _claims) do

Who can i use this attributes in another module without expose them with functions?

Thanks

Can’t you use a module attribute as a constant?

defmodule MyModule
  @type_vote "7"

  def create(conn, %{ "type" => @type_vote, "photo_id" => photo_id }, current_user, _claims) do
  end
end

Note that module attributes are defined like this:
@attr "value"
not like this:
@attr = value

No. Module-Attributes can’t be used in another module. At least not without certain coding overhead.

The following is not tested, but should at least push you into the direction:

defmodule A do
  Module.register_attribute __MODULE__, :vote_type, persist: false
  @vote_type "7"
end

defmodule B do
  require A # make sure `A` is compiled before `B`
  @vote_type Module.get_attribute(A, :vote_type)

  def create(conn, %{"type": @vote_type, ...}, do: ...
end

This way, you should be able to do them centralised. Another way would be to use use:

defmodule A do
  defmacro __using__([]) do
    quote do
      @vote_type "7"
    end
  end
end

defmodule B do
  use A

  def create(conn, %{"type": @vote_type, ...}, do: ...
end
1 Like

sorry i wrote wrong

Instead of that, do this:

defmacro type_vote, do: "7"

That will then be inlined and will work in function heads and across modules, a defmacro is called at compile-time and returns an AST, in this case "7" is a valid AST so it will work as expected. :slight_smile:

There are enum-style libraries that make this more obvious too.

2 Likes