Newsgroup: it.comp.lang.c++
Titolo: multiparadigmatico

ho letto qualche volta questo termine riferito al C++.
Ora, anche se in qualche maniera riuscivo a desumere il senso 
generale delle frasi dal contesto, in effetti non ho una idea 
chiara di cosa tale termine significhi.
Chi me lo spiega in modo semplice (se possibile) e magari mi fa 
un esempio pratico di cosa significhi e di come sarebbe il 
linguaggio se NON fosse multiparadigmatico (oppure di come si 
possa usare in modo NON multietcetc)-
Ciao
Soviet_Mario

Soviet_Mario ha scritto:

Molto semplicemente:
C++ permette sia la programmazione ad oggetti che quella funzionale.
Ossia ti permette di definire classi da cui istanzi oggetti, oppure di 
definire delle funzioni senza che facciano necessariamente parte di una 
classe/oggetto.
Entrambe le tipologie di programmazione sono "paradigmi" (ossia modi di 
vedere o modellizzare qualcosa).
quindi il linguaggio C++ e' "multiparadigma" o "multiparadigmatico", che 
dir si voglia.
Un esempio di linguaggio mono-paradigma:
il linguaggio C, solo funzionale, non permette la programmazione ad 
oggetti (non esistono le classi, l'ereditarieta' ed il polimorfismo).
Un'altro, opposto:
Smalltalk, che e' solo ad oggetti.
-- 
Roal Zanazzi

 
e anche Java, per citarne uno piu' conosciuto.
-- 
Roal Zanazzi

 Chissa' cosa pensavo quando ho risposto...
cmq avevo capito giusto, per una sorta di coincidenza
1) ho pensato alle funzioni (che procedure sono, in C++)
2) non conosco il tipo di linguaggio che tu chiami "funzionale"
Cmq sia, grazie delle dritte. Si, in effetti era un parolone 
grosso grosso ma il concetto di fondo molto semplice
Grazie
ciao
Soviet_Mario


 2) non conosco il tipo di linguaggio che tu chiami "funzionale"
Scheme. Ma anche i vari linguaggi della famiglia del lisp.

AnalogFile ha scritto:

ma, "filosoficamente", in cosa consisterebbe questo genere di 
programmazione funzionale ?
Esistono perifrasi (come data hiding, data abstraction, 
ereditarietà, etc etc) che possano fare intuire il senso senza 
scendere troppo nel dettaglio ?
Mi rendo conto di essere OT ....
Tornando parzialmente IT, assumendo di aver capito questo terzo 
paradigma, esiste un qualche modo di emularlo anche in parte in 
C++ ? E che utilità avrebbe ? Voglio dire, i ling. funzionali a 
che genere di problematiche idealmente rispondono ?
Ciao
Soviet

 2) non conosco il tipo di linguaggio che tu chiami "funzionale"
Un linguaggio è funzionale se e solo se ogni algoritmo descritto da esso 
è composto esclusivamente da simboli di valore costante.
Per es. una funzione f:S->S è un sottoinsieme di S^2, cioè associa ad 
ogni elemento di S un *unico* elemento di S.
Il valore di f(2), una volta che è stato definito, è sempre lo stesso.
Se f(2) = 3 e f(4) = 1, puoi sempre sostituire
f(2)+f(4)
con 4, indipendemente dal contesto o dalla "storia" dell'esecuzione.
In un linguaggio non funzionale questo non è più vero. Pensa alla 
semplice funzione rand(). Il modello della sostituzione usato in 
matematica non è più valido, infatti non puoi scrivere che rand()+rand() 
= 2*rand(). In un linguaggio funzionale puoi.
Questo è il motivo principale per il quale i programmi che seguono il 
paradigma funzionale contengono molti meno errori. E' inoltre più facile 
verificare la correttezza di un programma utilizzando una semplice 
algebra (pensa al lamda calcolo).
Kiuhnm

Roal Zanazzi ha scritto:

funzionale o procedurale .... io direi + procedurale ... funzionale non 
è l'approccio dei linguaggi lisp like ?
ciao
PP

 Scheme. Ma anche i vari linguaggi della famiglia del lisp.
