Как вы проверяете тип переменной в Elixir

В Elixir, как вы проверяете тип, например, в Python:

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

Я читаю в Elixir, есть такие проверки типов, как 'is_bitstring', 'is_float', 'is_list', 'is_map' и т.д., но что, если вы не знаете, какой тип может быть?

Ответ 1

Нет прямого способа получить тип переменной в Elixir/Erlang.

Обычно вы хотите узнать тип переменной, чтобы действовать соответствующим образом; вы можете использовать функции is_*, чтобы действовать в зависимости от типа переменной.

Узнайте, что у вас есть Erlang хорошая глава о вводе в Erlang (и, следовательно, в Elixir).

Самым идиоматичным способом использования семейства функций is_*, вероятно, будет использование их в совпадениях с шаблонами:

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

Ответ 2

Начиная с elixir 1.2 существует команда i в iex, которая будет перечислять тип и больше любой переменной 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 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

Если вы посмотрите в коде для команды i, вы увидите, что это реализовано через протокол.

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

Если вы хотите реализовать функцию для любого типа данных в Elixir, способ сделать это - определить протокол и реализацию протокола для всех типов данных, над которыми вы хотите работать. К сожалению, вы не можете использовать функцию протокола в охранниках. Однако простой "тип" протокола был бы очень прост в реализации.

Ответ 3

Другой подход - использовать сопоставление шаблонов. Предположим, вы используете Timex, который использует структуру %DateTime{}, и вы хотите увидеть, является ли элемент одним. Вы можете найти совпадение, используя сопоставление шаблонов в методе.

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

def is_a_datetime?(_) do
  false
end

Ответ 4

Также для целей отладки, если вы не находитесь в iex, вы можете вызвать его напрямую:

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

Ответ 5

Я просто оставлю это здесь ради того, чтобы кто-то надеялся найти действительно разумную версию. На данный момент нет хороших ответов на этот вопрос в 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

Для полноты, тестовые примеры:

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

Здесь есть решение с протоколами; Я не уверен, что они быстрее (я, конечно, надеюсь, что они не делают цикл по всем типам), но это довольно уродливо (и хрупко, если они добавляют или удаляют базовый тип или переименовывают его, он сломает его).

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

Ответ 6

Я просто вставляю код из 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

Ответ 7

Я столкнулся с ситуацией, когда нужно проверить, что параметр должен быть определенным типом. Может быть, может действовать лучше.

Вот так:

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

Использование:

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