What does something like this def cancelled, do: @cancelled do

what does a single line like this def cancelled, do: @cancelled do.

this is a single line of code, but don’t understand what it does or how it works. I see lots of code like this. unfortunately, the coders did not see fit to document such likes of code.

here is a complete section as the above:

def cancelled, do: @cancelled
** def completed, do: @completed**
** def reassigned, do: @reassigned**
** def snoozed, do: @snoozed**
** def started, do: @started**
** def new, do: @new**

** def complete_statuses, do: [@completed, @cancelled]**
** def incomplete_statuses, do: [@new, @started, @snoozed, @reassigned]**

Thanks! :LC

@foo is syntax for a module attribute
Module attributes are like compile time constants, they get evaluated while the module is being compiled, and the resulting value is embedded right into the final code itself in all the places where it’s used

If you have this module:

defmodule A do
  @foo Date.utc_today

  def bar, do: @foo
end

Then when the module is being compiled, the occurrences of @foo will be replaced with the date at which the module was compiled. So for instance if you compile it today, calling A.bar will return ~D[2021-07-22].

The code you shared is the same. Somewhere in your module these module attributes are being defined, and when the module is compiled all the occurrences of that module attribute will be replaced with the value it was assigned to.

People often use it when there are multiple functions that use a single configurable value that won’t change at runtime(ie once the program is live and running), or when there are values that can be resolved at compile time, so you pay the price of computing it during compilation and then at runtime getting the value is silly fast(because it has already been computed). There are more advanced use cases but I believe this should give you the general idea and will get you quite far.

9 Likes

A quick github search yields this EWallet Elixir library file

As @doorgan mentions, there are other more advanced uses, but in this particular case the coders are just using them to store the actual status strings, such as @pending "pending", then using that constant in other places to set up the schema module:

@statuses [@pending, @approved, @rejected, @confirmed, @cancelled, @failed, @expired]

def pending, do: @pending
...
field(:status, :string, default: @pending)
...
TransactionConsumption
    |> where([t], t.status == @pending)
...
def cancellable?(consumption) do
    consumption.status == @pending
end

In this way if there is ever a need to change the actual string, you do it in one place and it will be changed everywhere during compile time.

To answer you specific question, this function definition def pending, do: @pending sets up a clear API that gives library users a constant means to rely on the value returned by def pending, and hence the concept of pending transaction as coded in the library, instead of the actual "pending" value.

3 Likes

Thank you @doorgan clear and great explanation. Yes those attributes are being used just as you describe.

again, like @doorgan’s explanation, your explanation completes the description very nicely. my confusion originated from the fact that my attributes are being used only once on the code I’m reviewing/editing so I questioned their use but with these explanations now my code makes more sense.

I assume the focus for these attributes is modules only and these constants/attributes do not cross modules and or files.

again, thank you for these great explanations.

2 Likes

Excellent, glad to help and provide concrete code that we could review to understand the intention.

That’s right, in these cases you’re actually creating custom attributes by using the @/1 Kernel macro

…such attributes are not stored in the module by default since it is common in Elixir to use custom attributes to store temporary data that will be available at compile-time.

So you can only access them by reading them from inside functions, as per the samples in your OP.

1 Like