In che modo i sostenitori della Programmazione funzionale risponderebbero a questa affermazione nel Codice completo?


30

A pagina 839 della seconda edizione, Steve McConnell sta discutendo tutti i modi in cui i programmatori possono "conquistare la complessità" nei grandi programmi. I suoi consigli culminano con questa affermazione:

"La programmazione orientata agli oggetti fornisce un livello di astrazione che si applica contemporaneamente agli algoritmi e ai dati , una sorta di astrazione che la decomposizione funzionale da sola non ha fornito."

Insieme alla sua conclusione che "ridurre la complessità è senza dubbio la chiave più importante per essere un programmatore efficace" (stessa pagina), ciò sembra praticamente una sfida alla programmazione funzionale.

Il dibattito tra FP e OO è spesso inquadrato dai sostenitori di FP intorno alle questioni della complessità che deriva specificamente dalle sfide della concorrenza o del parallelismo. Ma la concorrenza non è certamente l'unico tipo di complessità che i programmatori software devono conquistare. Forse concentrarsi sulla riduzione di un tipo di complessità lo aumenta notevolmente in altre dimensioni, in modo tale che per molti casi il guadagno non vale il costo.

Se spostassimo i termini del confronto tra FP e OO da questioni particolari come la concorrenza o la riusabilità alla gestione della complessità globale, come sarebbe il dibattito?

MODIFICARE

Il contrasto che volevo evidenziare è che OO sembra incapsulare e astrarre lontano dalla complessità dei dati e degli algoritmi, mentre la programmazione funzionale sembra incoraggiare a lasciare più "esposti" i dettagli di implementazione delle strutture di dati durante il programma.

Vedi, ad esempio, Stuart Halloway (un sostenitore di Clojure FP) che qui afferma che "l'eccessiva specificazione dei tipi di dati" è "conseguenza negativa dello stile idiomatico OO" e favorisce la concettualizzazione di una Rubrica come un semplice vettore o mappa invece di un oggetto OO più ricco con proprietà e metodi aggiuntivi (non vettoriali e non mappa). (Inoltre, i sostenitori di OO e Design guidato dal dominio possono dire che esporre una Rubrica come vettore o mappa sovraesporre i dati incapsulati a metodi che sono irrilevanti o addirittura pericolosi dal punto di vista del dominio).


3
+1 nonostante la domanda sia stata inquadrata in modo piuttosto antagonista, è una buona domanda.
Mattnz,

16
Come molti hanno affermato nelle risposte, la decomposizione funzionale e la programmazione funzionale sono due diversi animali. Quindi la conclusione che "questa sembra essere una sfida alla programmazione funzionale" è chiaramente sbagliata, non ha nulla a che fare con essa.
Fabio Fracassi,

5
Chiaramente la conoscenza di McConnel nei moderni sistemi di dati di tipo funzionale e moduli di prima classe di alto ordine è alquanto irregolare. La sua affermazione è assolutamente senza senso, dato che abbiamo i moduli e i funzioni di prima classe (vedi SML), classi di tipo (vedi Haskell). È solo un altro esempio di come il modo di pensare OO sia più una religione che una metodologia di progettazione rispettosa. E, a proposito, dove hai preso questa cosa sulla concorrenza? La maggior parte dei programmatori funzionali non si preoccupa affatto del parallelismo.
SK-logic,

6
@ SK-logic Tutto ciò che McConnell ha affermato è che la "sola decomposizione funzionale" non fornisce gli stessi mezzi di astrazione di OOP, il che mi sembra un'affermazione abbastanza sicura. Da nessuna parte dice che le lingue FP non hanno mezzi di astrazione potenti come OOP. In realtà non menziona affatto le lingue FP. Questa è solo l'interpretazione del PO.
sepp2k,

2
@ sepp2k, ok, capisco. Tuttavia, un sistema molto complesso e ben stratificato di strutture dati e elaborazione di astrazioni può essere costruito su nient'altro che una decomposizione funzionale per un calcolo lambda quasi puro, attraverso la simulazione del comportamento dei moduli. Nessuna necessità di astrazioni OO.
Logica SK

Risposte:


13

Tieni presente che il libro è stato scritto per oltre 20 anni. Per i programmatori professionisti del momento, FP non esisteva: era interamente nel regno di accademici e ricercatori.

