web-dev-qa-db-fra.com

Comment vérifier le type de variable dans Elixir

Dans Elixir, comment vérifier le type tel qu'en Python:

>>> a = "test"
>>> type(a)
<type 'str'>
>>> b =10
>>> type(b)
<type 'int'>

J'ai lu dans Elixir qu'il y a des vérificateurs de type tels que 'is_bitstring', 'is_float', 'is_list', 'is_map', etc.

110
Low Kian Seong

Il n'y a pas de moyen direct d'obtenir le type d'une variable dans Elixir/Erlang.

Vous voulez généralement connaître le type d'une variable afin d'agir en conséquence; vous pouvez utiliser les fonctions is_* pour agir en fonction du type d'une variable.

Learn You Some Erlang a un beau chapitre à propos de la frappe à Erlang (et donc à Elixir).

La manière la plus idiomatique d’utiliser la famille de fonctions is_* serait probablement de les utiliser dans des correspondances de modèle:

def my_fun(arg) when is_map(arg), do: ...
def my_fun(arg) when is_list(arg), do: ...
def my_fun(arg) when is_integer(arg), do: ...
# ...and so on
80
whatyouhide

À partir de elixir 1.2, il existe une commande i dans iex qui répertorie le type et plus d'une variable Elixir. 

iex> foo = "a string" 
iex> i foo 
Term
 "a string"
Data type
 BitString
Byte size
 8
Description
 This is a string: a UTF-8 encoded binary. It's printed surrounded by
 "double quotes" because all UTF-8 encoded codepoints in it are        printable.
Raw representation
  <<97, 32, 115, 116, 114, 105, 110, 103>>
Reference modules
  String, :binary

Si vous regardez dans le code de la commande i, vous verrez que cela est implémenté via un protocole. 

https://github.com/elixir-lang/elixir/blob/master/lib/iex/lib/iex/info.ex

Si vous souhaitez implémenter une fonction pour tout type de données dans Elixir, vous devez définir un protocole et l'implémenter du protocole pour tous les types de données sur lesquels vous souhaitez que la fonction fonctionne. Malheureusement, vous ne pouvez pas utiliser une fonction de protocole dans les gardes. Cependant, un protocole simple "type" serait très simple à mettre en œuvre. 

136

Une autre approche consiste à utiliser une correspondance de modèle. Supposons que vous utilisez Timex, qui utilise une structure %DateTime{}, et que vous voulez voir si un élément en est un. Vous pouvez trouver une correspondance à l'aide de la correspondance de modèle dans la méthode.

def is_a_datetime?(%DateTime{}) do
  true
end

def is_a_datetime?(_) do
  false
end
20
popedotninja

Également à des fins de débogage, si vous n'êtes pas dans iex, vous pouvez l'appeler directement:

IEx.Info.info(5)
=> ["Data type": "Integer", "Reference modules": "Integer"]
17
atomkirk

Je vais laisser cela ici pour que quelqu'un puisse espérer trouver une version réellement saine. Pour le moment, il n'y a pas de bonne réponse à cette question sur Google ...

defmodule Util do
    def typeof(self) do
        cond do
            is_float(self)    -> "float"
            is_number(self)   -> "number"
            is_atom(self)     -> "atom"
            is_boolean(self)  -> "boolean"
            is_binary(self)   -> "binary"
            is_function(self) -> "function"
            is_list(self)     -> "list"
            is_Tuple(self)    -> "Tuple"
            _                 -> "idunno"
        end    
    end
end

Par souci d'exhaustivité, cas de test:

cases = [
    1.337, 
    1337, 
    :'1337', 
    true, 
    <<1, 3, 3, 7>>, 
    (fn(x) -> x end), 
    {1, 3, 3, 7}
]

Enum.each cases, fn(case) -> 
    IO.puts (inspect case) <> " is a " <> (Util.typeof case)
end

Voici une solution avec des protocoles; Je ne sais pas s'ils sont plus rapides (j'espère bien qu'ils ne font pas une boucle sur tous les types), mais c'est assez moche (et fragile; s'ils ajoutent ou suppriment un type de base ou le renomment, cela le cassera).

defprotocol Typeable, do: def typeof(self)
defimpl Typeable, for: Atom, do: def typeof(_), do: "Atom"
defimpl Typeable, for: BitString, do: def typeof(_), do: "BitString"
defimpl Typeable, for: Float, do: def typeof(_), do: "Float"
defimpl Typeable, for: Function, do: def typeof(_), do: "Function"
defimpl Typeable, for: Integer, do: def typeof(_), do: "Integer"
defimpl Typeable, for: List, do: def typeof(_), do: "List"
defimpl Typeable, for: Map, do: def typeof(_), do: "Map"
defimpl Typeable, for: PID, do: def typeof(_), do: "PID"
defimpl Typeable, for: Port, do: def typeof(_), do: "Port"
defimpl Typeable, for: Reference, do: def typeof(_), do: "Reference"
defimpl Typeable, for: Tuple, do: def typeof(_), do: "Tuple"

IO.puts Typeable.typeof "Hi"
IO.puts Typeable.typeof :ok
12
Dmitry

Je viens de coller le code de https://elixirforum.com/t/just-created-a-typeof-module/2583/5 :)

defmodule Util do
  types = ~w[function nil integer binary bitstring list map float atom Tuple pid port reference]
  for type <- types do
    def typeof(x) when unquote(:"is_#{type}")(x), do: unquote(type)
  end
end
10
user3197999

Juste parce que personne n'en a parlé

IO.inspect/1

Sorties pour consolider l'objet ... c'est presque l'équivalent de JSON.stringify

Très utile lorsque vous ne pouvez pas comprendre la nature d'un objet dans un test.

2
John Nicholas

Je suis tombé sur une situation nécessitant de vérifier le paramètre doit être certain type. Peut-être peut-être actif d'une meilleure façon.

Comme ça:

@required [{"body", "binary"},{"fee", "integer"}, ...]
defp match_desire?({value, type}) do
  apply(Kernel, :"is_#{type}", [value])
end

Usage:

Enum.map(@required, &(match_desire?/1))
0
Bingoabs