Le piccole quantità di programmazione funzionale sono comprensibili per le persone non FP? [chiuso]

43

Caso : lavoro in un'azienda, sto scrivendo un'applicazione in Python che gestisce molti dati negli array. Sono l'unico sviluppatore di questo programma al momento, ma probabilmente sarà usato / modificato / esteso in futuro (1-3 anni) da qualche altro programmatore, in questo momento sconosciuto a me. Probabilmente non sarò lì direttamente per aiutare, ma forse darò un po 'di supporto via email se avrò il tempo per farlo.

Quindi, come sviluppatore che ha imparato la programmazione funzionale (Haskell), tendo a risolvere, ad esempio, il filtro in questo modo:

filtered = filter(lambda item: included(item.time, dur), measures)

Il resto del codice è OO, sono solo alcuni piccoli casi in cui voglio risolverlo in questo modo, perché secondo me è molto più semplice e più bello.

Domanda : oggi è corretto scrivere codice come questo?

  • In che modo uno sviluppatore che non ha scritto / imparato FP reagisce al codice come questo?
  • È leggibile?
  • modificabile?
  • Devo scrivere una documentazione come spiegare a un bambino cosa fa la linea?

     # Filter out the items from measures for which included(item.time, dur) != True
    

Ho chiesto al mio capo e ha appena detto "FP è magia nera, ma se funziona e rappresenta la soluzione più efficiente, allora è OK usarlo."

Qual è la tua opinione su questo? Come programmatore non FP, come reagisci al codice? Il codice è "googable" in modo che tu possa capire cosa fa? Mi piacerebbe un feedback su questo.

    
posta kd35a 30.07.2012 - 11:54
fonte

7 risposte

50

Is it readable?

Per me: Sì, ma ho capito che la comunità Python sembra spesso considerare la comprensione delle liste una soluzione più pulita rispetto all'utilizzo di map() / filter() .

In effetti, GvR ha anche considerato che ha eliminato completamente tali funzioni.

Considera questo:

filtered = [item for item in measures if included(item.time, dur)]

Inoltre, questo ha il vantaggio che una lista di comprensione restituirà sempre una lista. map() e filter() d'altra parte restituiranno un iteratore in Python 3.

Nota: se vuoi avere un iteratore, è semplice come sostituire [] con () :

filtered = (item for item in measures if included(item.time, dur))

Per essere onesti, vedo poco o nessun motivo per usare map() o filter() in Python.

Is it Modifiable?

Sì, certo, tuttavia, c'è una cosa che rende tutto più semplice: rendilo una funzione, non una lambda.

def is_included(item):
    return included(item.time, dur)
filtered = filter(is_included, measures)
filtered = [item for item in measures if is_included(item)]

Se le tue condizioni diventano più complesse, questo si ridimensionerà molto più facilmente, inoltre, ti permetterà di riutilizzare l'assegno. (Si noti che è possibile creare funzioni all'interno di altre funzioni, questo può tenerlo più vicino al luogo in cui viene utilizzato.)

How does a developer that hasn't written/learned FP react on code like this?

Cerca a google la documentazione di Python e sa come funziona cinque minuti dopo. Altrimenti, non dovrebbe programmare in Python.

map() e filter() sono estremamente semplici. Non è che stai chiedendo loro di capire le monadi. Ecco perché non penso che sia necessario scrivere tali commenti. Usa buoni nomi di variabili e funzioni, quindi il codice è quasi auto esplicativo. Non è possibile prevedere quali caratteristiche linguistiche non sono a conoscenza di uno sviluppatore. Per quel che ne sai, il prossimo sviluppatore potrebbe non sapere che cos'è un dizionario.

Ciò che non capiamo di solito non è leggibile per noi. Quindi, potresti obiettare che non è meno leggibile di una lista di comprensione se non hai mai visto nessuno di loro prima. Ma come ha detto Joshua nel suo commento, anch'io credo sia importante essere coerenti con ciò che altri sviluppatori usano - almeno se l'alternativa non offre alcun vantaggio sostanziale.

    
risposta data 30.07.2012 - 12:46
fonte
24