Dobbiamo inquadrare la "decomposizione funzionale" nel giusto contesto del lavoro. L'autore non si riferisce alla programmazione funzionale. Dobbiamo ricollegarlo alla "programmazione strutturata" e al GOTOcaos che ha preceduto. Se il tuo punto di riferimento è un vecchio FORTRAN / COBOL / BASIC che non aveva funzioni (forse, se fossi fortunato avresti ottenuto un singolo livello di GOSUB) e tutte le tue variabili sono globali, potendo abbattere il tuo programma in strati di funzioni è un grande vantaggio.

OOP è un ulteriore perfezionamento di questo tipo di "decomposizione funzionale". Non solo puoi raggruppare le istruzioni in funzioni, ma puoi anche raggruppare le funzioni correlate con i dati su cui stanno lavorando. Il risultato è un pezzo di codice chiaramente definito che puoi guardare e comprendere (idealmente) senza dover inseguire tutto il tuo codice per trovare cos'altro potrebbe operare sui tuoi dati.


27

Immagino che i sostenitori della programmazione funzionale sosterrebbero che la maggior parte dei linguaggi FP offre più mezzi di astrazione rispetto alla "sola decomposizione funzionale" e in effetti consente mezzi di astrazioni comparabili in potenza a quelli dei linguaggi orientati agli oggetti. Ad esempio, si potrebbero citare le classi di tipo di Haskell oi moduli di ordine superiore di ML come tali mezzi di astrazione. Quindi l'affermazione (di cui sono quasi certo sull'orientamento agli oggetti rispetto alla programmazione procedurale, non alla programmazione funzionale) non si applica a loro.

Va inoltre sottolineato che FP e OOP sono concetti ortogonali e non si escludono a vicenda. Quindi non ha senso confrontarli tra loro. Potresti paragonare molto bene "OOP imperativo" (ad esempio Java) con "OOP funzionale" (ad esempio Scala), ma l'affermazione che hai citato non si applica a quel confronto.


10
+1 "Funzion decomposition"! = "Programmazione funzionale". Il primo si basa sulla classica codifica sequenziale che utilizza strutture di dati vaniglia senza eredità, incapsulamento e polimorfismo (o solo laminati a mano). Il secondo esprime soluzioni usando il calcolo lambda. Due cose completamente diverse.
Binary Worrier, il

4
Ci scusiamo, ma la frase "programmazione procedurale" ha ostinatamente rifiutato di venire in mente prima. La "decomposizione funzionale" per me è molto più indicativa della programmazione procedurale che della programmazione funzionale.
Binary Worrier

Sì hai ragione. Ho ipotizzato che la programmazione funzionale favorisca le funzioni riutilizzabili che operano sempre sulle stesse semplici strutture dati (elenchi, alberi, mappe) e afferma effettivamente che questo è un punto di forza su OO. Vedi Stuart Halloway (un sostenitore di Clojure FP) che qui afferma che "l'eccessiva specificazione dei tipi di dati" è "conseguenza negativa dello stile idiomatico OO" e favorisce la concettualizzazione di una Rubrica come vettore o mappa invece di un oggetto OO più ricco con altri (non -vectorish e non-maplike) proprietà e metodi.
dan

Il link per la citazione di Stuart Halloway: thinkrelevance.com/blog/2009/08/12/…
dan

2
@dan Questo potrebbe essere il modo in cui viene fatto nel linguaggio tipizzato dinamicamente Clojure (non lo so, non uso Clojure), ma penso che sia pericoloso concludere da ciò, che è così che ha fatto in FP in generale. Le persone di Haskell, per esempio, sembrano essere molto grandi in termini di tipi astratti e nascondimento di informazioni (forse non tanto quanto i Java).
sepp2k,

7

Trovo la programmazione funzionale estremamente utile nella gestione della complessità. Si tende a pensare alla complessità in un modo diverso, definendolo come funzioni che agiscono su dati immutabili a diversi livelli piuttosto che incapsulamento in senso OOP.

Ad esempio, di recente ho scritto un gioco in Clojure e l'intero stato del gioco è stato definito in un'unica struttura di dati immutabile:

