Quali funzionalità funzionali meritano una piccola confusione OOP per i benefici che apportano?

13

Dopo aver appreso la programmazione funzionale in Haskell e F #, il paradigma OOP sembra ass-back con classi, interfacce, oggetti. Quali aspetti della FP posso portare al lavoro che i miei colleghi possono capire? Alcuni stili FP valgono la pena di parlare con il mio capo di riqualificare la mia squadra in modo che possiamo usarli?

Possibili aspetti di FP:

  • Immutabilità
  • Applicazione parziale e aggiornamento
  • Funzioni di prima classe (puntatori di funzioni / oggetti funzionali / modello di strategia)
  • Valutazione pigra (e Monade)
  • Funzioni pure (senza effetti collaterali)
  • Espressioni (rispetto alle istruzioni - ogni riga di codice produce un valore anziché, o oltre a causare effetti collaterali)
  • ricorsione
  • Pattern Matching

È un free-for-all in cui possiamo fare tutto ciò che il linguaggio di programmazione supporta al limite che il linguaggio supporta? O c'è una linea guida migliore?

    
posta bonomo 13.11.2013 - 04:55
fonte

6 risposte

12

La programmazione funzionale è un paradigma diverso dalla programmazione orientata agli oggetti (una mentalità diversa e un modo diverso di pensare ai programmi). Hai iniziato a capire che qui c'è più di un modo (orientato agli oggetti) per pensare ai problemi e alle loro soluzioni. Ce ne sono altri (mi viene in mente la programmazione procedurale e generica). Come reagisci a questa nuova conoscenza, se accetti e integri questi nuovi strumenti e approcci nel tuo set di abilità, determinerai se cresci e diventerai uno sviluppatore più completo e competente.

Siamo tutti addestrati a gestire e agio con un certo livello di complessità. Mi piace chiamare il limite di hrair di una persona (da Watership Down, quanto in alto puoi contare). È una grande cosa espandere la tua mente, la tua capacità di considerare più opzioni e avere più strumenti per approcciare e risolvere i problemi. Ma è un cambiamento, e ti tira fuori dalla tua zona di comfort.

Un problema che potresti incontrare è che diventerai meno contenuto per seguire la folla di "tutto è un oggetto". Potrebbe essere necessario sviluppare pazienza mentre lavori con persone che potrebbero non capire (o volere capire) perché un approccio funzionale allo sviluppo del software funzioni bene per determinati problemi. Proprio come un approccio di programmazione generico funziona bene per determinati problemi.

Buona fortuna!

    
risposta data 13.11.2013 - 05:17
fonte
6

La programmazione funzionale è molto pratica, con i piedi per terra, produttività nella scrittura del codice di tutti i giorni: alcune funzionalità favoriscono la chiarezza, che è grande perché meno codice scrivi, meno guasti che fai e meno manutenzione è necessaria.

Essendo un matematico, trovo le cose funzionali fantasiose molto allettanti, ma di solito è utile quando si progetta un'applicazione: questi le strutture possono codificare nella struttura del programma molti invarianti di il programma, senza rappresentare questi invarianti per variabili.

La mia combinazione preferita potrebbe sembrare piuttosto banale, credo comunque che ha un impatto molto elevato sulla produttività. Questa combinazione è Parziale Applicazione e Currying e Funzioni di prima classe che vorrei fare relabel mai scrivere di nuovo un ciclo : passa invece il corpo del file loop a una funzione iterating o mapping. Sono stato recentemente assunto per a Lavoro in C ++ e mi sono divertito a notare, ho perso totalmente l'abitudine di scrivere per-loop!

La combinazione di Recursion e Pattern Matching annulla il bisogno di quel modello di progettazione dei visitatori . Basta confrontare il codice che ti serve programmare un valutatore di espressioni booleane: in qualsiasi funzionale linguaggio di programmazione questo dovrebbe essere di circa 15 righe di codice, in OOP la cosa giusta fare è usare quel modello di design Visitor , che lo trasforma esempio di giocattolo in un ampio saggio. I vantaggi sono ovvi e io non lo sono consapevole di qualsiasi inconveniente.

    
risposta data 13.11.2013 - 18:16
fonte
5

