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


43

Caso : sto lavorando in un'azienda, scrivendo un'applicazione in Python che sta gestendo molti dati negli array. Sono l'unico sviluppatore di questo programma al momento, ma probabilmente verrà utilizzato / modificato / esteso in futuro (1-3 anni) da qualche altro programmatore, in questo momento a me sconosciuto. Probabilmente non sarò lì direttamente per aiutarti, ma forse darò un po 'di supporto via e-mail se ho 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 va bene scrivere codice come questo?

  • In che modo uno sviluppatore che non ha scritto / imparato FP reagisce a questo codice?
  • È leggibile?
  • Modificabile?
  • Dovrei scrivere 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 lui dice semplicemente "FP è magia nera, ma se funziona ed è la soluzione più efficiente, allora va bene usarla."

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


14
A mio avviso, molti linguaggi moderni consentono un certo grado di programmazione funzionale e l' utilizzo che diventa sempre più comune. Personalmente direi di provarci (almeno per le attività di filtro / mappatura relativamente semplici).
Joachim Sauer,

Non posso darti una risposta inalterata, perché mi piace FP. Sarebbe una buona idea aggiungere un commento, se è comunque semplice.
Bruno Schäpper,

3
Il tuo commento potrebbe essere più chiaro; Il filtro include elementi per i quali il test è True, quindi il tuo commento potrebbe essere letto # Select the item's from measures for which included(item.time, dur) == True:, evitare un doppio negativo migliora sempre la comprensione.
Martijn Pieters,

6
Hai preso in considerazione l'utilizzo della comprensione dell'elenco? Sono spesso considerati più "pitonici" della mappa / filtro.
phant0m

2
@MatjazMuhic Bene, è bellissimo ...;)
kd35a

Risposte:


50

È leggibile?

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

In effetti, GvR ha persino considerato di abbandonare del tutto quelle funzioni.

Considera questo:

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

Inoltre, ciò ha il vantaggio che la comprensione di un elenco restituirà sempre un elenco. map()e filter()d'altra parte restituirà un iteratore in Python 3.

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

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

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

È modificabile?

Sì, certo, tuttavia, c'è una cosa per renderlo più semplice: renderlo una funzione, non un 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 ridimensionerà molto più facilmente, inoltre, ti permetterà di riutilizzare il tuo controllo. (Si noti che è possibile creare funzioni all'interno di altre funzioni, questo può tenerlo più vicino al luogo in cui viene utilizzato.)

In che modo uno sviluppatore che non ha scritto / imparato FP reagisce su un codice del genere?

Cerca su 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 tu debba scrivere tali commenti. Usa nomi di variabili e funzioni validi, quindi il codice è quasi autoesplicativo. Non puoi prevedere quali lingue non siano note a uno sviluppatore. Per quanto ne sai, il prossimo sviluppatore potrebbe non sapere cos'è un dizionario.

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


5
Potrebbe valere la pena menzionare anche le espressioni dei generatori , che funzionano allo stesso modo della comprensione delle liste ma restituiscono i generatori invece delle liste, come mape filterfanno in Python 3.
James,

@James: Ottima idea, li ho aggiunti al mio post. Grazie!
phant0m

2
Solitamente sollevo reduceun controesempio per cui puoi sempre usare un argomento di comprensione dell'elenco , poiché è relativamente difficile da sostituire reducecon un generatore inline o comprensione dell'elenco.
Kojiro,

4
+1 per notare il linguaggio preferito della lingua in questione. La coerenza di IMHO ha un valore enorme e l'uso di un linguaggio nel modo in cui una parte significativa dei "oratori" può fornire più manutenibilità rispetto ai commenti a volte.
Joshua Drake,

4
+1 per "Cerca su Google la documentazione di Python e sa come funziona cinque minuti dopo. Altrimenti, non dovrebbe programmare in Python."
Bjarke Freund-Hansen,

25