No, è multi-paradigma: è funzionale e imperativo.
Grazie all'assoluta libertà e dinamicità di cui gode, è possibile 
programmare in qualsiasi paradigma derivato (OOP, ecc...).
Per es.
(define (make-withdraw balance)
   (lambda (amount)
     (if (>= balance amount)
         (begin (set! balance (- balance amount))
                balance)
         "Insufficient funds")))
(define W1 (make-withdraw 100))
(define W2 (make-withdraw 100))
crea due oggetti W1 e W2. Nota che ogni W_i ha la sua variabile locale 
'balance'. Nota anche che W1 e W2 sono due funzioni!
Un possibile uso (in questo caso, interattivo) è:
(W1 50)
50
(W2 70)
30
(W2 10)
20
ecc...
Prova a farlo con un linguaggio funzionale...
Se proprio non puoi fare a meno del "for", ti basta aggiungerlo 
scrivendo qualche riga di codice.
In pochi giorni puoi creare una libreria che supporti il paradigma ad 
oggetti in modo completamente trasparente (sarà indistinguibile da 
quello "nativo"). Ogni singolo aspetto sarà dinamico.
Purtroppo, per padroneggiare un simile linguaggio serve una mente 
piuttosto analitica e abbastanza orientata alla matematica.
Kiuhnm

 2) non conosco il tipo di linguaggio che tu chiami "funzionale"
Puoi fare della programmazione funzionale anche in C++. Basta non usare 
mai gli operatori di assegnamento, se non per inizializzare variabili 
(in tal caso non e' un assegnamento).
Ecco un esempio di programma funzionale:
#include <iostream>
long fattoriale(long n) {
    if (n <= 1) return 1;
    return n * fattoriale(n -1);
}
int main() {
   std::cout << fattoriale(8);
}
-- 
     Carlo Milanesi
 
http://digilander.libero.it/carlmila

 }
mah .... temo di non aver capito. Io questa la chiamavo 
ricorsione. In che aspetto significhi anche "funzionale", non so.
Ora mi armo di coraggio e cerco di masticare la risposta di 
Kiunm .... tango non gliela fo, o sacce.
ciao
Soviet


 Kiuhnm

 che aspetto significhi anche "funzionale", non so.
"Funzionale" significa che le procedure hanno le stesse proprietą delle 
funzioni matematiche, e quindi sono prive di effetti collaterali.
Inoltre, siccome non sono consentiti assegnamenti, in quanto sono 
istruzioni che hanno effetti collaterali (per definizione), non sono 
consentiti nemmeno i cicli (while e for). Pertanto, al posto dei cicli 
si deve usare la ricorsione, in cui ogni iterazione e' sostituita da una 
chiamata ricorsiva.


 linguaggi funzionali, vero ?
Non ci sono problemi:
r_1 = FIRST_VALUE
r_n = rand(r_{n-1})
dove r_1, r_2, ... è una sequenza di valori pseudo-casuali.
Devi cioè passare alla rand o il valore iniziale o quello che ti ha 
restituito in precedenza.
Kiuhnm

 ricorsione nel compilato, vero ?
Esatto. Se, come spesso succede, la chiamata ricorsiva e' l'ultima della 
funzione, si parla di tail-recursion. Le funzioni tail-recursive possono 
essere tradotte automaticamente in routine iterative che non fanno un 
uso smodato dello stack.
Tuttavia, tutto cio' a poco a che fare con il C++, in quanto non credo 
proprio che qualcuno faccia seriamente della programmazione funzionale 
in C++, a meno che faccia della metaprogrammazione di template. 
Quest'ultima e' infatti proprio programmazione funzionale.
Per saperne di piu':
http://en.wikipedia.org/wiki/Functional_programming
http://en.wikipedia.org/wiki/Template_metaprogramming


Soviet_Mario wrote:
 > Carlo Milanesi ha scritto:
 >
 >> Inoltre, siccome non sono consentiti assegnamenti, in quanto sono 
istruzioni che hanno effetti collaterali (per definizione),
 >
 >
 > ecco, qui invece ammetto che avevo interpretato in forma soft il 
