web-dev-qa-db-fra.com

Comment définissez-vous les constantes dans les modules Elixir?

En Ruby, si l'on définissait des constantes dans les classes, elles les définiraient en utilisant toutes les majuscules. Par exemple:

class MyClass
  MY_FAVORITE_NUMBER = 13
end

Comment faites-vous cela dans Elixir? Et si aucun équivalent n'existe, comment contourner le problème des nombres magiques dans Elixir?

35
Martimatix

Vous pouvez ajouter votre nom de variable avec @:

defmodule MyModule do
  @my_favorite_number 13
end

Voici les docs

44
AbM

Les modules Elixir peuvent avoir des métadonnées associées. Chaque élément des métadonnées est appelé un attribut et est accessible par son nom. Vous le définissez à l'intérieur d'un module en utilisant @name value. Et est accessible en tant que @name

defmodule Example
  @site 'StackOverflow' #defining attribute

  def get_site do
    @site #access attribute
  end 
end

N'oubliez pas que cela ne fonctionne qu'au niveau supérieur d'un module et que vous ne pouvez pas définir un attribut de module dans une définition de fonction.

8
coderVishal

Une autre approche pour définir les constantes est celle que j'ai adoptée avec les fichiers d'en-tête wxErlang. Autrement dit, vous pouvez simplement définir une fonction de ligne unique qui renvoie la valeur constante pour vous. Ainsi:

  def wxHORIZONTAL, do: 4
  def wxVERTICAL, do: 8
  def wxBOTH, do: (wxHORIZONTAL ||| wxVERTICAL)

et voici un autre exemple du même code source:

 # From "defs.h": wxDirection
  def wxLEFT, do: 16
  def wxRIGHT, do: 32
  def wxUP, do: 64
  def wxDOWN, do: 128
  def wxTOP, do: wxUP
  def wxBOTTOM, do: wxDOWN
  def wxNORTH, do: wxUP
  def wxSOUTH, do: wxDOWN
  def wxWEST, do: wxLEFT
  def wxEAST, do: wxRIGHT
  def wxALL, do: (wxUP ||| wxDOWN ||| wxRIGHT ||| wxLEFT)

Comme vous pouvez le voir, il est un peu plus facile de définir une constante en termes d'une autre constante. Et quand je veux ces constantes dans un module différent, tout ce que je dois faire est de require WxConstants en haut du module. Cela facilite beaucoup la définition d'une constante en un seul endroit et son utilisation dans plusieurs autres - cela aide beaucoup avec DRY.

Au fait, vous pouvez voir le repo ici si vous êtes curieux.

Comme je l'ai dit, j'ajoute cette réponse principalement par souci d'exhaustivité.

8
Onorio Catenacci

Peut-être que vous définissez un fichier de module de constantes et que vous pouvez y définir des macros comme ceci

defmodule MyApp.Constants do
  defmacro const_a do
    quote do: "A"
  end
end

Vous l'utilisez en l'important dans n'importe quel autre module

defmodule MyApp.ModuleA do
  import MyApp.Constants

  def get_const_a do
    const_a()
  end 
end

L'avantage est également que vous n'encourez aucun coût d'exécution ainsi que l'avantage de l'utiliser en cas de correspondance

case something do
  const_a() -> do_something
  _ -> do_something_else
end
4
sat

En regardant autour de github, je vois que cela est utilisé, ce qui me plaît. Permet d'accéder à la constante à partir d'autres modules.

ModuleA
  @my_constant 23

  defmacro my_constant, do: @my_constant


ModuleB
  Require ModuleA
  @my_constant ModuleA.my_constant

Encore une fois une solution d'élixir simple et fascinante

3
Aleksandarf

Je voulais ajouter comment j'ai commencé à faire des constantes, ce qui est similaire à la réponse de @Onorio Catenacci, mais utilise des citations:

defmodule IbGib.Constants do

  @doc """
  Use this with `use IbGib.Constants, :ib_gib`
  """
  def ib_gib do
    quote do
      defp delim, do: "^"
      defp min_id_length, do: 1
      # etc...
    end
  end

  @doc """
  Use this with `use IbGib.Constants, :error_msgs`
  """
  def error_msgs do
    quote do
      defp emsg_invalid_relations do
        "Something about the rel8ns is invalid. :/"
      end
      # etc...
    end
  end

  @doc """
  When used, dispatch to the appropriate controller/view/etc.
  """
  defmacro __using__(which) when is_atom(which) do
    apply(__MODULE__, which, [])
  end
end

Et puis vous l'utilisez comme ceci en haut du module où vous voulez les consommer:

use IbGib.Constants, :ib_gib # < specifies only the ib_gib constants
use IbGib.Constants, :error_msgs

# ... then in some function
Logger.error emsg_invalid_relations

J'ai compris comment Phoenix importait/utilisait des clauses avec MyApp.Web. Je ne suis pas du tout près d'un expert Elixir, mais avec cette méthode, vous pouvez importer uniquement les constantes que vous souhaitez et vous n'avez pas besoin de les préfixer avec un espace de noms/portée. De cette façon, vous pouvez facilement sélectionner les groupes individuels de constantes.

Avec des fonctions simples, (je pense), vous devrez les diviser en plusieurs modules, puis importer le module.

Je ne connais pas les ramifications d'optimisation de ces fonctions vs module direct, mais je pensais que c'était assez soigné - en particulier pour la pratique entre les différentes façons d'importer des choses dans Elixir (import, use, alias, require est très déroutant en tant que débutant venant d'autres langues où il s'agit d'une seule instruction using ou import).

EDIT: J'ai changé les déclarations constantes def en defp. En effet, lorsque plusieurs modules import le fichier de constantes, il y a un conflit d'ambiguïté. Le fait de les remplacer par des fonctions de portée privée évite ce conflit. Ainsi, chaque module possède sa propre "copie privée" de la même constante.

1
user4275029