<div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">2015-07-11 16:03 GMT+01:00 Enrico Bianchi <span dir="ltr"><<a href="mailto:enrico.bianchi@ymail.com" target="_blank">enrico.bianchi@ymail.com</a>></span>:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><span class="">On 07/03/2015 03:36 PM, enrico franchi wrote:<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">
 Parli di override o di overload?<br>
</blockquote></span>
Oddio, confondo sempre i termini...<br></blockquote><div><br></div><div>Ok, a te interessa fare overloading.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">
Quello che mi interessava fare era semplicemente questo:<br>
<br>
func Sum(a, b int) int {<br>
  return a + b<br>
}<br>
<br>
func Sum(a, b float) float {<br>
  return a + b<br>
}<br>
<br>
Certo, posso usare anche SumInt e SumFloat, ma diventa molto piu` complesso, ovvero potrei avere un caso in cui non so il tipo di variabile e prima me lo devo andare a cercare (a meno di non fare evidenti cast di variabili). </blockquote><div><br></div><div>Si, capisco. Ma explicit is better than implicit. In primo luogo, a me verrebbe da dire che non e' del tutto normale non sapere il tipo statico di una variabile e volerci fare sopra cose. Tipicamente finisce che qualcosa non funziona.</div><div><br></div><div>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. 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.</div><div><br></div><div>Ma in generale, tutte le volte che ho avuto a che fare con overloading, avrei voluto *non* averlo. Certo, l'idea di SumInt e SumFloat da davvero fastidio e fa brutto. Poi quando uno realizza quello che implica, dice toglietelo. *Specie* ma non limitatamente alla combo overloading + type conversion implicite. Troppe regole da tenere a mente, troppo facile fare cazzate.</div><div><br></div><div>Ci sono chiari esempi di API design piuttosto sensati che in presenza di overloading diventano relativamente complessi da gestire. Il primo che mi viene in mente e' quello di slf4j (facade di logging su Java). Questo esempio non ha nemmeno il problema dell'interazione con le type conversion e similia.</div><div><br></div><div>- error(String, Object)</div><div><div>- error(String, Object, Object)</div></div><div><div>- error(String, Object...)</div></div><div><div>- error(String, Throwable)</div></div><div><br></div><div>In pratica quando gli passi un singolo throwable fa una cosa [case (d)], quando passi un singolo object [case (a)] ne fa un'altra (che e' un caso speciale di quello che succede quando gliene passi 2 [case (b)] o N [case(c)]). E' veramente facile senza pensarci troppo prendere una chiamata tipo:</div><div><br></div><div>logger.error("...", t);</div><div><br></div><div>e farla diventare, che so...</div><div><br></div><div>logger.error("...: {}", t, o);</div><div><br></div><div>e non accorgersi che la semantica e' cambiata parecchio (in particolare quello che accade con lo stack-trace).</div><div><br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">Per inciso, ricordo un thread su di un gruppo Facebook in cui si criticava specificatamente la mancanza di questa caratteristica (e in cui era uscita anche questa cosa simpatica: <a href="http://play.golang.org/p/Isoo0CcAvr" rel="noreferrer" target="_blank">http://play.golang.org/p/Isoo0CcAvr</a> )</blockquote><div><br></div><div>Questo e' un problema completamente *non* relativo a quello di la sopra. In effetti trattano parti di linguaggio completamente distinti. Potrsti avere overloading e avere ancora quel problema sopra menzionato.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><span class=""><br>
<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">
Credo che tu non abbia chiara la gestione degli errori in Go.<br>
</blockquote></span>
Guarda, faccio prima a farti vedere cosa intendo: prendiamo questo caso:<br>
<br>
package main<br>
<br>
import (<br>
  "fmt"<br>
)<br>
<br>
func Sum(a int, b *int) int {<br>
    return a + *b;<br>
}<br>
<br>
func main() {<br>
    var first int<br>
    var second *int<br>
<br>
    first = 1<br>
    second = nil<br>
    fmt.Println(Sum(first, second))<br>
}<br>
<br>
Questo codice genera, ovviamente, un runtime error in quanto passo un puntatore nil come argomento della somma. Ora, per gestire questa situazione, posso sicuramente fare un check che le variabili siano valorizzate, ma questo non mi protegge da altri "imprevisti". Per capirci, una situazione del genere in Python la gestirei cosi`:<br>
<br>
def sum(a, b):<br>
    return a + b<br>
<br>
if __name__ == "__main__":<br>
    try:<br>
        print(sum(1, None))<br>
    except:<br>
        print("Argh!")<br>
