Leggibilità della programmazione funzionale [chiuso]


13

Sono curioso di questo perché ricordo prima di imparare qualsiasi linguaggio funzionale, li ho considerati tutti orribilmente, terribilmente, terribilmente illeggibili. Ora che conosco Haskell e f #, trovo che ci vuole un po 'più tempo a leggere meno codice, ma quel piccolo codice fa molto più di una quantità equivalente in un linguaggio imperativo, quindi sembra un guadagno netto e non sono estremamente praticato in funzionale.

Ecco la mia domanda, sento costantemente dalla gente di OOP che lo stile funzionale è terribilmente illeggibile. Sono curioso di sapere se questo è il caso e mi sto illudendo, o se si sono presi il tempo per imparare un linguaggio funzionale, l'intero stile non sarebbe più illeggibile di OOP?

Qualcuno ha visto delle prove o ha avuto aneddoti in cui ha visto andare in un modo o nell'altro con una frequenza sufficiente per dirlo? Se la scrittura funzionale è davvero di bassa leggibilità, non voglio continuare a usarla, ma davvero non so se è così o no.


1
Ricordo di aver visto il codice di "Super Nario Brothers" svn.coderepos.org/share/lang/haskell/nario quando sapevo a malapena qualcosa sulla programmazione funzionale e ricordo di aver pensato 'Wow. Sembra davvero leggibile '
WuHoUnited,



3
@BlackICE linq e lambdas sono molto lontani dalla definizione di "programmazione funzionale". La programmazione funzionale coinvolge i sistemi di tipi il più delle volte perché quando inizi a riconoscere le funzioni come tipi anziché solo oggetti / classi / record come tipi, finisci con molte nuove abilità e tecniche. Le espressioni LINQ e Lambda (o la lista monade e le funzioni di ordine superiore) sono solo due specifiche tecniche risultanti, di cui ce ne sono molte altre.
Jimmy Hoffa,

Risposte:


15

La leggibilità del codice è molto soggettiva . Per questo motivo, persone diverse considererebbero lo stesso codice leggibile o illeggibile.

Un compito x = x + 1sembra strano per un matematico, perché un compito sembra fuorviante simile a un confronto.

Un semplice modello di progettazione OOP sarebbe del tutto poco chiaro per qualcuno che non ha familiarità con questi schemi. Considera singleton . Qualcuno avrebbe chiesto "Perché ho bisogno di un costruttore privato? Qual è il metodo misterioso getInstance() ? Perché non creiamo una variabile globale e vi assegniamo un oggetto?"

Lo stesso vale per un codice FP. A meno che non si conoscano gli schemi logici dietro le quinte, sarà molto difficile persino capire qualcosa di molto semplice, ad esempio come passare una funzione come argomento a un'altra funzione.

Detto questo, si dovrebbe capire che FP non è un proiettile d'argento. Esistono molte applicazioni in cui FP sarebbe difficile da applicare e pertanto porterebbe a un codice leggibile peggio .