(def starting-game-state {:map ....
                          :player ....
                          :weather ....
                          :other-stuff ....}

E il loop di gioco principale potrebbe essere definito come l'applicazione di alcune funzioni pure allo stato del gioco in un loop:

 (loop [initial-state starting-game-state]
   (let [user-input (get-user-input)
         game-state (update-game initial-state user-input)]
     (draw-screen game-state)
     (if-not (game-ended? game-state) (recur game-state))))

La funzione chiave chiamata è update-game, che esegue un passaggio di simulazione dato uno stato di gioco precedente e alcuni input dell'utente e restituisce il nuovo stato di gioco.

Dov'è la complessità? A mio avviso è stato gestito abbastanza bene:

  • Certamente la funzione di aggiornamento del gioco fa molto lavoro, ma è essa stessa costruita componendo altre funzioni, quindi in realtà è piuttosto semplice. Una volta scesi di alcuni livelli, le funzioni sono ancora piuttosto semplici, facendo qualcosa come "aggiungere un oggetto a una tessera mappa".
  • Certamente lo stato del gioco è una grande struttura di dati. Ma ancora una volta, è solo costruito componendo strutture di dati di livello inferiore. Inoltre sono "dati puri" piuttosto che avere qualsiasi metodo incorporato o definizione di classe richiesta (se lo desideri, puoi considerarlo come un oggetto JSON immutabile molto efficiente), quindi la piastra di cottura è molto ridotta.

OOP può anche gestire la complessità attraverso l'incapsulamento, ma se si confronta questo con OOP, il funzionale presenta alcuni vantaggi molto grandi:

  • La struttura dei dati sullo stato del gioco è immutabile, quindi molta elaborazione può essere facilmente eseguita in parallelo. Ad esempio, è perfettamente sicuro avere un rendering che richiama lo schermo in un thread diverso dalla logica del gioco: non possono influenzarsi a vicenda o vedere uno stato incoerente. Ciò è sorprendentemente difficile con un grande grafico a oggetti mutabili ......
  • Puoi scattare un'istantanea dello stato del gioco in qualsiasi momento. I replay sono banali (grazie alle strutture di dati persistenti di Clojure, le copie occupano pochissima memoria poiché la maggior parte dei dati è condivisa). Puoi anche eseguire update-game per "prevedere il futuro" per aiutare l'IA a valutare diverse mosse, ad esempio.
  • Da nessuna parte ho dovuto fare qualche compromesso difficile per adattarmi al paradigma OOP, come definire una rigida erirarchia di classe. In questo senso, la struttura dei dati funzionali si comporta più come un sistema flessibile basato su prototipo.

Infine, per le persone che sono interessate a ulteriori approfondimenti su come gestire la complessità nei linguaggi funzionali rispetto a quelli OOP, consiglio vivamente il video del discorso di Rich Hickey, Simple Made Easy (filmato alla conferenza sulla tecnologia Strange Loop )


2
Penso che un gioco sia uno dei peggiori esempi possibili per dimostrare i presunti "benefici" dell'immutabilità forzata. Le cose si muovono costantemente in un gioco, il che significa che devi ricostruire il tuo stato di gioco in ogni momento. E se tutto è immutabile, ciò significa che non devi solo ricostruire lo stato del gioco, ma tutto nel grafico che contiene un riferimento ad esso, o che contiene un riferimento a quello, e così via in modo ricorsivo fino a quando non ricicli l'intero programma a 30+ FPS, con tonnellate di churn GC per l'avvio! Non c'è modo di ottenere buone prestazioni da questo ...
Mason Wheeler,

7
Ovviamente i giochi sono difficili con l'immutabilità - ecco perché l'ho scelto per dimostrare che può ancora funzionare! Tuttavia rimarrai sorpreso da ciò che le strutture di dati persistenti possono fare - la maggior parte dello stato del gioco non ha bisogno di essere ricostruito, solo le cose che cambiano. E certo c'è un certo sovraccarico, ma è solo un piccolo fattore costante. Dammi un numero sufficiente di core e
batterò il tuo

3
@Mason Wheeler: In realtà, è possibile ottenere prestazioni praticamente uguali (buone come con la mutazione) con oggetti immutabili, senza molto GC. Il trucco di Clojure sta usando strutture di dati persistenti : sono immutabili al programmatore, ma in realtà mutabili sotto il cofano. Il meglio di entrambi i mondi.
Joonas Pulakka,

4
@quant_dev Più core sono più economici dei core migliori ... escapistmagazine.com/news/view/…
deworde

6
@quant_dev - non è una scusa, è un fatto matematico e architettonico che è meglio avere un sovraccarico costante se riesci a rimediare ridimensionando le prestazioni quasi linearmente con il numero di core. Il motivo per cui i linguaggi funzionali alla fine offriranno prestazioni superiori è che siamo arrivati ​​alla fine della linea per le prestazioni single core, e in futuro si tratterà di concorrenza e parallelismo. gli approcci funzionali (e l'immutabilità in particolare) sono importanti per far funzionare questo.
Mikera,

3

"La programmazione orientata agli oggetti fornisce un livello di astrazione che si applica contemporaneamente agli algoritmi e ai dati, una sorta di astrazione che la decomposizione funzionale da sola non ha fornito."

La decomposizione funzionale da sola non è sufficiente per creare alcun tipo di algoritmo o programma: è necessario rappresentare anche i dati. Penso che la frase sopra presupponga implicitamente (o almeno si possa intendere come) che i "dati" nel caso funzionale siano del tipo più rudimentale: solo elenchi di simboli e nient'altro. Programmare in un tale linguaggio ovviamente non è molto conveniente. Tuttavia, molti, specialmente i linguaggi nuovi e moderni, funzionali (o multiparadigm), come Clojure, offrono ricche strutture di dati: non solo elenchi, ma anche stringhe, vettori, mappe e set, record, strutture - e oggetti! - con metadati e polimorfismo.

L'enorme successo pratico delle astrazioni OO non può essere contestato. Ma è l'ultima parola? Come hai scritto, i problemi di concorrenza sono già il problema principale e la classica OO non ha alcuna idea di concorrenza. Di conseguenza, le soluzioni OO de facto per gestire la concorrenza sono solo nastro adesivo sovrapposto: funziona, ma è facile da rovinare, prende una considerevole quantità di risorse cerebrali dal compito essenziale a portata di mano e non si adatta bene. Forse è possibile prendere il meglio di molti mondi. Questo è ciò che perseguono le moderne lingue multiparadigm.


1
Ho sentito la frase "OO nel grande, FP nel piccolo" da qualche parte - penso che Michael Feathers l'abbia citata. Ciò significa che FP può essere utile per parti particolari di un grande programma, ma in generale dovrebbe essere OO.
dan

Inoltre, invece di utilizzare Clojure per tutto, anche le cose che sono espresse in modo più pulito nella sintassi OO più tradizionale, che ne dici di usare Clojure per i bit di elaborazione dei dati in cui è più pulito e usare Java o qualche altro linguaggio OO per gli altri bit. Programmazione Polyglot anziché programmazione multiparadigm con la stessa lingua per tutte le parti del programma. (Un po 'come il modo in cui la maggior parte delle applicazioni Web utilizza SQL e OO per diversi livelli.)?
dan

@dan: utilizzare qualsiasi strumento adatto al lavoro migliore. Nella programmazione poliglotta, il fattore cruciale è la comoda comunicazione tra le lingue e Clojure e Java difficilmente potrebbero giocare meglio insieme . Credo che la maggior parte dei programmi Clojure sostanziali utilizzino almeno alcune parti delle librerie Java standard di JDK qua e là.
Joonas Pulakka,

2

Lo stato mutevole è la radice della maggior parte delle complessità e dei problemi relativi alla programmazione e alla progettazione di software / sistema.

OO abbraccia lo stato mutevole. FP detesta lo stato mutevole.

Sia OO che FP hanno i loro usi e punti di forza. Scegliere saggiamente. E ricorda il detto: "Le chiusure sono oggetti da poveri. Gli oggetti sono la chiusura di poveri".


3
Non sono sicuro che la tua affermazione iniziale sia vera. La radice della "maggior parte" delle complessità? Nella programmazione che ho fatto o visto, il problema non è tanto lo stato mutevole quanto una mancanza di astrazione e una sovrabbondanza di dettagli attraverso il codice.
dan

1
@Dan: interessante. Ho visto molto il contrario, in realtà: i problemi derivano da un uso eccessivo dell'astrazione, rendendo difficile sia capire che, se necessario, fissare i dettagli di ciò che sta realmente accadendo.
Mason Wheeler,

1

La programmazione funzionale può avere oggetti, ma quegli oggetti tendono ad essere immutabili. Le funzioni pure (funzioni senza effetti collaterali) operano quindi su tali strutture dati. È possibile creare oggetti immutabili in linguaggi di programmazione orientati agli oggetti, ma non sono stati progettati per farlo e non è così che tendono ad essere utilizzati. Questo rende difficile ragionare su programmi orientati agli oggetti.

Facciamo un esempio molto semplice. Supponiamo che Oracle abbia deciso che Java Strings dovrebbe avere un metodo inverso e che tu abbia scritto il seguente codice.

String x = "abc";
StringBuffer y = new StringBuffer(x);
y.reverse();
x.reverse();
x.toString().equals(y.toString());

a cosa valuta l'ultima riga? È necessaria una conoscenza speciale della classe String per sapere che ciò valuterà falso.

E se avessi creato la mia classe WuHoString

String x = "abc";
WuHoString y = new WuHoString(x);
y.reverse();
x.reverse();
x.toString().equals(y.toString())

È impossibile sapere a cosa valuta l'ultima riga.

In uno stile di programmazione funzionale sarebbe scritto più come segue:

String x;
equals(toString(reverse(x)), toString(reverse(WuHoString(x))))

e dovrebbe essere vero.

Se 1 funzione in una delle classi più elementari è così difficile da ragionare, allora ci si chiede se l'introduzione di questa idea di oggetti mutabili abbia aumentato o diminuito la complessità.

Ovviamente ci sono tutti i tipi di definizioni di ciò che costituisce orientato agli oggetti e cosa significa essere funzionali e cosa significa avere entrambi. Per me puoi avere uno "stile di programmazione funzionale" in lingua che non ha cose come le funzioni di prima classe ma altri linguaggi sono fatti per questo.


3
È un po 'divertente che tu dica che i linguaggi OO non sono creati per oggetti immutabili e quindi in seguito usi un esempio con le stringhe (che sono immutabili nella maggior parte dei linguaggi OO incluso Java). Inoltre, dovrei sottolineare che esistono linguaggi OO (o piuttosto multi-paradigma) progettati con un'enfasi su oggetti immutabili (Scala per esempio).
sepp2k,

@ sepp2k: abituati. I sostenitori di FP stanno sempre lanciando esempi bizzarri e inventati che non hanno nulla a che fare con la codifica del mondo reale. È l'unico modo per far apparire bene i concetti chiave di FP come l'immutabilità forzata.
Mason Wheeler,

1
@ Mason: eh? Il modo migliore per rendere l'immutabilità più bello sarebbe quello di dire "Java (e C #, python, ecc.) Usa stringhe immutabili e funziona alla grande"?
sepp2k,

1
@ sepp2k: se le stringhe immutabili funzionano così bene, perché le classi di stile StringBuilder / StringBuffer continuano ad apparire ovunque? È solo un altro esempio di inversione di astrazione che ti ostacola.
Mason Wheeler,

2
Molti linguaggi orientati agli oggetti ti consentono di creare oggetti immutabili. Ma il concetto di legare i metodi alla classe lo scoraggia davvero dal mio punto di vista. L'esempio String non è in realtà un esempio inventato. Ogni volta che chiamo qualsiasi mehtod in java, sto correndo la possibilità che i miei parametri vengano mutati all'interno di quella funzione.
WuHoUnited,

0

Penso che nella maggior parte dei casi l'astrazione OOP classica non copra la complessità della concorrenza. Pertanto OOP (con il suo significato originale) non esclude FP, ed è per questo che vediamo cose come Scala.


0

La risposta dipende dalla lingua. I Lisps, ad esempio, hanno davvero il giusto che il codice sia un dato: gli algoritmi che scrivi sono in realtà solo elenchi di Lisp! I dati vengono archiviati nello stesso modo in cui si scrive il programma. Questa astrazione è allo stesso tempo più semplice e approfondita rispetto a OOP e ti consente di fare cose davvero ordinate (controlla le macro).

Haskell (e un linguaggio simile, immagino) hanno una risposta completamente diversa: tipi di dati algebrici. Un tipo di dati algebrico è come una Cstruttura, ma con più opzioni. Questi tipi di dati forniscono l'astrazione necessaria per modellare i dati; le funzioni forniscono l'astrazione necessaria per modellare gli algoritmi. Le classi di tipi e altre funzionalità avanzate forniscono un livello di astrazione ancora più elevato su entrambi.

Ad esempio, sto lavorando su un linguaggio di programmazione chiamato TPL per divertimento. I tipi di dati algebrici lo rendono davvero facile per rappresentare i valori:

data TPLValue = Null
              | Number Integer
              | String String
              | List [TPLValue]
              | Function [TPLValue] TPLValue
              -- There's more in the real code...

Ciò che dice - in modo molto visivo - è che un valore TPL (qualsiasi valore nella mia lingua) può essere un Nullo un Numbercon un Integervalore o anche un Functioncon un elenco di valori (i parametri) e un valore finale (il corpo ).

Successivamente posso usare le classi di tipi per codificare alcuni comportamenti comuni. Ad esempio, potrei fare TPLValueun'istanza e Showciò significa che può essere convertito in una stringa.

Inoltre, posso utilizzare le mie classi di tipi quando devo specificare il comportamento di determinati tipi (compresi quelli che non ho implementato da solo). Ad esempio, ho una Extractableclasse di tipo che mi consente di scrivere una funzione che accetta a TPLValuee restituisce un valore normale appropriato. Quindi extractpuò convertire a Numberin an Integero a Stringin a Stringpurché Integere Stringsiano istanze di Extractable.

Infine, la logica principale del mio programma è in diverse funzioni come evale apply. Questi sono davvero il nucleo - prendono TPLValues e li trasforma in più TPLValues, oltre a gestire lo stato e gli errori.

Nel complesso, le astrazioni che sto usando nel mio codice Haskell sono in realtà più potenti di quelle che avrei usato in un linguaggio OOP.


Sì, devo amare eval. "Ehi, guardami! Non ho bisogno di scrivere i miei buchi di sicurezza; ho una vulnerabilità di esecuzione del codice arbitraria integrata nel linguaggio di programmazione!" La confluenza dei dati con il codice è la causa principale di una delle due classi di vulnerabilità di sicurezza più popolari di tutti i tempi. Ogni volta che vedi qualcuno essere hackerato a causa di un attacco di iniezione SQL (tra le altre cose) è perché alcuni programmatori là fuori non sanno come separare correttamente i dati dal codice.
Mason Wheeler,

evalnon dipende molto dalla struttura di Lisp: è possibile utilizzarlo evalin lingue come JavaScript e Python. Il vero potere sta nella scrittura di macro, che sono fondamentalmente programmi che agiscono su programmi come dati e producono altri programmi. Ciò rende la lingua molto flessibile e crea facilmente astrazioni potenti.
Tikhon Jelvis,

3
Sì, ho già sentito parlare più volte delle "macro sono fantastiche". Ma non ho mai visto un esempio reale di una macro Lisp che fa qualcosa che 1) è pratico e qualcosa che ti piacerebbe davvero fare nel codice del mondo reale e 2) non può essere realizzato altrettanto facilmente in qualsiasi linguaggio che supporti le funzioni.
Mason Wheeler,

1
@MasonWheeler in corto circuito and. corto circuito or. let. let-rec. cond. defn. Nessuno di questi può essere implementato con funzioni in linguaggi di ordine applicativo. for(comprensione delle liste). dotimes. doto.

1
@MattFenwick: OK, avrei dovuto aggiungere un terzo punto ai miei due precedenti: 3) non è già integrato in alcun linguaggio di programmazione sano. Perché è l'unico macro esempio davvero utile che io abbia mai visto, e quando dici "Ehi guardami, il mio linguaggio è così flessibile che posso implementare il mio corto circuito and!" Sento "hey guardami, la mia lingua è così paralizzata che non arriva nemmeno con un corto circuito ande devo reinventare la ruota per tutto !"
Mason Wheeler,