Poiché la comunità degli sviluppatori sta riacquistando interesse nella programmazione funzionale, non è raro vedere una programmazione funzionale in lingue che erano originariamente completamente orientate agli oggetti. Un buon esempio è C #, in cui i tipi anonimi e le espressioni lambda consentono di essere molto più brevi ed espressivi attraverso la programmazione funzionale.

Detto questo, la programmazione funzionale è strana per i principianti. Ad esempio, durante un corso di formazione, ho spiegato ai principianti come possono migliorare il loro codice attraverso la programmazione funzionale in C #, alcuni di loro non erano convinti, e alcuni hanno detto che il codice originale era più facile da capire per loro. L'esempio che ho dato a loro era il seguente:

Codice prima del refactoring:

var categorizedProducts = new Dictionary<string, List<Product>>();

// Get only enabled products, filtering the disabled ones, and group them by categories.
foreach (var product in this.Data.Products)
{
    if (product.IsEnabled)
    {
        if (!categorizedProducts.ContainsKey(product.Category))
        {
            // The category is missing. Create one.
            categorizedProducts.Add(product.Category, new List<Product>());
        }

        categorizedProducts[product.Category].Add(product);
    }
}

// Walk through the categories.
foreach (var productsInCategory in categorizedProducts)
{
    var minimumPrice = double.MaxValue;
    var maximumPrice = double.MinValue;

    // Walk through the products in a category to search for the maximum and minimum prices.
    foreach (var product in productsInCategory.Value)
    {
        if (product.Price < minimumPrice)
        {
            minimumPrice = product.Price;
        }

        if (product.Price > maximumPrice)
        {
            maximumPrice = product.Price;
        }
    }

    yield return new PricesPerCategory(category: productsInCategory.Key, minimum: minimumPrice, maximum: maximumPrice);
}

Stesso codice dopo il refactoring utilizzando FP:

return this.Data.Products
    .Where(product => product.IsEnabled)
    .GroupBy(product => product.Category)
    .Select(productsInCategory => new PricesPerCategory(
              category: productsInCategory.Key, 
              minimum:  productsInCategory.Value.Min(product => product.Price), 
              maximum:  productsInCategory.Value.Max(product => product.Price))
    );

Questo mi fa pensare che:

  • non ti devi preoccupare se sai che il prossimo sviluppatore che manterrà il tuo codice avrà sufficiente esperienza complessiva e una certa conoscenza della programmazione funzionale, ma:

  • altrimenti, evita la programmazione funzionale, o metti un commento dettagliato spiegando allo stesso tempo la sintassi, i vantaggi e le possibili avvertenze del tuo approccio rispetto a uno di programmazione non funzionale.

risposta data 30.07.2012 - 12:24
fonte
20