postulato, nel senso di : le funzioni non devono mai modificare i 
parametri ricevuti (o, che so, usare puntatori vaganti per modificare 
oggetti esterni a sé stesse). Davo invece per scontato che potesseo 
modificare parametri "local", privati allo scope di funzione. Non riesco 
a capire bene a chi possa fregare se modificano variabili private, che 
compaiono dopo che gli argomenti, intoccabili, sono stati copiati sullo 
stack, e che svaniscono nel nulla quando la funzione ritorna.
 > Cui prodest che non possano farsi assegnazioni in senso così forte ? 
Come si memorizza un risultato da qualche parte senza possibilità di 
assegnarlo ? Mi sconcerta questa clausola
Infatti, in tutti i linguaggi procedurali tali vincoli non ci sono.
Tutt'al più, in alcuni linguaggi si considera "funzione" solo una 
procedura che prende i parametri per valore e non modifica variabili 
esterne ad essa.
Anche i linguaggi piu' usati come linguaggi funzionali come LISP, Scheme 
e (in un certo senso) Prolog, oltre a favorire il paradigma di 
programmazione funzionale, hanno istruzioni con effetti collaterali, 
che, se usate, rendono non completamente funzionale il programma.
In pratica, mi pare che la programmazione funzionale pura e' usata 
solamente in esercizi accademici, o in contesti applicativi speciali, 
cioe' con linguaggi domain-specific.
 >> non sono consentiti nemmeno i cicli (while e for). Pertanto, al 
posto dei cicli si deve usare la ricorsione, in cui ogni iterazione e' 
sostituita da una chiamata ricorsiva.
 >
 >
 > Miii, mi vengono i brividi. Immagino che sia una clausola che vale 
solo nel codice, e che il compilatore in realtà traduca in iterazioni in 
modo trasparente tutto quanto iterabile, e che non implementi seriamente 
la ricorsione nel compilato, vero ?
Esatto. Se, come spesso succede, la chiamata ricorsiva e' l'ultima della 
funzione, si parla di tail-recursion. Le funzioni tail-recursive possono 
essere tradotte automaticamente in routine iterative che non fanno un 
uso smodato dello stack.
Tuttavia, tutto cio' ha poco a che fare con il C++, in quanto non credo 
proprio che qualcuno faccia seriamente della programmazione funzionale 
in C++, a meno che faccia della metaprogrammazione di template. 
Quest'ultima e' infatti proprio programmazione funzionale.
Per saperne di piu':
http://en.wikipedia.org/wiki/Functional_programming
http://en.wikipedia.org/wiki/Template_metaprogramming
-- 
     Carlo Milanesi
 
http://digilander.libero.it/carlmila

 Prova a farlo con un linguaggio funzionale...
hmmm. Prima dovrei ripassare un pochino ;-)
Ritornando ai linguaggi puramente funzionali, se escludiamo i linguaggi 
della famiglia del lisp a causa di set! direi che ci restano solo le 
famiglie di ML (il più conosciuto credo sia Caml) e di Miranda (il più 
conosciuto credo sia Haskel).
O anche in questi è stato introdotto un qualche tipo di supporto diretto 
agli stati che li rende multiparadigma (i monad di Haskel non direi che 
lo rendono imperativo, pur supportando, in un certo senso, gli stati).

 qualsiasi macchina di Turing.
Probabilmente è corretto dire che i template del C++ costituiscono una 
specie di linguaggio funzionale. Ma questa definizione mi risulta in 
realtà piuttosto stretta.
A questa stregua anche XSLT sarebbe un linguaggio funzionale.
Ma personalmente fatico a considerare un linguaggio funzionale come tale 
se non prevede una qualche forma di notazione lambda e di supporto per 
funzioni di ordine superiore.

 cioe' con linguaggi domain-specific.
No.
Per esempio Linspire usa Haskel (linguaggio funzionale) per la 
programmazione di sistema:
http://urchin.earth.li/pipermail/debian-haskell/2006-May/000169.html

 Soviet_Perplexed
