Bagaimana Anda memeriksa jenis variabel di Elixir

138

Di Elixir bagaimana Anda memeriksa jenis seperti di Python:

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

Saya membaca di Elixir ada jenis checker seperti 'is_bitstring', 'is_float', 'is_list', 'is_map' dll, tetapi bagaimana jika Anda tidak tahu apa jenisnya?

Low Kian Seong
sumber

Jawaban:

104

Tidak ada cara langsung untuk mendapatkan jenis variabel di Elixir / Erlang.

Anda biasanya ingin mengetahui jenis variabel untuk bertindak sesuai; Anda bisa menggunakan is_*fungsi untuk bertindak berdasarkan tipe variabel.

Learn You Some Erlang memiliki bab yang bagus tentang mengetik di Erlang (dan karenanya dalam Elixir).

Cara paling idiomatis untuk menggunakan is_*keluarga fungsi mungkin adalah menggunakannya dalam pencocokan pola:

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
apa yang kamu sembunyikan
sumber
3
Apakah Erlang / Elixir benar-benar tidak memiliki informasi tipe yang tersimpan? Apakah saya benar-benar perlu membuat pembungkus yang sama sekali baru dari jenis yang ada agar bahasa dapat digunakan? Oo
Dmitry
2
@Lakukan apa yang Anda maksud dengan yang bisa digunakan? Bisakah saya melihat contoh konkret di mana Anda akan menggunakan hasil dari sesuatu seperti typeof(variable)?
sembunyikan
1
Ketika suatu program meninggalkan waktu kompilasi dan memasuki runtime, semua informasi tentang apa beberapa objek hilang. Ketika saya ingin memeriksa informasi program yang sedang berjalan, satu-satunya cara untuk mengetahui apa yang sedang terjadi adalah dengan memeriksa hal-hal yang diekspos melalui jaringan peta. jika informasi jenis tidak tersedia, dan saya ingin memeriksa jenisnya, biayanya jauh lebih besar untuk menganalisis objek untuk mendapatkan jenisnya daripada jika jenisnya sudah terbuka. typeof memungkinkan kita untuk menganalisis sistem yang sedang berjalan dan memperluasnya pada saat runtime dengan cara yang memungkinkan pengetikan ketik dan polimorfisme.
Dmitry
2
Untuk lebih spesifik; penggunaan typeof yang paling berguna adalah kemampuan untuk secara langsung memetakan tabel hash dari [type string, function] ke daftar yang tidak diketahui. Sebagai contoh; IO.puts tidak dapat dipetakan foo = [1, "hello", [1, 2, 3]], dengan kode Enum.map(foo, fn(x) -> IO.puts x end)karena [1,2, 3] akan dibaca sebagai karakter (mengapa erlang !!?), Dan akan menunjukkan kepada Anda banyak wajah tersenyum (coba saja!). jadi kita dipaksa untuk menggunakan inspeksi meskipun inspeksi hanya diperlukan jika itu daftar, jika tidak, sebagian besar waktu kita tidak membutuhkannya. typeof memungkinkan kita mengubah jika pernyataan (O (n)) menjadi pencarian kamus (O (1)).
Dmitry
1
@Dmitry untuk jenis penggunaan protokol Elixir akan berguna. elixir-lang.org/getting-started/protocols.html Anda dapat mengimplementasikan Printableprotokol Anda sendiri yang membungkus dan mengubah perilaku pencetakan misalnya daftar bilangan bulat. Pastikan Anda tidak menggunakannya dengan kode Erlang — atau Anda akan menggaruk-garuk kepala bertanya-tanya mengapa bukannya pesan yang Anda lihat daftar bilangan bulat.
Matt Jadczak
168

Mulai di elixir 1.2 ada iperintah dalam iex yang akan mencantumkan jenis dan lebih dari variabel 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

Jika Anda melihat dalam kode untuk iperintah Anda akan melihat bahwa ini diimplementasikan melalui Protokol.

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

Jika Anda ingin mengimplementasikan fungsi untuk tipe data apa pun di Elixir, cara untuk melakukannya adalah dengan mendefinisikan Protokol dan implementasi Protokol untuk semua tipe data yang Anda inginkan fungsi berfungsi. Sayangnya, Anda tidak dapat menggunakan fungsi Protokol dalam penjaga. Namun, protokol "tipe" sederhana akan sangat mudah diimplementasikan.