Sono un programmatore non FP e di recente ho dovuto modificare il codice del mio collega in JavaScript. C'era una richiesta Http con un callback che assomigliava molto alla dichiarazione inclusa da te. Devo dire che mi ci è voluto un po 'di tempo (come mezz'ora) per capire tutto (il codice nel complesso non era molto grande).

Non ci sono stati commenti, e penso che non ci fosse alcuna necessità per nessuno. Non ho nemmeno chiesto al mio collega di aiutarmi a capire il suo codice.

Considerando che lavoro per circa 1,5 anni, penso che la maggior parte dei programmatori sarà in grado di capire e modificare tale codice, dal momento che l'ho fatto.

Inoltre, come ha detto Joachim Sauer nel suo commento, spesso ci sono pezzi di FP in molte lingue, come C # (indexOf, per esempio). Così tanti programmatori non FP si occupano di questo molto spesso, e il frammento di codice che hai incluso non è qualcosa di terribile o incomprensibile.

    
risposta data 30.07.2012 - 12:26
fonte
3

Direi di sì sì!

Ci sono molti aspetti della programmazione funzionale e l'uso di funzioni di ordine superiore, come nel tuo esempio, è solo uno di questi.

Ad esempio, considero che scrivere pure funzioni è estremamente importante per qualsiasi software scritto in qualsiasi lingua (dove per " puro "Voglio dire senza effetti collaterali), perché:

  • sono più facili da testare l'unità
  • sono molto più componibili rispetto alle funzioni di effetti collaterali
  • sono più facili da eseguire il debug

Spesso evito anche di mutare valori e variabili - un altro concetto preso in prestito da FP.

Entrambe queste tecniche funzionano bene in Python e in altri linguaggi che non sono comunemente classificati come funzionali. Spesso sono supportati anche dal linguaggio stesso (cioè final variabili in Java). Pertanto, i futuri programmatori di manutenzione non si troveranno di fronte a un'enorme barriera alla comprensione del codice.

    
risposta data 30.07.2012 - 18:48
fonte
2

Abbiamo avuto la stessa discussione su un'azienda che ho lavorato l'anno scorso.

La discussione riguardava "codice magico" e se doveva essere incoraggiata o meno. Guardandolo un po 'di più sembrava che le persone avessero opinioni molto diverse su quello che in realtà era "codice magico". Quelli che hanno portato la discussione sembravano principalmente indicare che le espressioni (in PHP) che usavano lo stile funzionale erano "codice magico" mentre gli sviluppatori che provenivano da altri linguaggi che usavano più stile FP nel loro codice sembrano pensare che il codice magico fosse piuttosto quando hai reso l'inclusione dinamica dei file tramite nomi di file e così via.

Non siamo mai giunti a nessuna conclusione positiva, più di quanto la gente pensi che il codice che sembra non familiare sia "magico" o difficile da leggere. Quindi è una buona idea evitare il codice che non sembra familiare agli altri utenti? Penso che dipenda da dove viene usato. Mi asterrò dall'utilizzare espressioni in stile fp (inclusione di file dinamici e così via) in un metodo principale (o parti centrali importanti delle applicazioni) in cui i dati dovrebbero essere tunnel in modo chiaro e intuitivo, facile da leggere. D'altra parte, non credo che si debba avere paura di spingere la busta, gli altri sviluppatori probabilmente impareranno FP velocemente se si troveranno ad affrontare il codice FP e forse avranno delle buone risorse interne da consultare sui problemi.

TL; DR: evitare nella parte centrale di alto livello delle applicazioni (che devono essere lette per una panoramica della funzionalità dell'applicazione). Altrimenti usalo.

    
risposta data 30.07.2012 - 12:47
fonte
2

La comunità C ++ ha recentemente ottenuto anche lambda, e credo che abbiano grosso modo la stessa domanda. La risposta potrebbe non essere la stessa, però. L'equivalente in C ++ sarebbe:

std::copy_if(measures.begin(), measures.end(), inserter(filter),
  [dur](Item i) { return included(i, dur) } );

Ora std::copy non è nuovo, e anche le varianti _if non sono nuove, ma è il lambda. Tuttavia è definito piuttosto chiaramente nel contesto: dur è catturato e quindi costante, Item i varia nel ciclo e la singola istruzione return fa tutto il lavoro.

Questo sembra accettabile per molti sviluppatori C ++. Non ho ancora provato le opinioni sui lambda di ordine superiore, e mi aspetterei molto meno accettazione.

    
risposta data 30.07.2012 - 15:19
fonte
0

Pubblica un frammento di codice su un altro dev che non è così fluente in python e chiedigli se può dedicare 5 minuti a controllare il codice per vedere se lo capisce.

Se sì, probabilmente sei a posto. In caso contrario, dovresti cercare di renderlo più chiaro.

Il tuo collega potrebbe essere stupido e non capire qualcosa che dovrebbe essere ovvio? Sì, ma dovresti sempre programmare secondo KISS.

Forse il tuo codice è più efficiente / di bell'aspetto / elegante di un approccio più semplice, a prova di idiota? Allora devi chiederti: devo fare questo? Di nuovo, se la risposta è no, allora non farlo!

Se dopo tutto questo, pensi ancora di aver bisogno e vuoi farlo nel modo FP, allora fallo con tutti i mezzi. Fidati del tuo istinto, sono più adatti alle tue esigenze rispetto alla maggior parte delle persone su qualsiasi forum:)

    
risposta data 21.08.2012 - 12:01
fonte

Leggi altre domande sui tag