Poiché la comunità di sviluppatori sta riacquistando interesse per la programmazione funzionale, non è inusuale vedere una programmazione funzionale in linguaggi che in origine erano completamente orientati 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 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 dovresti preoccuparti se sai che il prossimo sviluppatore che manterrà il tuo codice avrà un'esperienza complessiva sufficiente e una certa conoscenza della programmazione funzionale, ma:

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


8
+1 se FP non rende il tuo codice più leggibile a nessuno , stai sbagliando.
György Andrasek,

7
Vedo come in isolamento qualcuno che non ha mai visto FP prima potesse considerare il primo snippet più leggibile rispetto a quest'ultimo. E se lo moltiplichiamo per 100? Leggendo le 100 brevi frasi sintetiche quasi in stile inglese che descrivono il cosa rispetto a migliaia di righe di idraulica come codice. Il semplice volume di codice, non importa quanto familiare, dovrebbe essere così travolgente che la familiarità non è più una vittoria così grande. Ad un certo punto deve essere più facile per chiunque mettersi a proprio agio con gli elementi di FP e poi leggere una manciata di righe vs leggere migliaia di righe di codice familiare.
Esailija,

7
Ciò che mi preoccupa di più è che siamo contenti di credere che sia accettabile per gli sviluppatori non imparare lo stile funzionale. I vantaggi sono stati dimostrati molte volte, il paradigma è supportato dai linguaggi tradizionali. Se le persone non hanno la capacità di comprendere le tecniche funzionali, dovrebbero essere insegnate o dovrebbero insegnare a se stesse, anche se non intendono usarla. Odio XSLT con una passione ardente, ma ciò non mi ha impedito di impararlo, come professionista.
Sprague,

2
@MainMa Il tuo punto è completamente valido, avrei dovuto essere più specifico. Ciò che intendo è che uno sviluppatore in una determinata lingua dovrebbe utilizzare le funzionalità di tali lingue in modo efficace. Ad esempio, l'uso di "stile funzionale" in C # (uso di filtro / mappa / riduzione della programmazione di stile o funzioni di passaggio / ritorno) non è una novità, quindi penso che tutti gli sviluppatori C # che non sono in grado di leggere tali dichiarazioni dovrebbero aggiornare il loro abilità ... probabilmente molto rapidamente. Penso certamente che ogni sviluppatore dovrebbe imparare un po 'di Haskell, ma solo per motivi accademici. Questa è una cosa diversa
Sprague,

2
@Sprague: ottengo il tuo punto e sono d'accordo. È solo che non possiamo aspettarci, ad esempio, che i programmatori C # inizino a usare i paradigmi di FP, quando troppi di quei programmatori non usano nemmeno i generici. Fino a quando non ci saranno così tanti programmatori non qualificati, la barra della nostra professione sarà bassa, soprattutto perché la maggior parte delle persone senza background tecnico non capisce affatto di cosa tratta lo sviluppo.
Arseni Mourzenko,

20

Sono un programmatore non FP e recentemente 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 del tempo (come mezz'ora) per capire tutto (il codice tutto sommato non era molto grande).

Non c'erano commenti e penso che non ce n'era bisogno per nessuno. Non ho nemmeno chiesto al mio collega di aiutarmi a capire il suo codice.

Tenendo conto del fatto che sto lavorando da circa 1,5 anni, penso che la maggior parte dei programmatori sarà in grado di comprendere e modificare tale codice, da quando 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). Quindi molti programmatori non-FP lo affrontano abbastanza spesso e lo snippet di codice che hai incluso non è qualcosa di terribile o incomprensibile.


1
Grazie per il tuo commento! Mi ha dato maggiori informazioni sull'altra prospettiva. È facile diventare ciechi in casa, e quindi non sai com'era quando non conoscevi FP :)
kd35a

1
@ kd35a, prego. Fortunatamente, posso essere obiettivo qui))
superM

1
+1 per indicare che molte lingue ora contengono elementi di FP.
Joshua Drake,