Ni.
In generale la ricorsione avviene. Ma nel caso di "tail recursion" vale 
l'ottimizzazione che non viene generato un nuovo stack frame per ogni 
chiamata.
Nota però che in un linguaggio funzionale, in generale, gli stack frames 
  sono allocati nello heap e non nello stack (e vengono liberati dal 
garbage collector). Questo perché in realtà le funzioni sono oggetti di 
prima classe e possono essere passati come parametri e ritornati come 
valori. Una funzione ritornata come valore verrà richiamata in un 
secondo tempo e potrà accedere a valori presenti nello stack frame della 
funzione in cui è stata definita, anche se questa è terminata. Pertanto 
in realtà una funzione è una closure delle variabili (o meglio dei 
parametri) ad essa visibili.
Infatti è possibile definire in maniera leggermente diversa quanto sopra:
(define foo
   (lambda (a)
     ( (lambda (b)
          (sum a b))
       3)))
ovvero in pseudo-c:
int foo( int a ) {
   int sum( int b ) {
     return a+b;
   }
   return sum(3);
}
dove, se noti, la funzione interna può accedere ai parametri di quella 
esterna.
Ora immagina di definire un generatore di funzioni. Ovvero una funzione 
che ti ritorna una funzione:
(define make_incrementer
    (lambda (increment)
      (lambda (x) (sum x increment))))
Ovvero in pseudo C:
typdef int (*FUN)(int)
FUN make_incrementer( int increment )
{
    int the_incrementer( int x )
    {
       return x + increment;
    }
    return the_incrementer;
}
L'idea è che tu puoi generare una funzione che prende un valore e lo 
incrementa di un tot. Per esempio:
FUN plus4 = make_incrementer(4);
FUN plus7 = make_incrementer(7);
plus4(2); // ritorna 6
plus7(2); // ritorna 9
Ovvero:
(define plus4 (make_incrementer 4))
(define plus7 (make_incrementer 7))
(plus4 2)
(plus7 2)
Naturalmente questo in C non è possibile dato che le funzioni non sono 
oggetti di prima classe. In C++ la cosa è parzialmente possibile. 
Ovvero: non è possibile se vuoi una funzione nel vero senso del termine, 
ma è possibile se ti accontenti di una funzione nel senso generale che 
comprende i "function objects".
L'implementazione in C++ di un codice più o meno equivalente al seguente:
(define make_incrementer
    (lambda (increment)
      (lambda (x) (sum x increment))))
(define plus4 (make_incrementer 4))
(define plus7 (make_incrementer 7))
(plus4 2)
(plus7 2)
è qualche cosa del genere:
template <typename T>
   class Incrementer {
     private:
       const T increment;
     public:
       explicit Incrementer( const T& i ):increment(i) {}
       T operator()( const T& x ) const
       {
          return x + increment;
       }
   }
template <typename T>
   Incrementer<T> make_incrementer( const T& i )
   {
      return Incrementer<T>(i);
   }
Incrementer<int> plus4 = make_incrementer(4);
Incrementer<int> plus7 = make_incrementer(7);
plus4(2);
plus7(2);

 lo rendono imperativo, pur supportando, in un certo senso, gli stati).
Direi che restano comunque funzionali. Le monadi costituiscono solo uno 
strappo al... paradigma. Tra l'altro trovo che siano a dir poco contorte.
Kiuhnm

 dichiarativo, ma non "entro nella polemica". Non  ne ho la statura :)
Dichiarativo? Non direi. Con i template devi scrivere il "modo esatto" 
per ottenere un risultato. Il C++ applica pedissequamente e 
"stupidamente" le definizioni ricorsive indicate.
Non è possibile in alcun modo descrivere una proprietà del risultato 
senza fornire una formula ricorsiva ben precisa per ottenerlo.
Kiuhnm

 funzioni di ordine superiore.
Tecnicamente, basta che sia completo. E' ovvio che questo non basta 
nella pratica.
Pensa se qualcuno ti portasse un programma scritto in questo modo:
(q0, t1)(q1, t1, ->)
(q1, t3)(q1, B, <-)
...
Kiuhnm

 almeno una grossissima azienda di telecomunicazioni...
Se e' usato solo per le telecomunicazioni, allora e' domain-specific.


 Io però il Prolog proprio non lo metterei fra i funzionali eh.