<br>
(il che potrebbe essere deprecabile in alcuni contesti, mentre in altri, e.g. per gestire la morte prematura delle connessioni da parte di un client, potrebbe essere una facility non indifferente)</blockquote><div><br></div><div>Scusa, non capisco. Mi fai vedere un pezzo di codice ... pezzo di codice che ha chiaramente un baco (indipendente da Go). Cioe', quel codice non dovrebbe essere scritto in quella maniera (e non dovrebbe essere utilizzato in quella maniera). Tra l'altro, per come funziona Go, non ci sono estremi buoni motivi per scriverlo in quella maniera... ora, capisco che sia un esempio... ma non riesco a vedere come un problema il fatto che se dereferenzi puntatori senza fare check sei nella guazza.</div><div><br></div><div>E il modo in cui lo risolvi in Python e', appunto, sbagliato a mio avviso. In primo luogo il catch all except e' qualcosa che viene sconsigliato da lungo tempo. E, per il resto, si espone un'altro problema di Python sugli esempietti piccoli.</div><div><br></div><div>In Java una funzione come sum(Integer a, Integer b) la avresti (sperabilmente) scritta come sum(@Nonnull Integer a, @Nonnull Integer b) e i tool di analisi statica ti avrebbero flaggato degli usi potenzialmente non safe della stessa. Alternativamente, avresti forse messo delle guardie che controllano che i valori siano bboni. Questo si riesce a fare perche' da un lato puoi fare analisi statica, dall'altro il compilatore e il JIT possono fare si che suddetto costo sia trascurabile (o comunque piccolo).</div><div><br></div><div>In Python non hai modo di farlo: riempire tutto di null check ti fa schiantare le performance e l'analisi statica... beh, buona fortuna. </div><div><br></div><div>In Go... beh, in Go non scrivi la funzione in quella maniera: se passi un puntatore ad un intero e' perche' vuoi modificarlo. I tool di analisi statica potrebbero esistere (ma al momento non ho idea dello stato) e il costo dei check e' piu' piccolo che in Python, ma potenzialmente piu' costoso che in Java (a seconda di quanto e' veramente smart il compilatore, ma temo non abbastanza).</div><div><br></div><div>Ma comunque torniamo al punto chiave: per quanto mi riguarda se ti trovi con una npe, hai un baco nel codice. E' accettabile spegnere tutto, a patto di farlo in modo pulito (panic vs. seg fault). E' accettabile anche un'eccezione: ma facci caso: di solito non fai catch broad di exceptions che non puoi gestire in modo specifico (e puoi solo silenziare). Tantomeno ho visto codice che cattura esplicitamente npe in Python: di solito se ho un pattern tipo</div><div><br></div><div>a = may_return_null(...)</div><div>f(a, ...)</div><div><br></div><div>quello che scrivo e'</div><div><br></div><div>a = may_return_null(...)</div><div>if a is not None:</div><div>    f(a, ...)</div><div><br></div><div>-- a meno che f non sia fatta in modo tale che None e' ok -- </div><div><br></div><div>Questa e' un'essenziale differenza rispetto al classico EAFP (che in generale preferisco) per vari motivi. In primo luogo, se None e' un valore non valido per a, non voglio nemmeno fare partire la computazione. Il fatto che la funzione sopra mi abbia dato None vuole dire qualcosa di ben preciso: di fatto e' un caso in cui may_return_null utilizza i valori di ritorno per segnalare che qualcosa non e' andato ammodo. Possiamo discutere sul design di may_return_null, ma se fa cosi', io cosi' lo devo gestire. E il secondo motivo e' che probabilmente l'eccezione da f si propaga un po' piu' tardi di quello che vorrei, per cui preferisco intervenire.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><span class=""><br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">
(o meglio, della versione castrata che e' implementata nei vari Python, Java, C++ e combriccola con cui sei probabilmente familiare)<br>
</blockquote></span>
Per curiostia`, perche` sarebbero castrate?<br></blockquote><div><br></div><div>Fanno sempre e solo stack unwind, non danno controllo al programmatore.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">
<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">
puoi usare panic.<br>
</blockquote>
Panic da quello che ho visto manda in traceback l'applicativo, ovvero e` l'equivalente di un raise in Python o di un throw in Java. Quello che vorrei fare io e` il catch</blockquote><div><br></div><div>Loro sconsigliano di vederlo in quel modo. Il comportamento apparente e' simile, ma lo scopo e' diverso.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><span class=""><br>
<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">
(vedi gli esempi nella stdlib di Go)<br>
</blockquote></span>
Uhm, a quali esempi ti riferisci?</blockquote><div><br></div><div>Se leggi la doc su come si usano i vari panic, recover e error-handling ti sara' chiarissimo.</div><div>Li specificano anche che e' cattiva pratica fare propagare i panic dalle librerie (a meno che non siano veri panic: hei, suicidiamoci gloriosamente prima di fare altri danni). Il che vuole dire che hai l'ottima proprieta' che devi gestire, di fatto, solo l'errore di ritorno.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><span class=""><br>
<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">
Io personalmente trovo che la gestione degli errori di Go rende il codice complessivamente molto piu' snello.<br>
</blockquote></span>
Dipende dal concetto di snello che hai Per esempio, se per creare un file, devo fare questo:<br>
<br>
file, err := os.Create(filename)<br>
if err != nil {<br>
  panic(err)<br>
}<br>
defer file.Close()<br>
<br>
invece di questo:<br>
<br>
try:<br>
    with open(filename, "w") as f:<br>
        # Codice<br>
except:<br>
    raise Exception("Argh!")<br>
<br>
beh, io trovo piu` snella e chiara la seconda :)<div class=""><div class="h5"><br></div></div></blockquote><div><br></div><div>Come dicevo... quello non e' Go idiomatico a mio avviso. Qualcuno con piu' esperienza interverra', ma io quel codice lo scriverei diversamente. E non sono sicuro che userei panic (a meno che davvero non sono molto vicino al top-level...). </div><div><br></div><div>Uno dei problemi con le eccezioni e' quando hai piu' operazioni che possono andare male. Immagina di dovere aprire n files. E immagina che un qualunque fallimento implica che devi uscire di li...</div><div><br></div><div>Immagina anche di dovere fare una serie di operazioni sui files, e di dovere rimuovere i files in caso appunto di errore (aprendo o lavorandoci). Ecco... questo comincia a dare un'idea del come il codice con le eccezioni diventi bruttozzo alla svelta. </div><div><br></div><div>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). </div></div><div><br></div>-- <br><div class="gmail_signature"> .<br>..: -enrico-</div>
</div></div>