Perché non possono esserci conversioni implicite?

13

A quanto ho capito, le conversioni implicite possono causare errori.

Ma non ha senso - le conversioni normali non dovrebbero anche causare errori, quindi?

Perché non avere

len(100)

funziona nell'interpretazione della lingua (o la compila) come

len(str(100))

specialmente perché questo è il solo modo (lo so) perché funzioni. La lingua sa qual è l'errore, perché non risolverlo?

Per questo esempio ho usato Python, sebbene ritenga che per qualcosa di così piccolo sia fondamentalmente universale.

    
posta Quelklef 26.05.2015 - 03:20
fonte

9 risposte

36

Per quel che vale, len(str(100)) , len(chr(100)) e len(hex(100)) sono tutti diversi. str è non l'unico modo per farlo funzionare, poiché c'è più di una conversione diversa in Python da un intero a una stringa. Uno di essi è ovviamente il più comune, ma non è necessariamente detto che è quello che intendevi. Una conversione implicita significa letteralmente "va da sé".

Uno dei problemi pratici con le conversioni implicite è che non è sempre ovvio quale conversione verrà applicata dove ci sono diverse possibilità, e questo fa sì che i lettori commettano errori interpretando il codice perché falliscono per capire la conversione implicita corretta. Tutti dicono sempre che ciò che intendevano è "l'interpretazione ovvia". È ovvio per loro perché è quello che intendevano. Potrebbe non essere ovvio per qualcun altro.

Questo è il motivo per cui (il più delle volte) Python preferisce l'esplicito all'implicito, preferisce non rischiare. Il caso principale in cui Python esegue la coercizione è in aritmetica. Permette 1 + 1.0 perché l'alternativa sarebbe troppo fastidiosa per convivere, ma non consente 1 + "1" perché pensa che dovresti specificare se vuoi dire int("1") , float("1") , ord("1") , str(1) + "1" , o qualcos'altro. Inoltre non consente (1,2,3) + [4,5,6] , anche se potrebbe definire regole per scegliere un tipo di risultato, proprio come definisce le regole per scegliere il tipo di risultato di 1 + 1.0 .

Altre lingue non sono d'accordo e hanno molte conversioni implicite. Più includono, meno diventano ovvi. Prova a memorizzare le regole dallo standard C per "promozioni intere" e "solite conversioni aritmetiche" prima di colazione!

    
risposta data 26.05.2015 - 14:32
fonte
27

As I understand it, implicit conversions can cause errors.

Manca una parola: le conversioni implicite possono causare errori di runtime .

Per un caso semplice come quello che mostri, è abbastanza chiaro cosa intendi. Ma le lingue non possono funzionare sui casi. Devono lavorare con le regole. Per molte altre situazioni, non è chiaro se il programmatore abbia fatto un errore (usando il tipo sbagliato) o se il programmatore intendesse fare ciò che il codice presume che intendessero fare.

Se il codice assume un errore, si ottiene un errore di runtime. Dal momento che sono noiosi da rintracciare, molte lingue errano nel dire che hai incasinato e ti permetti di dire al computer cosa volevi dire veramente (aggiustare il bug o fare esplicitamente la conversione). Altre lingue fanno l'ipotesi, dal momento che il loro stile si presta a un codice facile e veloce che è più facile da eseguire il debug.

Una cosa da notare è che le conversioni implicite rendono il compilatore un po 'più complesso. Devi essere più attento ai cicli (proviamo questa conversione da A a B, oops che non ha funzionato, ma c'è una conversione da B a A! e quindi devi preoccuparti anche dei cicli di dimensioni C e D ), che è una piccola motivazione per evitare conversioni implicite.

    
risposta data 26.05.2015 - 03:33
fonte
14

Le conversioni implicite sono del tutto possibili. La situazione in cui ti trovi nei guai è quando non sai da che parte dovrebbe funzionare qualcosa.

Un esempio di questo può essere visto in Javascript dove l'operatore + funziona in modi diversi in momenti diversi.

>>> 4 + 3
7
>>> "4" + 3
43
>>> 4 + "3"
43

Se uno degli argomenti è una stringa, l'operatore + è una concatenazione di stringhe, altrimenti è un'aggiunta.

Se ti viene fornito un argomento e non sai se si tratta di una stringa o di un intero e vuoi aggiungerlo, può essere un po 'un disastro.

Un altro modo per affrontare questo è l'eredità di base (che segue perl da - vedi La programmazione è difficile, Let's Go Scripting ... )

In Basic, la funzione len ha senso solo essere invocata su una stringa (documenti per visual basic :" Qualsiasi espressione String o nome di variabile valida. Se Expression è di tipo Object, la funzione Len restituisce la dimensione così come verrà scritta nel file dalla funzione FilePut. ").

