Como você verifica o tipo de variável no Elixir


138

No Elixir, como você verifica tipos, como em Python:

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

Li no Elixir que existem verificadores de tipos como 'is_bitstring', 'is_float', 'is_list', 'is_map' etc.

Respostas:


104

Não existe uma maneira direta de obter o tipo de uma variável no Elixir / Erlang.

Você geralmente deseja saber o tipo de uma variável para agir em conformidade; você pode usar as is_*funções para agir com base no tipo de uma variável.

Aprenda você Alguns Erlang tem um bom capítulo sobre como digitar Erlang (e, portanto, Elixir).

A maneira mais idiomática de usar a is_*família de funções provavelmente seria usá-las em correspondências de padrões:

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

3
O Erlang / Elixir realmente não possui informações de tipo armazenado? Eu realmente preciso criar um novo invólucro sobre os tipos existentes para que o idioma possa ser usado? Oo
Dmitry

2
@Dmitry, o que você quer dizer com utilizável? Posso ver um exemplo concreto em que você usaria o resultado de algo parecido typeof(variable)?
whatyouhide

1
Quando um programa sai do tempo de compilação e entra no tempo de execução, todas as informações sobre o que é um objeto são perdidas. Quando quero inspecionar as informações de um programa em execução, a única maneira de saber o que está acontecendo é inspecionando coisas expostas por uma rede de mapas. se as informações de tipo não estiverem disponíveis e eu quiser inspecionar o tipo, custa muito mais para analisar o objeto para obter seu tipo 'do que se o tipo já estivesse exposto. typeof nos permite analisar o sistema em execução e estendê-lo em tempo de execução de uma maneira que permita digitação e polimorfismo.
Dmitry

2
Para ser mais específico; o uso mais útil de typeof é a capacidade de mapear diretamente uma tabela de hash de [type string, function] na lista de incógnitas. Por exemplo; IO.puts não podem ser mapeados foo = [1, "hello", [1, 2, 3]], com código Enum.map(foo, fn(x) -> IO.puts x end)porque [1,2, 3] será lido como caracteres (por que erlang !!?) E mostrará um monte de rostos sorridentes (tente!). então somos forçados a usar a inspeção, mesmo que a inspeção seja necessária apenas se for uma lista; caso contrário, na maioria das vezes, não precisamos dela. typeof permite transformar as declarações if (O (n)) em pesquisas de dicionário (O (1)).
Dmitry

1
@ Dmitry para esse tipo de uso protocolos Elixir seria útil. elixir-lang.org/getting-started/protocols.html Você pode implementar seu próprio Printableprotocolo que envolve e altera o comportamento da impressão, por exemplo, listas de números inteiros. Apenas certifique-se de não usá-lo com o código Erlang - ou você estará coçando a cabeça se perguntando por que, em vez de mensagens, está vendo listas de números inteiros.
Matt Jadczak

168

A partir do elixir 1.2, existe um icomando no iex que lista o tipo e mais de qualquer variável do 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

Se você procurar no código o icomando, verá que isso é implementado por meio de um protocolo.

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

Se você deseja implementar uma função para qualquer tipo de dados no Elixir, a maneira de fazer isso é definir um protocolo e a implementação do protocolo para todos os tipos de dados nos quais você deseja que a função trabalhe. Infelizmente, você não pode usar uma função de protocolo nos guardas. No entanto, um protocolo "tipo" simples seria muito simples de implementar.


1
em 2019 este retorna um erro undefined function i/1- mesmo para info / 1
krivar

1
Isso ainda funciona no Elixir 1.8.1. Você deve ter uma versão muito antiga do elixir instalada.
Fred, o Cão Maravilha Mágico

2
@krivar @ fred-the-magic-wonder-dog ambos estão corretos :). &i/1é uma função ativada IEx.Helpers. Se você colocar &IEx.Helpers.i/1no seu Elixir de baunilha, você gerará um, a CompileErrormenos que tenha incluído :iexcomo aplicativo no seu mix.exs.
popedotninja

39

Também para fins de depuração, se você não estiver no iex, pode chamá-lo diretamente:

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

1
Se você quiser vê-lo em seu log, adicione IO.inspect (IEx.Info.info (5))
Guillaume

24

Outra abordagem é usar a correspondência de padrões. Digamos que você esteja usando o Timex, que usa uma %DateTime{}estrutura, e deseja ver se um elemento é um. Você pode encontrar uma correspondência usando a correspondência de padrões no método

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

def is_a_datetime?(_) do
  false
end

1
ou, como a resposta aceita observou, mas não enfatizou: »Você geralmente deseja saber o tipo de uma variável para agir em conformidade«. no Elixir, você age de acordo com a correspondência de padrões, não com switch/ case.
Mariotomo 06/06/19

18

Vou deixar isso aqui por causa de alguém, com sorte, descobrir uma versão realmente sã. No momento, não há boas respostas para isso chegando no 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"
            true              -> "idunno"
        end    
    end
end

Por uma questão de integridade, casos de teste:

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

Aqui está uma solução com protocolos; Não tenho certeza se eles são mais rápidos (espero que eles não estejam fazendo um loop em todos os tipos), mas é muito feio (e frágil; se eles adicionarem ou removerem um tipo básico ou renomearem, isso será interrompido).

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

Se você realmente deseja um verificador de "tipo", crie um facilmente usando as ferramentas da organização de pedra do filósofo. github.com/philosophers-stone . O fenético ainda está nos primeiros dias, mas pode fazer isso e muito mais.
Fred the Wonder Dog Magia

me vincular facilmente a uma dependência externa? como isso vai melhorar minha capacidade de compartilhar código com amigos? Este é um caminho para 2 problemas.
Dmitry

Obrigado pela edição @aks; Na verdade, eu posso voltar para 4 espaços agora ^ _ ^
Dmitry

15

Acabei de colar o código em 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

Uso inteligente de citação! Quanto mais eu vejo o código Elixir, mais ele me lembra Perl; essa construção ~ w se parece muito com qw //. Gostaria de saber se o Perl tem algum mecanismo inteligente para simular a citação de Lisplike.
Dmitry

Eu me pergunto como a cotação funciona; ele pode ser emulado usando um pré-processador de expressão regular ou requer que um analisador percorra todo o código para fazer a expansão da macro.
Dmitry

1

Me deparei com uma situação precisa verificar o parâmetro precisa ser de determinado tipo. Talvez possa ativar uma maneira melhor.

Como isso:

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

Uso:

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

1

Só porque ninguém mencionou

IO.inspect/1

Saídas para consolar o objeto ... é quase equivalente ao JSON.stringify

Muito útil quando você simplesmente não consegue descobrir como é um objeto em um teste.


4
Não é uma resposta para a pergunta, nem mesmo perto
LowFieldTheory
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.