Muhammad Lukman Low
Muhammad Lukman Low

Reputation: 8523

How do you check for the type of variable in Elixir

In Elixir how do you check for type such as in Python:

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

I read in Elixir there are type checkers such as 'is_bitstring', 'is_float', 'is_list', 'is_map' etc, but what if you have no idea what the type could be ?

Upvotes: 178

Views: 123116

Answers (12)

nelsonic
nelsonic

Reputation: 33134

Useful.typeof/1

Inspired by this thread we added typeof/1 to our library of Useful functions.
Does exactly what you expect.

Add it to your deps in mix.exs:

def deps do
  [
    {:useful, "~> 1.15.0"}
  ]
end

Usage:

iex> pi = 3.14159
iex> Useful.typeof(pi)
"float"

iex> fun = fn (a, b) -> a + b end
iex> Useful.typeof(fun)
"function"

iex> Useful.typeof(&Useful.typeof/1)
"function"

iex> int = 42
iex> Useful.typeof(int)
"integer"

iex> list = [1,2,3,4]
iex> Useful.typeof(list)
"list"

Docs: https://hexdocs.pm/useful/Useful.html#typeof/1
As always, feedback/contributions/improvements very much welcome.

Upvotes: 4

mit
mit

Reputation: 11251

You can use IEx.Info.info in iex and in your code. The result is a list of tuples, so the actual type information, which is contained as a string, needs some unpacking:

IEx example

iex(1)> "abc" |> IEx.Info.info |> hd |> elem(1)
"BitString"

In your code

def datatype(myvar) do
  myvar |> IEx.Info.info |> hd |> elem(1)
end

var1 = {1,2,3}
IO.puts(datatype(var1)) # will print the string "Tuple"

Upvotes: 4

Dmytro
Dmytro

Reputation: 5213

I'll just leave this here for the sake of somebody hopefully figuring out an actually sane version. At the moment there are no good answers to this coming up on google...

defmodule Util do
    def typeof(a) do
        cond do
            is_float(a)    -> "float"
            is_number(a)   -> "number"
            is_atom(a)     -> "atom"
            is_boolean(a)  -> "boolean"
            is_binary(a)   -> "binary"
            is_function(a) -> "function"
            is_list(a)     -> "list"
            is_tuple(a)    -> "tuple"
            true           -> "idunno"
        end    
    end
end

For the sake of completeness, test cases:

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

Here's a solution with protocols; I am not sure if they are faster(I sure hope they are not doing a loop over all types), but it is pretty ugly(and fragile; if they add or remove a basic type or rename, it will break it).

defprotocol Typeable, do: def typeof(a)
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

Upvotes: 40

mrroot5
mrroot5

Reputation: 1951

Python type using an Elixir Protocol

An approximation with a module implementation is @Dimitry answer here.

Protocol source code

The code is big for an answer so I created a github gist. Protocol implementation.

I am a noob on testing and Elixir so any improvements are really appreciated.

Docs

Upvotes: 1

M-Dahab
M-Dahab

Reputation: 439

Based on the implementation here, you can pattern match against the tuple returned by IEx.Info.info/1:

defmodule Type do
  def of(x) do
    [{_, type} | _] = IEx.Info.info(x)

    type
  end
end

Type.of(1) # "Integer"

Upvotes: 2

devoutsalsa
devoutsalsa

Reputation: 1200

Another approach is to use pattern matching. Say you're using Timex, which uses a %DateTime{} struct, and you want to see if an element is one. You can find a match using pattern matching in the method.

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

def datetime?(_) do
  false
end

Upvotes: 29

John Nicholas
John Nicholas

Reputation: 4836

Just because nobody has mentioned it

IO.inspect/1

Outputs to console the object ... its almost equiv to JSON.stringify

Very helpful when you just cannot for the life of you figure out what an object looks like in a test.

Upvotes: -2

Bingoabs
Bingoabs

Reputation: 609

I came across a situation need to check the parameter need to be certain type. Maybe can active a better way.

Like this:

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

Usage:

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

Upvotes: 1

Rupert
Rupert

Reputation: 539

I just paste the code from 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

Upvotes: 20

atomkirk
atomkirk

Reputation: 3791

Also for debugging purposes, if you're not in iex, you can call it directly:

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

Upvotes: 70

Starting in elixir 1.2 there is an i command in iex that will list the type and more of any Elixir variable.

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

If you look in the code for the i command you'll see that this is implemented via a Protocol.

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

If you want to implement a function for any Data type in Elixir, the way to do that is to define a Protocol and implementation of the Protocol for all the data types you want the function to work on. Unfortunately, you can't use a Protocol function in guards. However, a simple "type" protocol would be very straightforward to implement.

Upvotes: 222

whatyouhide
whatyouhide

Reputation: 16781

There's no direct way to get the type of a variable in Elixir/Erlang.

You usually want to know the type of a variable in order to act accordingly; you can use the is_* functions in order to act based on the type of a variable.

Learn You Some Erlang has a nice chapter about typing in Erlang (and thus in Elixir).

The most idiomatic way to use the is_* family of functions would probably be to use them in pattern matches:

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

Upvotes: 126

Related Questions