Le persone dovrebbero creare qualcosa di simile a loro (l'umanità). Ad esempio, anche nella visione artificiale abbiamo reti neurali ora. Il corpo umano è costituito da oggetti, attributi, metodi e descriviamo altre cose usando oggetti, attributi, metodi. Quindi la programmazione funzionale non ha alcun senso
user25

12

Risposta breve:

Uno degli elementi che inducono le persone a dire che il codice di programmazione funzionale è difficile da leggere è che dà la preferenza a una sintassi più compatta.

Risposta lunga:

La stessa programmazione funzionale non può essere leggibile o illeggibile, poiché è un paradigma, non uno stile di scrittura del codice. In C # per esempio, la programmazione funzionale è simile a:

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

e sarebbe considerato leggibile da qualsiasi persona con sufficiente esperienza in Java, C # o linguaggi simili.

La sintassi della lingua, d'altra parte, è più compatta per molti linguaggi funzionali (inclusi Haskell e F #) rispetto alle lingue OOP popolari, dando una preferenza ai simboli piuttosto che alla parola in inglese semplice.

Questo vale anche per le lingue al di fuori di FP. Se confronti le lingue OOP popolari con alcune meno popolari che tendono a usare più parole inglesi, le ultime sembrerebbero più facili da capire per le persone senza esperienza di programmazione.

Confrontare:

public void IsWithinRanges<T>(T number, param Range<T>[] ranges) where T : INumeric
{
    foreach (var range in ranges)
    {
        if (number >= range.Left && number <= range.Right)
        {
            return true;
        }
    }

    return false;
}

per:

public function void IsWithinRanges
    with parameters T number, param array of (Range of T) ranges
    using generic type T
    given that T implements INumeric
{
    for each (var range in ranges)
    {
        if (number is from range.Left to range.Right)
        {
            return true;
        }
    }

    return false;
}

Nello stesso modo:

var a = ((b - c) in 1..n) ? d : 0;

potrebbe essere espresso in un linguaggio immaginario come:

define variable a being equal to d if (b minus c) is between 1 and n or 0 otherwise;

Quando la sintassi più corta è migliore?

Mentre una sintassi più dettagliata è più facile da capire per un principiante, una sintassi più compatta è più facile per gli sviluppatori esperti. Codice più breve significa meno caratteri da digitare, il che significa maggiore produttività.

In particolare, non ha davvero senso forzare una persona a digitare una parola chiave per indicare qualcosa che può essere dedotto dalla sintassi.

Esempi:

  • In PHP devi digitare functionprima di ogni funzione o metodo, senza un motivo specifico per farlo.

  • Ada mi ha sempre spaventato per aver costretto gli sviluppatori a digitare molte parole inglesi, soprattutto perché non esiste un IDE corretto con il completamento automatico. Non ho usato Ada di recente e il loro tutorial ufficiale è inattivo, quindi non posso fare un esempio; se qualcuno ha un esempio, sentiti libero di modificare la mia risposta.

  • La sintassi 1..n, utilizzata in molti FP (e Matlab), potrebbe essere sostituita da between 1, no between 1 and no between (1, n). La betweenparola chiave rende molto più facile la comprensione per le persone che non hanno familiarità con la sintassi della lingua, ma tuttavia, due punti sono molto più veloci da digitare.


8
Sono d'accordo con il sentimento, ma per motivi diversi. Non si tratta della facilità con cui un esperto può scrivere codice, in quanto il codice viene generalmente letto molto più spesso di quanto non sia scritto. Il codice più breve può anche aiutare la leggibilità, non sprecando tempo prezioso, spazio sullo schermo e capacità mentale se qualcosa fosse già evidente da un indicatore più breve (ad esempio, newline invece di punti e virgola, le due coincidenze comunque). Può anche ferire quando diventa troppo enigmatico (ad esempio, preferisco for x in xsover for x xs, perché mi aiuta a distinguere la variabile loop e il contenitore).

Molte delle persone che trovo si lamentano di FP si lamentano anche dello snippet C # che hai elencato come hai detto, è anche un esempio di FP. Forse questo è più un problema dei miei colleghi che della leggibilità se la maggior parte delle persone di OOP trova quel frammento leggibile, anche se non sono completamente convinto che la maggior parte delle persone di OOP fa delle mie esperienze ... quindi perché non mi sento così sicuro di questo argomento.
Jimmy Hoffa,

@Jimmy Hoffa: vedi programmers.stackexchange.com/a/158721/6605 dove discuto esattamente lo snippet C # e come viene percepito dai programmatori principianti.
Arseni Mourzenko,

1
@Sergiy: in Java o C #, una firma del metodo non contiene una methodparola chiave. Esempio: public int ComputePrice(Product product). L'aggiunta di tale parola chiave, come ha fatto PHP, aggiunge solo confusione invece di rendere le cose più facili da leggere. Se un programmatore non è in grado di capire che una funzione è una funzione della sua firma, o questo programmatore non ha alcuna esperienza o il codice è molto più illeggibile del peggior codice pezzo che abbia mai visto.
Arseni Mourzenko,

1
@Sergiy: ci sono casi in cui la verbosità può aiutare (altrimenti vedremmo lingue e codici come p i ComputePrice(product): _.unitPrice * ~.cart[_]), ma alcune lingue spingono troppo oltre. L'esempio della functionparola chiave ridondante in PHP è un buon esempio.
Arseni Mourzenko,

6

Penso che uno dei motivi sia che la programmazione funzionale tende a lavorare con concetti molto più astratti. Questo rende il codice più breve e più leggibile per le persone che conoscono e comprendono i concetti (perché esprimono appropriatamente l'essenza del problema), ma è illeggibile per le persone che non ne hanno familiarità.

Ad esempio, per un programmatore funzionale è naturale scrivere un codice che può fallire in diversi punti all'interno della Maybemonade o Eithermonade. Oppure, per scrivere un calcolo con stato con l'aiuto della Statemonade. Ma per qualcuno che non capisce il concetto di monadi, tutto questo sembra una specie di stregoneria nera.

Quindi penso che sarebbe ragionevole usare pezzi di programmazione funzionale anche in un team di persone OOP, purché si utilizzino concetti che sono facili da capire o da imparare, come mappare una collezione con una funzione o chiusure .

(E ovviamente dipende anche dal programmatore che ha scritto un pezzo di codice. Puoi scrivere un codice orribilmente illeggibile in qualsiasi lingua, così come puoi scrivere in uno stile piacevole e pulito.)


2

La leggibilità non ha nulla a che fare con i paradigmi, i modelli e simili. Più codice riflette la semantica del tuo dominio problematico, più sta gestendo una terminologia e modi di dire di tale dominio problematico, più è leggibile.

Questo è il motivo per cui il codice OOP non è affatto leggibile: non che molti problemi del mondo reale siano naturalmente espressi in termini di tassonomie gerarchiche. Gli oggetti che si scambiano messaggi sono un concetto strano ed è molto lontano da qualsiasi cosa "reale". Sorprendentemente, ci sono molti più concetti del mondo reale che possono essere naturalmente mappati agli idiomi funzionali. I problemi tendono a essere definiti in modo dichiarativo, quindi una soluzione dichiarativa è più naturale.

Tuttavia, ci sono molte altre semantiche che potrebbero essere più vicine al mondo reale di un puro funzionale, OOP puro, flusso di dati puro o qualunque cosa sarebbe mai stata. E per questo motivo, un approccio orientato al linguaggio alla risoluzione dei problemi produce il codice più leggibile, battendo tutti i paradigmi "puri" esistenti. Con le lingue specifiche del dominio, ogni problema è espresso nella sua terminologia naturale. E i linguaggi funzionali sono leggermente migliori delle cose tradizionali di OOP nel facilitare l'implementazione delle DSL (sebbene siano disponibili approcci molto migliori).


1

La leggibilità del codice è soggettiva. Sono sicuro che alcune persone lo criticano per mancanza di comprensione. Ci sono modi di dire naturalmente diversi nel modo in cui vengono implementati i modelli comuni. Ad esempio, il modello Visitatore non ha davvero senso in una lingua con corrispondenza del modello .

L'inferenza del tipo può essere una funzione complicata. Di solito, è un grande acceleratore di sviluppo, tuttavia a volte può essere difficile capire quali tipi sono coinvolti a causa della mancanza di contesto che porta a errori confusi. Questo non si limita ai linguaggi di programmazione funzionale - l'ho visto anche nei modelli C ++!

Le lingue moderne tendono ad essere multi-paradigma . Ciò include sia C # che F #. È possibile scrivere uno stile funzionale in C # e uno stile imperativo in F #. Le stesse persone che criticano i linguaggi funzionali probabilmente scrivono codice funzionale nel loro linguaggio orientato agli oggetti.

Lo sviluppo commerciale nei linguaggi funzionali è ancora relativamente immaturo. Ho visto un certo numero di errori che sarebbero stati considerati cattivi in ​​qualsiasi lingua. Ad esempio:

  1. Cambiare stile man mano che impari la lingua (e non il refactoring) portando a un'incoerenza.
  2. Troppo in un file.
  3. Troppe dichiarazioni su una riga.

Per quanto riguarda gli errori aggiungerei anche: 4. Uso eccessivo di simboli, ad esempio: ~ @ # $% ^ & * () -_ + = \ | /.,; 5. Caratteristiche implicite: dichiarazioni / parole chiave / dichiarazioni opzionali, conversioni implicite, ecc .;
Sergiy Sokolenko,
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.