README.md

<!--
SPDX-FileCopyrightText: 2024 Łukasz Niemier <#@hauleth.dev>

SPDX-License-Identifier: MIT
-->

# defconstant

Helper macros for defining constants in Elixir code

## Installation

```elixir
def deps do
  [
    {:defconstant, "~> 2.0.0"}
  ]
end
```

Optionally add `:defconstant` to your `.formatter.exs` to have `defconst`
formatted without parens:

```
[
  import_deps: [
    :defconstant,
    ...
  ],
  ...
]
```

## Usage

This library provides 2 macros:

- `defconst` - provided body will be evaluated *at compile* time
- `defonce` - provided body will be evaluated *at runtime*, but only once. After
  that it will be cached and served from cache.

Both helper macros allows defining only 0-ary functions (functions that take no
arguments).

For details see the full docs on [hexdocs.pm](https://hexdocs.pm/defconstant/Defconstant.html)

### Example usage

``` elixir
defmodule Demo.MyConst do
  import Defconstant

  defconst the_answer do
    42
  end

  # NOTE: For real code you'd use `:math.pi` instead
  defconst pi do
    3.14159
  end

  defonce calculated_at do
    NaiveDateTime.utc_now()
  end

  def run_calculations(circumference) do
    circle_radius = circumference / 2 * pi()
    "radius is #{circle_radius} and was first calculated at #{inspect calculated_at()}"
  end
end
```

And the constants can be used from another module (e.g. `Demo.MyConst.the_answer()` will return `42`)

## Why

Elixir and Erlang/OTP don't have true constants. They do have module attributes,
which are often used like constants, but module attributes can be modified so
they aren't truly constants. For example:

``` elixir
defmodule ModuleAttributeDemo do
  @pi 3.14159
  def pi, do: @pi

  @pi 33
  def radius(circumference), do: circumference / 2 * @pi
end
```

Calling `ModuleAttributeDemo.radius(5)` will use `33` as the value for `@pi`
instead of `3.14159`. With `defconst` this can be avoided because you will
receive a warning when re-defining a constant.

## License

MIT