Come si controlla il tipo di variabile in Elisir?


138

In Elisir come si controlla il tipo come in Python:

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

Ho letto su Elisir che ci sono controlli di tipo come 'is_bitstring', 'is_float', 'is_list', 'is_map' ecc, ma cosa succede se non hai idea di quale tipo potrebbe essere?

Risposte:


104

Non esiste un modo diretto per ottenere il tipo di variabile in Elisir / Erlang.

Di solito vuoi conoscere il tipo di una variabile per agire di conseguenza; puoi usare le is_*funzioni per agire in base al tipo di una variabile.

Impara un po 'di Erlang ha un bel capitolo su come scrivere in Erlang (e quindi in Elisir).

Il modo più idiomatico di usare la is_*famiglia di funzioni sarebbe probabilmente quello di usarli nelle corrispondenze dei modelli:

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
Erlang / Elixir non ha davvero informazioni sul tipo memorizzate? Devo davvero creare un wrapper completamente nuovo rispetto ai tipi esistenti affinché la lingua sia utilizzabile? Oo
Dmitry,

2
@Dmitry cosa intendi per usabile? Posso vedere un esempio concreto in cui useresti il ​​risultato di qualcosa del genere typeof(variable)?
quant'è il

1
Quando un programma lascia il tempo di compilazione ed entra in runtime, tutte le informazioni su ciò che un oggetto è perso vengono perse. Quando voglio ispezionare le informazioni di un programma in esecuzione, l'unico modo per sapere cosa sta succedendo è ispezionare le cose che sono esposte attraverso una rete di mappe. se le informazioni sul tipo non sono disponibili e voglio ispezionarlo, costa molto di più analizzare l'oggetto per ottenere il suo 'tipo che se il tipo fosse già esposto. typeof ci consente di analizzare il sistema in esecuzione e di estenderlo in fase di esecuzione in un modo che consenta il controllo dei caratteri e il polimorfismo.
Dmitry

2
Per essere più specifici; l'uso più utile di typeof è la possibilità di mappare direttamente una tabella hash di [tipo stringa, funzione] su un elenco di incognite. Per esempio; IO.puts non può essere mappato foo = [1, "hello", [1, 2, 3]], con il codice Enum.map(foo, fn(x) -> IO.puts x end)perché [1,2, 3] verrà letto come carattere (perché erlang !!?) E ti mostrerà un sacco di facce sorridenti (provalo!). quindi siamo costretti a usare inspect anche se inspect è necessario solo se si tratta di un elenco, altrimenti la maggior parte delle volte non ne abbiamo bisogno. typeof ci consente di trasformare if istruzioni (O (n)) in ricerche nel dizionario (O (1)).
Dmitry,

1
@Dmitry per quel tipo di utilizzo I protocolli elisir sarebbero utili. elixir-lang.org/getting-started/protocols.html È possibile implementare il proprio Printableprotocollo che avvolge e modifica il comportamento della stampa, ad esempio elenchi di numeri interi. Assicurati solo di non usarlo con il codice Erlang, o ti gratterai la testa chiedendoti perché invece dei messaggi vedi elenchi di numeri interi.
Matt Jadczak il

168

A partire da elisir 1.2 c'è un icomando in iex che elencherà il tipo e altro di ogni variabile elisir.

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 cerchi il codice per il icomando vedrai che questo è implementato tramite un protocollo.

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

Se desideri implementare una funzione per qualsiasi tipo di dati in Elisir, il modo per farlo è quello di definire un protocollo e l'implementazione del protocollo per tutti i tipi di dati su cui vuoi che la funzione funzioni. Sfortunatamente, non è possibile utilizzare una funzione Protocol in guards. Tuttavia, un semplice protocollo di "tipo" sarebbe molto semplice da implementare.


1
nel 2019 questo restituisce un errore undefined function i/1- lo stesso per info / 1
krivar il

1
Funziona ancora con Elixir 1.8.1. Devi avere una versione molto vecchia di elisir installata.
Fred the Magic Wonder Dog,

2
@krivar @ fred-the-magic-wonder-dog hai ragione entrambi :). &i/1è una funzione attiva IEx.Helpers. Se lo metti &IEx.Helpers.i/1nel tuo Elisir alla vaniglia, genererai un a CompileErrormeno che tu non abbia incluso :iexcome applicazione nel tuo mix.exs.
popedotninja,

39

Anche per scopi di debug, se non sei in iex, puoi chiamarlo direttamente:

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

1
Se vuoi vederlo nel tuo registro aggiungi IO.inspect (IEx.Info.info (5))
Guillaume

24

Un altro approccio consiste nell'utilizzare la corrispondenza dei modelli. Supponiamo che tu stia utilizzando Timex, che utilizza una %DateTime{}struttura e che vuoi vedere se un elemento è uno. Puoi trovare una corrispondenza usando la corrispondenza del modello nel metodo.

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

def is_a_datetime?(_) do
  false
end

1
oppure, come ha osservato la risposta accettata ma non ha sottolineato: »Di solito si desidera conoscere il tipo di una variabile per agire di conseguenza«. in Elisir agisci di conseguenza abbinando i motivi, non con switch/ case.
mariotomo,

18

Lascio questo qui per il gusto di qualcuno che spera di trovare una versione davvero sana. Al momento non ci sono buone risposte a questo in arrivo su 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

Per completezza, casi di 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

Ecco una soluzione con protocolli; Non sono sicuro che siano più veloci (spero sicuramente che non stiano eseguendo un ciclo su tutti i tipi), ma è piuttosto brutto (e fragile; se aggiungono o rimuovono un tipo di base o rinominano, lo romperanno).

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 vuoi davvero un "tipo" di controllo, puoi facilmente crearne uno usando gli strumenti nell'organizzazione di pietra del filosofo. github.com/philosophers-stone . La fenetica è ancora agli inizi, ma può fare questo e molto altro.
Fred the Magic Wonder Dog,

mi lego facilmente a una dipendenza esterna? come migliorerà la mia capacità di condividere il codice con gli amici? Questa è una strada per 2 problemi.
Dmitry,

Grazie per la modifica @aks; Posso effettivamente tornare a 4 spazi ora ^ _ ^
Dmitry

15

Ho appena incollato il codice da 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 intelligente della citazione! Più vedo il codice Elisir, più mi ricorda Perl; quel costrutto ~ w sembra molto simile a qw //. Mi chiedo se Perl abbia qualche meccanismo intelligente per simulare la citazione di Lisplike.
Dmitry,

Mi chiedo come funziona la citazione; può essere emulato utilizzando un preprocessore di espressioni regolari o richiede un parser che percorra l'intero codice per eseguire l'espansione delle macro.
Dmitry,

1

Mi sono imbattuto in una situazione necessaria per verificare il parametro deve essere di un certo tipo. Forse puoi attivare un modo migliore.

Come questo:

@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

Solo perché nessuno lo ha menzionato

IO.inspect/1

Output per console l'oggetto ... è quasi equivalente a JSON.stringify

Molto utile quando proprio non riesci a capire come appare un oggetto in un test.


4
Non una risposta alla domanda, nemmeno vicino
LowFieldTheory
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.