Ho scritto "(in un certo senso) Prolog", in quanto il Prolog, come i 
linguaggi funzionali non consente assegnamenti se non in fase di 
inizializzazione, cioe' ogni variabile da quando assume un valore 
mantiene sempre lo stesso valore, e inoltre non ha variabili globali.
In realta' anche il Prolog ha alcune istruzioni con effetti collaterali, 
come quelle di input/output e le "assert" e "retract".
Il concetto e' che una porzione di programma priva di assert e retract e 
priva di istruzioni di input/output ha una semantica sostanzialmente 
funzionale, cioe' ogni funzione (che in Prolog si chiama predicato) e' 
priva di effetti collaterali.


 priva di effetti collaterali.
Ma è proprio qui il punto. La genesi teorica dei linguaggi funzionali è
il lambda calcolo di Church, il prolog viene dalla teoria dei linguaggi
del prim ordine (poi ci sono anche predicati di ordine superiore in
Prolog, ma amen).
In questo senso IMHO si può dire che hanno la caratteristica di non
avere effetti collaterali (fatte le dovute eccezioni), ma Prolog non è
un linguaggio funzionale.
-- 
blog:  
http://www.akropolix.net/rik0/blogs
 | Uccidete i filosofi,
site:  
http://www.akropolix.net/rik0/
      | tenetevi riso e
forum: 
http://www.akropolix.net/forum/
     | bacchette per voi.

 Se e' usato solo per le telecomunicazioni, allora e' domain-specific.
No. Č usato *da* un azienda di telecomunicazioni, che ci fonda piů o
meno *tutte* le infrastrutture IT.
Poi con lo stesso erlang ci hanno scritto pure mutt e slrn.
Comunicazione anche loro in effetti... :)


 Dichiarativo?
Stiamo parlando di XSLT :)
Io non ci ho lavorato molto (qualche giochino e basta) e in genere viene
definito un approccio dichiarativo.


 Stiamo parlando di XSLT :)
Scusa, stavo lavorando in multi-thread senza sincronizzazione.
Kiuhnm

 priva di effetti collaterali.
La diff. tra funzionale e logico è che nel primo caso dici "come" mentre 
nel secondo "cosa".
Kiuhnm

 Tra un po' passo allo studio di Ruby :)
Ruby è tutto diverso. E per certi aspetti molto più divertente sia di 
Scheme che di C/C++, Java, Python e altri.
Hai già dato un'occhiata a Smalltalk? IMO meglio che ti ci fai una 
infarinatura (non necessario, ovvio, ma è il linguaggio padre dell'OO).

 infarinatura (non necessario, ovvio, ma è il linguaggio padre dell'OO).
La dinamicità di Smalltalk non è già stata raggiunta da Ruby?
Kiuhnm

 La dinamicità di Smalltalk non è già stata raggiunta da Ruby?
Si. Ma ruby e smalltalk sono abbastanza diversi.
Non è una questione di dinamicità ne di paragigma (o forse di paradigma 
un pochino si). Direi che è una questione di 'filosofia'.
Ti consiglio di dare prima un'occhiata a smalltalk. Magari giocandoci 
anche un pochino (e studiandoti il codice delle classi principali, come 
la gerarchia numerica e quella dei contenitori).
Riguardo a Ruby, molti lo stanno imparando per usare Rails. Ma io ti 
consiglio di partire dal linguaggio in se e per se. Puoi scriverci dei 
piccoli programmini (a livello di scripting, per esempio).
Cerca di farti un'idea del linguaggio e della filosofia che ci sta dietro.
Dopo, ma solo dopo, dai anche un'occhiata a Rails. E' molto probabile 
che scoprirai che dietro a ruby c'è molto di più di quello che eri 
riuscito a immaginarti giocandoci.
E IMO ... c'è ancora di più di quello che fa Rails. Ma ci si arriverà 
nei prossimi anni.
Però ... imo Ruby necessita di un pochino di pulizia sintattica.

 Ti consiglio di dare prima un'occhiata a smalltalk. Magari giocandoci 
[..]
Ok, proverò anche smalltalk.
Kiuhnm

 abbastanza significative.
Esatto. Vedi infatti la mia risposta a kiuhnm (che riporto):
 >>> Hai già dato un'occhiata a Smalltalk? IMO meglio che ti ci fai
 >>> una infarinatura (non necessario, ovvio, ma è il linguaggio
 >>> padre dell'OO).
 >>
