Quando è giustificato ‘eval` in Ruby?

” Si suppone che ‘eval‘ sia cattivo? ” Ha ispirato questo:

Principalmente tutti sono d’accordo sul fatto che la eval è negativa e nella maggior parte dei casi c’è una sostituzione più elegante / più sicura.

Quindi volevo chiedere: se eval è abusato spesso, è davvero necessario come caratteristica del linguaggio? Sta facendo più male che bene?

Personalmente, l’unico posto che trovo utile è quello di interpolare stringhe fornite nel file di configurazione.

Modifica: L’intenzione di questa domanda è di ottenere il maggior numero ansible di casi reali quando eval è l’unica soluzione o la migliore. Quindi, per favore, non andare in “dovrebbe una lingua limitare la creatività di un programmatore” direzione.

Edit2: E quando parlo di eval , ovviamente mi riferisco a eval la stringa, non passando il ruby ​​block a instance_eval o class_eval .

L’unico caso che conosco (oltre a “Ho questa stringa e voglio eseguirlo”) si occupa in modo dinamico delle variabili locali e globali. Ruby ha metodi per ottenere i nomi delle variabili locali e globali, ma non ha metodi per ottenere o impostare i loro valori in base a questi nomi. L’unico modo per fare AFAIK è con eval .

Ogni altro uso è quasi certamente sbagliato. Non sono un guru e non posso affermare categoricamente che non ce ne sono altri, ma ogni altro caso d’uso che abbia mai visto dove qualcuno ha detto “Hai bisogno di valutazione per questo”, ho trovato una soluzione che non lo ha fatto.

Nota che sto parlando di eval qui, comunque. Ruby ha anche instance_eval , che può prendere una stringa o un blocco da eseguire nel contesto del ricevitore. La forma a blocchi di questo metodo è veloce, sicura e molto utile.

Quando è giustificato? Direi quando non ci sono alternative ragionevoli. Sono stato in grado di pensare a un uso in cui non riesco a pensare ad un’alternativa: irb, che, se si scava abbastanza in profondità (a workspace.rb , intorno alla riga 80 nella mia copia, se sei interessato) usa eval per eseguire il tuo ingresso:

 def evaluate(context, statements, file = __FILE__, line = __LINE__) eval(statements, @binding, file, line) end 

Mi sembra abbastanza ragionevole – una situazione in cui non sai esattamente quale codice devi eseguire fino al momento in cui ti viene chiesto di farlo. Qualcosa di dinamico e interattivo sembra adattarsi al conto.

Il motivo per cui è eval è perché quando ne hai bisogno, quando ne hai davvero bisogno, non ci sono sostituti. Dopotutto, c’è solo molto che puoi fare con l’invio di metodi creativi e, a un certo punto, devi eseguire codice arbitrario.

Solo perché una lingua ha una caratteristica che potrebbe essere pericolosa non significa che sia intrinsecamente una cosa negativa. Quando una lingua presume di sapere più del suo utente, è quando ci sono problemi.

Direi che quando trovi un linguaggio di programmazione privo di pericoli, ne hai trovato uno che non è molto utile.

Quando è giustificata la valutazione? In termini pragmatici, quando dici che lo è. Se è il tuo programma e tu sei il programmatore, imposti i parametri.

C’è un caso d’uso molto importante per eval() che non può (AFAIK) essere ottenuto usando qualcos’altro, e cioè trovare il corrispondente riferimento all’object per un legame.

Supponiamo che tu abbia passato un blocco ma (per qualche motivo) hai bisogno di accedere al contesto dell’object del binding, dovresti fare quanto segue:

 obj = eval('self', block.binding) 

È anche utile definire quanto segue:

 class Proc def __context__ eval('self', self.binding) end end 

IMO principalmente per le lingue specifiche del dominio.

” Opzioni di valutazione in Ruby ” è un articolo pubblicato da Jay Fields su InfoQ.

eval è uno strumento, non è né intrinsecamente buono né cattivo. È giustificato quando sei certo che sia lo strumento giusto per ciò che stai cercando di realizzare.

Uno strumento come eval riguarda la valutazione del codice in fase di esecuzione rispetto al tempo di “compilazione”. Sai qual è il codice quando lanci Ruby? Allora probabilmente non hai bisogno di valutazione. Il tuo codice genera codice durante il runtime? allora probabilmente hai bisogno di valutarlo.

Ad esempio, i metodi / le funzioni necessarie in un parser decente ricorsivo dipendono dalla lingua che viene analizzata. Se la tua applicazione genera al volo un parser simile, allora potrebbe avere senso utilizzare eval. Potresti scrivere un parser generalizzato, ma potrebbe non essere una soluzione elegante.

” Inserimento programmatico di un letrec in Scheme. Macro o eval? ” È una domanda che ho postato su eval in Scheme, dove il suo uso è per lo più inevitabile.

In generale, eval è una funzionalità linguistica utile quando si desidera eseguire codice arbitrario. Questa dovrebbe essere una cosa rara, ma forse stai creando il tuo REPL o vuoi esporre il run-time ruby all’utente finale per qualche motivo. Potrebbe accadere ed è per questo che la funzione esiste. Se la si utilizza per aggirare una parte della lingua (ad es. Le variabili globali), allora la lingua è imperfetta o la comprensione della lingua è errata. Solitamente la soluzione non consiste nell’utilizzare eval ma per capire meglio la lingua o scegliere una lingua diversa.

Vale la pena notare che nel ruby in particolare instance_eval e class_eval hanno altri usi.