Potresti dover limitare le parti delle tue conoscenze che usi al lavoro, il modo in cui Superman deve fingere di essere Clark Kent per godersi i vantaggi di una vita normale. Ma sapere di più non ti farà mai del male. Detto questo, alcuni aspetti della programmazione funzionale sono appropriati per un negozio orientato agli oggetti, e altri aspetti potrebbero valere la pena di parlare con il tuo capo in modo da poter aumentare il livello medio di conoscenza del tuo negozio e ottenere di conseguenza un codice migliore.

FP e OOP non si escludono a vicenda. Guarda Scala. Alcuni pensano che sia il peggiore perché FP è impuro, ma alcuni pensano che sia il migliore per lo stesso motivo.

Uno per uno, ecco alcuni aspetti che funzionano perfettamente con OOP:

  • Pure Functions (senza effetti collaterali) - Ogni linguaggio di programmazione che conosco supporta questo. Rendono il tuo codice molto più facile da ragionare e dovrebbero essere usati ogni volta che è pratico. Non devi chiamarlo FP. Chiamalo semplicemente buone pratiche di codifica.

  • Immutabilità: la stringa è probabilmente l'oggetto Java più comunemente utilizzato ed è immutabile. Copro Oggetti Java immutabili e Immutable Java Collections sul mio blog. Alcuni di questi potrebbero essere applicabili a te.

  • Funzioni di prima classe (puntatori di funzioni / Oggetti funzionali / Pattern di strategia) - Java ha avuto una versione mutevole e mutante di questo dalla versione 1.1 con la maggior parte delle classi API (e ce ne sono centinaia) che implementano l'interfaccia Listener . Runnable è probabilmente l'oggetto funzionale più utilizzato. Le funzioni di prima classe sono più utili per codificare in una lingua che non le supporta in modo nativo, ma a volte vale lo sforzo extra quando semplificano altri aspetti del codice.

  • La ricorsione è utile per l'elaborazione di alberi. In un negozio OOP, questo è probabilmente il principale uso appropriato della ricorsione. L'uso della ricorsione per divertimento in OOP dovrebbe probabilmente essere disapprovato se per nessun altro motivo la maggior parte dei linguaggi OOP non ha lo spazio di stack di default per renderlo una buona idea.

  • Espressioni (rispetto alle istruzioni - ogni riga di codice produce un valore anziché, o oltre a causare effetti collaterali) - L'unico operatore valutativo in C, C ++ e Java è il Operatore ternario . Discuto dell'uso appropriato sul mio blog. Potresti scoprire di scrivere alcune semplici funzioni altamente riutilizzabili e valutative.

  • Valutazione pigra (e Monade) - per lo più limitata all'inizializzazione pigra in OOP. Senza le caratteristiche del linguaggio che lo supportano, potresti trovare alcune API che sono utili, ma scrivere il tuo è difficile. Massimizza il tuo uso di stream invece - guarda le interfacce di Writer e Reader per gli esempi.

  • Applicazione parziale e Currying - Non pratico senza funzioni di prima classe.

  • Pattern Matching - generalmente scoraggiato in OOP.

In sintesi, non penso che il lavoro debba essere un free-for-all in cui puoi fare tutto ciò che il linguaggio di programmazione supporta al limite che il linguaggio supporta. Penso che la leggibilità dei tuoi colleghi dovrebbe essere la tua cartina di tornasole per il codice creato per il noleggio. Dove ti irritano di più, cercherò di iniziare una formazione sul lavoro per ampliare gli orizzonti dei tuoi collaboratori.

    
risposta data 13.11.2013 - 17:05
fonte
3

Oltre alla programmazione funzionale e alla programmazione orientata agli oggetti, esiste anche la programmazione dichiarativa (SQL, XQuery). Imparare ogni stile ti aiuta ad acquisire nuove conoscenze e imparerai a scegliere lo strumento giusto per il lavoro.

Ma sì, può essere molto frustrante scrivere codice in una lingua e sapere che se si stesse usando qualcos'altro, si potrebbe essere molto più produttivi per un particolare dominio problematico. Tuttavia, anche se stai usando un linguaggio come Java, è possibile applicare concetti da FP al tuo codice Java, anche se in modo indiretto. Il framework Guava, ad esempio, fa parte di questo.

    
risposta data 13.11.2013 - 07:20
fonte
2

Come programmatore penso che non dovresti mai smettere di imparare. Detto questo, è molto interessante che l'apprendimento di FP stia contaminando le tue abilità OOP. Tendo a pensare di imparare OOP come imparare come andare in bicicletta; non ti dimentichi mai come farlo.