0

La frase citata non ha più validità, per quanto posso vedere.

I linguaggi OO contemporanei non possono astrarre su tipi il cui tipo non è *, ovvero i tipi di tipo superiore non sono noti. Il loro sistema di tipi non consente di esprimere l'idea di "un contenitore con elementi Int, che consente di mappare una funzione sugli elementi".

Quindi, questa funzione di base come Haskells

fmap :: Functor f => (a -> b) -> f a -> f b 

non può essere scritto facilmente in Java *), ad esempio, almeno non in modo sicuro. Quindi, per ottenere le funzionalità di base, devi scrivere molta piastra di caldaia, perché è necessario

  1. un metodo per applicare una semplice funzione agli elementi di un elenco
  2. un metodo per applicare la stessa semplice funzione agli elementi di un array
  3. un metodo per applicare la stessa semplice funzione ai valori di un hash,
  4. .... impostato
  5. .... albero
  6. ... 10. unit test per lo stesso

Eppure, quei cinque metodi sono fondamentalmente lo stesso codice, dare o prendere alcuni. Al contrario, in Haskell, avrei bisogno di:

  1. Un'istanza di Functor per elenco, matrice, mappa, set e struttura (per lo più predefinita o può essere derivata automaticamente dal compilatore)
  2. la semplice funzione

