[Python] Why Go is not good

enrico franchi enrico.franchi a gmail.com
Ven 17 Lug 2015 15:07:53 CEST


2015-07-16 23:53 GMT+01:00 Enrico Bianchi <enrico.bianchi a ymail.com>:

> On 07/13/2015 11:55 AM, enrico franchi wrote:
>
>> Il problema con l'overloading e' che rende un sacco di cose
>> drammaticamente piu' complicate: in presenza dello stesso caso che menzioni
>> (ovvero che non sai quale tipo sia una variabile) vuole dire che
>> effettivamente il tuo codice compilera', ma tu non avrai idea di cosa
>> faccia.
>>
> Mmm... non sono del tutto convinto, ovvero se io implemento Sum(int, int)
> e Sum(float, float), io so che posso avere o un int o un float e
> comportarmi di conseguenza
>

Allora, in Go, si, perche' non hai type cohercion. In altri linguaggi? Devi
certamente ricordarti tutte le conversioni implicite. Per dire, in molti
linguaggi un long chiamerebbe la versione per float invece che quella per
int. Per dire... super intuitivo ;) Ricordati che i bachi hanno la tendenza
a rendersi manifesti alle 2 di notte quando a guardare il problema c'e'
tipicamente qualcuno che non e' l'ingegnere che ha scritto il codice.

E' un peccato che tu abbia tagliato il mio altro esempio che mostrava in
modo completamente evidente quale sia il problema grosso (e che la mancanza
di conversioni automatiche non e' sufficiente ad evitarlo). Se lo avessi
studiato un pochetto ti saresti chiarito quale e' il punto.


>  Se non sai quale e' il tipo della variabile, vuole dire che non sai quale
>> variante del metodo verra' chiamata. E quindi di fatto non sai che codice
>> stai eseguendo. Ecco che un'innocua seccatura in assenza di overloading e'
>> diventata una possibile fonte di bachi.
>>
> Mmm... In altre parole, stai dicendo che Python, in quanto (di solito) non
> sai il tipo della variabile masolo il suo contenuto, e` altamente
> problematico ;)


Per assurdo molto meno. Almeno non ci sono dubbi su quale sia il codice
della funzione che viene chiamato. Poi c'e' sempre il problema di quale sia
il codice che la funzione che hai chiamato chiama a sua volta, ma quello fa
parte del concetto di polimorfismo e tipicamente non vuoi farne a meno. E'
un male quasi necessario.



>  Senza offesa, ma da come rispondi sembrerebbe che ti soffermi piu` sul
> codice che sul concetto che volevo esprimere. Quello che interessa a me e`
> fare il catch del panic, in quanto, ad esempio, potrei voler fare in modo
> che l'applicazione non debba terminare assolutamente la sua esecuzione se
> non tramite intervento manuale. La soluzione, come dico in seguito (e come
> ho scoperto proprio grazie a questo thread) e` usando recover() in una
> deferred function, che di per se non e` anche una cattiva soluzione, anche
> se limitata dalla gestione dei defer in Go (ovvero a cascata non appena la
> funzione viene eseguita completamente)


Eh, ma se mostri codice per rappresentare il problema, ma il codice che
mostri non rappresenta il problema io posso solo parlare del codice in
se... la sfera di cristallo non mi funziona troppo bene. :)

Comunque, dipende come e' concepito il sistema. Ci sono casi in cui vuoi
catturare un'eccezione piuttosto esterna. Tipicamente posso pensare ad un
webserver essenzialmente stateless: fallisci la richiesta, tiri un 5xx e
vai avanti. Essendo stateless, non hai il problema che il panic sia un
sintomo di memoria corrotta. E se il sistema e' messo su da persone
normodotate avrai un quache allarme se il rate di 5xx e' troppo alto (che
indicherebbe a sua volta che il sistema e' compromesso e che bisogna
intervenire). Quindi anche in questo caso devi comunque avere qualche tipo
di supporto a livello di sistema/operations.

Su un sistema stateful in molti casi preferisco abbattere l'applicazione e
non pensarci due volte. Tanto (sempre siccome parliamo di individui
normodotati) il sistema avra' qualche tipo di ridondanza e questo non e' un
problema. E ci sara' qualche tipo di nanny che ritira su il tutto
(possibilmente partendo da uno stato non corrotto). Se ancora una volta
l'applicazione va giu' troppe volte in poco tempo vuole dire che c'e' un
problema, ci sara' un allarme e qualcuno dovra' correre a guardarci. Oppure
se abbiamo abbastanza capacity (che so... siamo in N+K) ci si guarda il
giorno dopo e tanti saluti. Nota, questo approccio funziona anche (e come
sempre meglio) in un sistema stateless: semplicemente se hai stato
catturare un'eccezione, loggare e andare avanti puo' volere dire che
quell'istanza da quel momento in poi serve merda, hai un gray failure ed e'
molto peggio: meglio abbattere tutto e tanti saluti.

Nel caso specifico di Go, ritirare su un'applicazione e' generalmente una
cosa molto veloce. *E* i panic non dovrebbero segnalare condizioni
"normali". Quindi e' assolutamente concepibile pulire tutto e fare morire e
saluti.


> Posso essere d'accordo, ma secondo me lo sconsigliare una pratica del
> genere e` difettata dalle varie casistiche. Vedi ad esempio il caso di
> un'applicazione che non deve morire mai


Salvo che nel tuo caso l'applicazione moriva comunque. Tra l'altro mi
aspetto che tu conosca la differenza fra except: ed except Exception:,
quindi non vedo di cosa stiamo parlando. Quello che hai scritto *non* e'
quello che vuoi nel 99.99% dei casi. Che e' il motivo per cui e' assodato
essere una bad practice. :)