Perl segue questo concetto di contesto. La confusione esistente in JavaScript con la conversione implicita dei tipi per l'operatore + è talvolta aggiunta e talvolta la concatenazione non avviene in perl perché + è sempre aggiunta e . è sempre concatenazione.

Se qualcosa viene usato in un contesto scalare, è uno scalare (ad esempio usando una lista come scalare, la lista si comporta come se fosse un numero corrispondente alla sua lunghezza). Se si utilizza un operatore di stringa ( eq per test di uguaglianza, cmp per il confronto delle stringhe) lo scalare viene utilizzato come se fosse una stringa. Allo stesso modo, se qualcosa è stato utilizzato in un contesto matematico ( == per test di uguaglianza e <=> per confronto numerico), lo scalare viene usato come se fosse un numero.

La regola fondamentale per tutta la programmazione è "fai la cosa che sorprende meno la persona". Questo non significa che non ci siano sorprese, ma lo sforzo è di sorprendere la persona meno.

Andando da un cugino stretto di perl-php, ci sono situazioni in cui un operatore può agire su qualcosa in contesti stringa o numerici e il comportamento può sorprendere le persone. L'operatore ++ è uno di questi esempi. Sui numeri, si comporta esattamente come previsto. Quando si agisce su una stringa, ad esempio "aa" , si incrementa la stringa ( $foo = "aa"; $foo++; echo $foo; stampe ab ). Rotolerà anche in modo che az quando incrementato diventi ba . Questo non è ancora particolarmente sorprendente.

$foo = "3d8";
echo "$foo\n";
$foo++;
echo "$foo\n";
$foo++;
echo "$foo\n";
$foo++;
echo "$foo\n";

( ideone )

Stampa:

3d8
3d9
3e0
4

Benvenuto nei pericoli delle conversioni implicite e degli operatori che agiscono in modo diverso sulla stessa stringa. (Perl gestisce quel blocco di codice in modo leggermente diverso - decide che "3d8" quando viene applicato l'operatore ++ è un valore numerico dall'inizio e va immediatamente a 4 ( ideone ) - questo comportamento è ben descritto in perlop : Incremento automatico e decremento automatico )

Ora, perché una lingua fa qualcosa in un modo o nell'altro, in un altro modo arriva ai pensieri progettuali dei designer. La filosofia di Perl è C'è più di un modo per farlo - e posso pensare a un certo numero di modi per farlo di queste operazioni. D'altra parte, Python ha una filosofia descritta in PEP 20 - The Zen of Python che afferma (tra le altre cose): "Ci dovrebbe essere uno - e preferibilmente solo un modo - ovvio per farlo."

Queste differenze di progettazione hanno portato a diverse lingue. C'è un modo per ottenere la lunghezza di un numero in Python. La conversione implicita va contro questa filosofia.

Lettura correlata: Perché Ruby non ha una conversione implicita di Fixnum in stringa?

    
risposta data 26.05.2015 - 04:46
fonte
11

ColdFusion ha fatto la maggior parte di questo. Definisce un insieme di regole per gestire le tue conversioni implicite, ha un singolo tipo di variabile, ed ecco fatto.

Il risultato è l'anarchia totale, dove l'aggiunta di "4a" a 6 è 6.16667.

Perché? Bene, perché la prima delle due variabili è un numero, quindi il risultato sarà numerico. "4a" viene analizzato come una data e visto come "4 AM". 4 AM è 4: 00/24: 00 o 1/6 di giorno (0,16667). Aggiungi a 6 e ottieni 6.16667.

Le liste hanno caratteri di separazione predefiniti di una virgola, quindi se aggiungi mai un elemento a un elenco contenente una virgola, hai aggiunto solo due elementi. Inoltre, gli elenchi sono segretamente stringhe, quindi possono essere analizzati come date se contengono solo 1 elemento.

Il confronto delle stringhe controlla se entrambe le stringhe possono essere analizzate prima delle date. Perché non esiste un tipo di data, lo fa. Stessa cosa per numeri e numeri contenenti stringhe, notazione ottale e booleani ("vero" e "sì", ecc.)

Invece di fallire velocemente e riportare un errore, ColdFusion gestirà le conversioni dei tipi di dati per te. E non in senso positivo.

Ovviamente, puoi correggere le conversioni implicite chiamando funzioni esplicite come DateCompare ... ma poi hai perso i "benefici" della conversione implicita.

Per ColdFusion, questo è un modo per aiutare a potenziare gli sviluppatori. Soprattutto quando tutti quegli sviluppatori sono abituati all'HTML (ColdFusion funziona con i tag, si chiama CFML, in seguito hanno aggiunto lo scripting tramite <cfscript> , dove non è necessario aggiungere tag per tutto). E funziona bene quando vuoi solo ottenere qualcosa lavorando. Ma quando hai bisogno che le cose accadano in un modo più preciso, hai bisogno di un linguaggio che si rifiuti di fare conversioni implicite per tutto ciò che potrebbe sembrare un errore.

    
risposta data 26.05.2015 - 09:57
fonte
7