Mentre imparavo i dettagli della FP, mi sono trovato a pensare in modo più matematico e ho acquisito una migliore prospettiva sui mezzi con cui scrivo software. Questa è la mia esperienza personale.

Man mano che acquisisci più esperienza, i concetti di programmazione di base saranno molto più difficili da perdere. Quindi ti suggerisco di metterlo facilmente in pratica fino a quando i concetti di OOP non saranno completamente consolidati nella tua mente. FP è un deciso cambio di paradigma. Buona fortuna!

    
risposta data 13.11.2013 - 05:03
fonte
0

Ci sono già molte buone risposte, quindi il mio affronterà un sottoinsieme della tua domanda; Ovvero, prendo in considerazione la premessa della tua domanda, in quanto OOP e le caratteristiche funzionali non si escludono a vicenda.

Se usi C ++ 11, ci sono molti di questi tipi di funzioni di programmazione funzionale incorporate nella libreria linguaggio / standard che si sincronizzano (abbastanza) bene con OOP. Naturalmente, non sono sicuro di quanto TMP sarà ricevuto dal tuo capo o dai tuoi colleghi, ma il punto è che puoi ottenere molte di queste funzionalità in una forma o in un'altra in linguaggi non funzionali / OOP, come C ++.

L'uso di modelli con ricorsione del tempo di compilazione si basa sui tuoi primi 3 punti,

  • Immutabilità
  • ricorsione
  • Pattern Matching

In questo modello i valori sono immutabili (costanti in tempo di compilazione), ogni iterazione viene eseguita utilizzando la ricorsione e la ramificazione viene eseguita utilizzando (più o meno) la corrispondenza del modello, sotto forma di risoluzione di sovraccarico.

Come per gli altri punti, l'utilizzo di std::bind e std::function fornisce un'applicazione di funzione parziale e i puntatori di funzione sono incorporati nella lingua. Gli oggetti chiamabili sono oggetti funzionali (così come l'applicazione di funzioni parziali). Nota che per oggetti chiamabili, intendo quelli che definiscono il loro operator () .

La valutazione pigra e le funzioni pure sarebbero un po 'più difficili; per le funzioni pure, puoi usare le funzioni lambda che acquisiscono solo per valore, ma questo non è ideale.

Infine, ecco un esempio di utilizzo della ricorsione in fase di compilazione con l'applicazione di funzioni parziali. È un esempio un po 'forzato, ma dimostra la maggior parte dei punti sopra. Legherà in modo ricorsivo i valori di una determinata tupla a una determinata funzione e genererà un oggetto funzione (chiamabile)

#include <iostream>
#include <functional>

//holds a compile-time index sequence
template<std::size_t ... >
struct index_seq
{};

//builds the index_seq<...> struct with the indices (boils down to compile-time indexing)
template<std::size_t N, std::size_t ... Seq>
struct gen_indices
  : gen_indices<N-1, N-1, Seq ... >
{};

template<std::size_t ... Seq>
struct gen_indices<0, Seq ... >
{
    typedef index_seq<Seq ... > type;
};


template <typename RType>
struct bind_to_fcn
{
    template <class Fcn, class ... Args>
    std::function<RType()> fcn_bind(Fcn fcn, std::tuple<Args...> params)
    {
        return bindFunc(typename gen_indices<sizeof...(Args)>::type(), fcn, params);
    }

    template<std::size_t ... Seq, class Fcn, class ... Args>
    std::function<RType()> bindFunc(index_seq<Seq...>, Fcn fcn, std::tuple<Args...> params)
    {
        return std::bind(fcn, std::get<Seq>(params) ...);
    }
};

//some arbitrary testing function to use
double foo(int x, float y, double z)
{
    return x + y + z;
}

int main(void)
{
    //some tuple of parameters to use in the function call
    std::tuple<int, float, double> t = std::make_tuple(1, 2.04, 0.1);                                                                                                                                                                                                      
    typedef double(*SumFcn)(int,float,double);

    bind_to_fcn<double> binder;
    auto other_fcn_obj = binder.fcn_bind<SumFcn>(foo, t);
    std::cout << other_fcn_obj() << std::endl;
}
    
risposta data 08.02.2014 - 02:46
fonte