>
>
>  a = may_return_null(...)
>> if a is not None:
>>     f(a, ...)
>>
> Bruttino, non tanto perche` non mi piace, ma per com'e` scritto :)
> La sintassi
> if a:
>   f(a,...)
>
> E` molto piu` elegante (si, e` una pulce che non serve a molto) :)


No, serve: per ricordare a tutti di non fare come suggerisci. if a is not
None e' una cosa molto diversa da if a:.
In un caso il mio problema especifico e' non avere None, nel secondo invece
e' un puro "ValueError", ovvero non vuoi nulla che non valuti a falso.
Mi vengono in mente tanti casi in cui la stringa vuota e' un valore
perfettamente legittimo che f deve sapere gestiere, mentre None, che non e'
una stringa (e non ha i metodi delle stringhe) non puo' farci nulla.

Quindi, se vuoi escludere la stringa vuota (o 0, o la lista vuota, etc) usi
l'idioma che suggerisci. Se vuoi escludere None, usi quello che suggerisco
io.


>  Fanno sempre e solo stack unwind, non danno controllo al programmatore.
>>
> Mmm... continuo a non capire... Un esempio (o della documentazione)?


Lisp.


>
>
>  Come dicevo... quello non e' Go idiomatico a mio avviso.
>>
> Anche qua, mi sembra che tu abbia dato piu` peso al codice che ho usato
> come esempio piuttosto che al concetto che volevo esprimere. Personalmente
> ritengo che un try/except sia piu` elegante rispetto al dover fare il check
> per ogni error che ti viene restituito. Quello che intendo e` che, ad
> esempio, le operazioni di apertura, scrittura e chiusura di un file in Go
> devono essere gestite una per una, mentre in Python le posso gestire tutte
> nello stesso try/except che, personalmente, ritengo piu` sensato


E che e' esattamente quello che non dovresti fare. Certo, salvi un po' di
caratteri. Poi e' 30 volte piu' complicato guardando un trace capire cosa
e' successo e corri sempre il rischio di nascondere errori dietro ad altri
errori. E ti trovi con quelle bellissime applicazioni che ad un certo punto
si decide che e' ok che ogni tanto falliscano e che nessuno sa perche'
finche', per caso, qualcuno non trova quale era effettivamente il problema
(nella migliore delle ipotesi) o, nella peggiore, qualcuno mette pezze che
non risolvono il problema ma lo mascherano ancora di piu.

In generale, se hai due linee che possono lanciare la stessa eccezione e tu
hai un'azione da compiere su quell'eccezione, e' molto frequente che
l'azione da compiere sia diversa. L'esempio tipico e' sui files... e' molto
diverso avere un errore nella creazione di un file (o nell'apertura) oppure
avere un errore nella read/write che segue tutto questo. Se poi dentro lo
stesso blocco hai un loop... buona fortuna. Ottime probabilita' che
qualcunque cosa stai facendo non sia transazionale.


>  Ancora peggio quando hai codice "lineare" del tipo fai una serie di
>> operazioni una in fila all'altra. E hai errori vari che possono arrivare da
>> ognuna di queste (possibilmente con un set di eccezioni non disgiunto per
>> le varie chiamate).
>>
> Boh, a pensarci mi sembrano abbastanza semplici e pulite da gestire...


O forse non stai pensando a tutti i failure mode che stai andando a
nascondere...


-- 
.
..: -enrico-
-------------- parte successiva --------------
Un allegato HTML è stato rimosso...
URL: <http://lists.python.it/pipermail/python/attachments/20150717/bca4ea49/attachment-0001.html>


Maggiori informazioni sulla lista Python