Stai dicendo che le conversioni implicite potrebbero essere una buona idea per operazioni non ambigue, come int a = 100; len(a) , in cui ovviamente significa convertire l'int in una stringa prima di chiamare.

Ma ti stai dimenticando che queste chiamate potrebbero essere sintatticamente non ambigue, ma potrebbero rappresentare un refuso del programmatore che intendeva passare a1 , che è una stringa. Questo è un esempio forzato, ma con gli IDE che forniscono il completamento automatico dei nomi delle variabili, questi errori si verificano.

Il sistema di tipi ha lo scopo di aiutarci a evitare errori, e per le lingue che scelgono il controllo del tipo più rigoroso, le conversioni implicite ne minano.

Dai un'occhiata ai guai di Javascript con tutte le conversioni implicite eseguite da ==, tanto che ora molti consigliano di attenersi all'operatore di conversione non implicita ===.

    
risposta data 26.05.2015 - 07:59
fonte
6

Immagina per un momento il contesto della tua affermazione. Dici che questo è l '"unico modo" in cui potrebbe funzionare, ma sei proprio sicuro che funzionerà così? Che dire di questo:

def approx_log_10(s):
    return len(s)
print approx_log_10(3.5)  # "3" is probably not what I'm expecting here...

Come altri hanno menzionato, nel tuo esempio molto specifico, sembra semplice per il compilatore intuire cosa intendevi. Ma in un contesto più ampio di programmi più complicati, spesso non è del tutto ovvio se intendevi inserire una stringa, o digitare un nome di variabile per un altro, o chiamare la funzione sbagliata, o una dozzina di altri potenziali errori logici .

Nel caso in cui l'interprete / compilatore indovini cosa intendi, a volte funziona magicamente, e altre volte passi ore a eseguire il debug di qualcosa che misteriosamente non funziona senza un motivo apparente. È molto più sicuro, nel caso medio, sentirsi dire che l'affermazione non ha senso, e dedicare alcuni secondi a correggerla in base a ciò che intendevi realmente.

    
risposta data 26.05.2015 - 08:47
fonte
3

Le conversioni implicite possono essere un vero problema con cui lavorare. In PowerShell:

$a = $(dir *.xml) # typeof a is a list, because there are two XML files in the folder.
$a = $(dir *.xml) # typeof a is a string, because there is one XML file in the folder.

All'improvviso ci sono il doppio di test necessari e il doppio dei bug di quanti ce ne sarebbero senza una conversione implicita.

    
risposta data 26.05.2015 - 10:34
fonte
2

I cast espliciti sono importanti perché stanno chiarendo le tue intenzioni. Prima di tutto l'uso di cast espliciti racconta una storia a qualcuno che sta leggendo il tuo codice. Rivelano che hai fatto intenzionalmente ciò che hai fatto. Inoltre lo stesso vale per il compilatore. Il seguente è illegale in C # per esempio

double d = 3.1415926;
// code elided
int i = d;

Il cast ti farà perdere la precisione, che potrebbe facilmente essere un bug. Quindi il compilatore rifiuta di compilare. Usando un cast esplicito stai dicendo al compilatore: "Ehi, so cosa sto facendo." E lui andrà: "Ok!" e compilare.

    
risposta data 26.05.2015 - 11:50
fonte
1

Le lingue che supportano le conversioni / conversioni implicite o la digitazione debole a cui talvolta si fa riferimento, faranno supposizioni per te che non sempre corrispondono al comportamento che hai inteso o previsto. I progettisti della lingua potrebbero aver avuto lo stesso processo di pensiero che hai fatto per un certo tipo di conversione o serie di conversioni, e in tal caso non vedrai mai un problema; tuttavia se non lo facessero, il tuo programma fallirà.

L'errore, tuttavia, può o non può essere ovvio. Poiché questi cast impliciti avvengono in fase di runtime, il tuo programma funzionerà felicemente e vedrai solo un problema quando guardi l'output o il risultato del tuo programma. Un linguaggio che richiede cast espliciti (alcuni si riferiscono a questo come una strong digitazione) ti darebbe un errore prima di programmare inizia addirittura l'esecuzione, che è un problema molto più ovvio e più facile da risolvere che i cast impliciti sono andati male.

L'altro giorno un mio amico ha chiesto a suo figlio di 2 anni cosa fosse 20 + 20 e lui ha risposto nel 2020. Ho detto al mio amico, "Sta per diventare un programmatore Javascript".

In Javascript:

20+20
40

20+"20"
"2020"

Così puoi vedere i problemi che i cast impliciti possono creare e perché non è qualcosa che può essere corretto. La soluzione, direi, è usare cast espliciti.

    
risposta data 29.05.2015 - 05:46
fonte

Leggi altre domande sui tag