LINQ è l'esempio principale di FP in C #
Konrad Morawski,

3

Direi sicuramente di si!

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

Ad esempio, ritengo che la scrittura di funzioni pure sia estremamente importante per qualsiasi software scritto in qualsiasi lingua (dove per "puro" non intendo effetti collaterali) perché:

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

Inoltre, spesso evito di mutare valori e variabili, un altro concetto preso in prestito da FP.

Entrambe queste tecniche funzionano bene in Python e in altre lingue che non sono comunemente classificate come funzionali. Spesso sono persino supportati dal linguaggio stesso (ovvero finalvariabili in Java). Pertanto, i futuri programmatori di manutenzione non dovranno affrontare un'enorme barriera per comprendere il codice.


2

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

La discussione riguardava il "codice magico" e se doveva essere incoraggiato o meno. Se lo guardiamo un po 'di più, sembra che le persone abbiano opinioni molto diverse su quello che in realtà era un "codice magico". Quelli che hanno sollevato la discussione sembravano significare soprattutto che le espressioni (in PHP) che utilizzavano lo stile funzionale erano "codice magico", mentre gli sviluppatori che avevano origine da altri linguaggi che usavano più stile FP nel loro codice sembrano pensare che il codice magico fosse piuttosto quando hai effettuato l'inclusione dinamica di file tramite nomi di file e così via.

Non siamo mai giunti a una buona conclusione su questo, più di quanto la gente pensi che il codice che sembra sconosciuto sia "magico" o difficile da leggere. Quindi è una buona idea evitare il codice che non sembra familiare ad altri utenti? Penso che dipenda da dove viene utilizzato. Vorrei astenermi dall'utilizzare espressioni in stile fp (inclusione dinamica di file e così via) in un metodo principale (o parti centrali importanti delle applicazioni) in cui i dati dovrebbero essere tunnel in modo chiaro e di facile lettura, intuitivo. D'altra parte, non penso che uno dovrebbe avere paura di spingere la busta, gli altri sviluppatori probabilmente impareranno rapidamente FP se si trovano ad affrontare il codice FP e forse hanno qualche buona risorsa interna per consultare i 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.


Un buon punto per evitare di usarlo nel codice di livello superiore!
kd35a,

Ho sempre pensato che ci fosse una mancanza di formalismo e molte opportunità nel definire tecniche di programmazione in diversi tipi di blocchi (come metodi statici, metodi pubblici, costruttori, ecc.) Buona risposta ... mi sta facendo rivisitare alcuni di quei pensieri.
Sprague,

2

La comunità C ++ ha recentemente ottenuto anche lambda, e credo che abbiano più o meno 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::copynon è nuovo e neanche le _ifvarianti sono nuove, ma lo è lambda. Eppure è definito piuttosto chiaramente nel contesto: durè acquisito e quindi costante, Item ivaria nel ciclo e la singola returnistruzione fa tutto il lavoro.

Questo sembra accettabile per molti sviluppatori C ++. Tuttavia, non ho raccolto opinioni sulle lambda di ordine superiore e mi aspetterei molto meno accettazione.


Punto interessante, che possono esserci risposte diverse a seconda della lingua in cui si trova. Probabilmente correlato al post di @Christopher Käck su come i programmatori PHP hanno avuto più problemi con questo tipo di cose rispetto ai programmatori Python.
kd35a,

0

Pubblica uno snippet di codice a un altro sviluppatore che non è fluente in Python e chiedigli se può passare 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 sciocco e non capire qualcosa che dovrebbe essere ovvio? Sì, ma dovresti sempre programmare secondo KISS.

Forse il tuo codice è più efficiente / bello / elegante di un approccio più semplice, a prova di idiota? Quindi devi chiederti: devo fare questo? Ancora una volta, se la risposta è no, allora non farlo!

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


sentiti libero di spiegare il motivo del voto negativo
Arnab Datta,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.