Si noti che questo non cambierà con Java 8 (solo che si possono applicare le funzioni più facilmente, ma poi, esattamente, il problema sopra si materializzerà. Finché non si hanno nemmeno funzioni di ordine superiore, è molto probabile che nemmeno in grado di capire a cosa servono i tipi di tipo superiore).

Anche le nuove lingue OO come Ceylon non hanno tipi di tipo più elevato. (Ho chiesto a Gavin King di recente, e lui mi ha detto, non era importante in questo momento.) Non so di Kotlin, comunque.

*) Per essere onesti, puoi avere un'interfaccia Functor che ha un metodo fmap. La cosa brutta è che non puoi dire: Ehi, so come implementare fmap per la classe di libreria SuperConcurrentBlockedDoublyLinkedDequeHasMap, caro compilatore, ti preghiamo di accettare che d'ora in poi, tutte le SuperConcurrentBlockedDoublyLinkedDequeHasMaps sono Functors.


FTR: coontrollore dei tipo Ceylon e JavaScript del backend fanno assistenza maggiore mano tipi (e anche più elevati di rango tipi). È considerata una funzionalità "sperimentale". Tuttavia, la nostra comunità ha faticato a trovare applicazioni pratiche per questa funzionalità, quindi è una domanda aperta se questa sarà mai una parte "ufficiale" della lingua. Io mi aspetto che essere supportato da Java back-end ad un certo punto.
Gavin King,