Fred Anjing Ajaib Ajaib
sumber
1
pada tahun 2019 ini mengembalikan kesalahan undefined function i/1- sama untuk info / 1
krivar
1
Ini masih berfungsi di Elixir 1.8.1. Anda harus menginstal elixir versi lama.
Fred the Magic Wonder Dog
2
@krivar @ fred-the-magic-wonder-dog kalian berdua benar :). &i/1adalah fungsi aktif IEx.Helpers. Jika Anda memasukkan &IEx.Helpers.i/1Elixir vanilla Anda ke dalamnya, Anda akan menghasilkan CompileErrorkecuali Anda telah dimasukkan :iexsebagai aplikasi di mix.exs.
popedotninja
39

Juga untuk tujuan debugging, jika Anda tidak di iex, Anda dapat memanggilnya langsung:

IEx.Info.info(5)
=> ["Data type": "Integer", "Reference modules": "Integer"]
atomkirk
sumber
1
Jika Anda ingin melihatnya di log Anda, tambahkan IO.inspect (IEx.Info.info (5))
Guillaume
24

Pendekatan lain adalah menggunakan pencocokan pola. Katakanlah Anda menggunakan Timex, yang menggunakan %DateTime{}struct, dan Anda ingin melihat apakah suatu elemen adalah satu. Anda dapat menemukan kecocokan menggunakan pencocokan pola dalam metode ini.

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

def is_a_datetime?(_) do
  false
end
popedotninja
sumber
1
atau, seperti jawaban yang diterima tetapi tidak menekankan: »Anda biasanya ingin mengetahui jenis variabel agar dapat bertindak sesuai«. di Elixir Anda bertindak sesuai dengan pencocokan pola, bukan oleh switch/ case.
mariotomo
18

Saya hanya akan meninggalkan ini di sini demi seseorang semoga mencari tahu versi yang benar-benar waras. Saat ini tidak ada jawaban yang baik untuk ini di 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

Demi kelengkapan, uji kasus:

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

Inilah solusi dengan protokol; Saya tidak yakin apakah mereka lebih cepat (saya yakin berharap mereka tidak melakukan loop atas semua jenis), tetapi itu cukup jelek (dan rapuh; jika mereka menambah atau menghapus tipe dasar atau mengganti nama, itu akan merusaknya).

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
Dmitry
sumber
Jika Anda benar-benar menginginkan pemeriksa "tipe", Anda dapat membuatnya dengan mudah menggunakan alat di org batu filsuf. github.com/philosophers-stone . Fenetik masih dalam masa awal, tetapi dapat melakukan ini dan banyak lagi.
Fred Anjing Ajaib Ajaib
mudah mengikat diri pada ketergantungan eksternal? bagaimana cara meningkatkan kemampuan saya untuk berbagi kode dengan teman? Ini adalah jalan menuju 2 masalah.
Dmitry
Terima kasih atas edit @aks; Saya benar-benar dapat kembali ke 4 spasi sekarang ^ _ ^
Dmitry
15

Saya hanya menempelkan kode dari 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
pengguna3197999
sumber
Penggunaan kutipan dengan cerdas! Semakin saya melihat kode Elixir semakin mengingatkan saya pada Perl; that ~ w construct terlihat sangat mirip dengan qw //. Saya ingin tahu apakah Perl memiliki beberapa mekanisme pintar untuk mensimulasikan kutipan Lisplike.
Dmitry
Saya bertanya-tanya bagaimana cara kerja kutipan; dapat ditiru menggunakan preprocessor ekspresi reguler, atau apakah itu memerlukan parser walk melalui seluruh kode untuk melakukan ekspansi makro.
Dmitry
1

Saya menemukan situasi perlu memeriksa parameter perlu jenis tertentu. Mungkin bisa aktif dengan cara yang lebih baik.

Seperti ini:

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

Pemakaian:

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

Hanya karena tidak ada yang menyebutkannya

IO.inspect/1

Output untuk menghibur objek ... hampir sama dengan JSON.stringify

Sangat membantu ketika Anda tidak bisa seumur hidup mencari tahu seperti apa suatu benda dalam sebuah tes.

John Nicholas
sumber
4
Bukan jawaban untuk pertanyaan, bahkan tidak menutup
LowFieldTheory