-2

Chiunque avesse mai programmato in dBase avrebbe saputo quanto fossero utili le macro a riga singola per creare codice riutilizzabile. Anche se non ho programmato in Lisp, ho letto da molti altri che giurano di compilare macro temporali. L'idea di iniettare codice nel codice al momento della compilazione viene utilizzata in una forma semplice in ogni programma C con la direttiva "include". Poiché Lisp può farlo con un programma Lisp e poiché Lisp è altamente riflessivo, si ottengono inclusioni molto più flessibili.

Qualsiasi programmatore che prendesse semplicemente una stringa di testo arbitraria dal web e la trasmettesse al loro database non è un programmatore. Allo stesso modo, chiunque consentirebbe ai dati "utente" di diventare automaticamente codice eseguibile è ovviamente stupido. Ciò non significa che consentire ai programmi di manipolare i dati al momento dell'esecuzione e quindi eseguirli come codice sia una cattiva idea. Credo che questa tecnica sarà indispensabile in futuro, che avrà il codice "intelligente" che in realtà scrive la maggior parte dei programmi. L'intero "problema di dati / codice" o no è una questione di sicurezza nella lingua.

Uno dei problemi con la maggior parte delle lingue è che sono stati fatti per una singola persona off line per eseguire alcune funzioni per se stessi. I programmi del mondo reale richiedono che molte persone abbiano accesso in ogni momento e allo stesso tempo da più core e più cluster di computer. La sicurezza dovrebbe far parte della lingua piuttosto che del sistema operativo e in un futuro non troppo lontano lo sarà.


2
Benvenuti ai programmatori. Ti preghiamo di considerare di attenuare la retorica nella tua risposta e di appoggiare alcune delle tue affermazioni con riferimenti esterni.

1
Qualsiasi programmatore che consentirebbe ai dati dell'utente di diventare automaticamente codice eseguibile è ovviamente ignorante . Non stupido. Farlo in questo modo è spesso facile e ovvio, e se non sanno perché è una cattiva idea e che esiste una soluzione migliore, non puoi davvero biasimarli per averlo fatto. (Chiunque continua a farlo dopo che gli è stato insegnato che esiste un modo migliore, tuttavia, è ovviamente stupido.)
Mason Wheeler,
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.