Stavo leggendo il documento scritto da Simon Peyton Jones, et al. chiamato “Playing by the Rules: Rewriting as a practical optimization technique in GHC”. Nella seconda sezione, cioè “ L'idea di base ” scrivono:Riscrittura come tecnica di ottimizzazione pratica in GHC: è davvero necessaria?
consideri la funzione
map
familiare, che applica una funzione per ogni elemento di una lista. Scritto in Haskell,map
assomiglia a questo:
map f [] = []
map f (x:xs) = f x : map f xs
Supponiamo ora che il compilatore incontra la seguente chiamata di
map
:
map f (map g xs)
Sappiamo che questa espressione è equivalente a
map (f . g) xs
(dove “. ” è la composizione della funzione) e sappiamo che quest'ultima espressione è più efficiente della prima perché non esiste una lista intermedia. Ma il compilatore non ha tale conoscenza.
Una possibile controreplica è che il compilatore dovrebbe essere più intelligente --- ma il programmatore saprà sempre cose che il compilatore non riesce a capire. Un altro suggerimento è questo: consentire al programmatore di comunicare tale conoscenza direttamente al compilatore. Questa è la direzione che esploriamo qui.
La mia domanda è, perché non possiamo rendere il compilatore più intelligente? Gli autori dicono che “ ma il programmatore saprà sempre cose che il compilatore non riesce a capire ”. Tuttavia, questo non è una risposta valida perché il compilatore può infatti capire che map f (map g xs)
è equivalente a map (f . g) xs
, ed ecco come:
map f (map g xs)
map g xs
unifica conmap f [] = []
.Quindi
map g [] = []
.map f (map g []) = map f []
.map f []
unifica conmap f [] = []
.Quindi
map f (map g []) = []
.map g xs
unifica conmap f (x:xs) = f x : map f xs
.Quindi
map g (x:xs) = g x : map g xs
.map f (map g (x:xs)) = map f (g x : map g xs)
.map f (g x : map g xs)
unifica conmap f (x:xs) = f x : map f xs
.Quindi
map f (map g (x:xs)) = f (g x) : map f (map g xs)
.
Quindi ora abbiamo delle regole:
map f (map g []) = []
map f (map g (x:xs)) = f (g x) : map f (map g xs)
Come si può vedere f (g x)
è solo (f . g)
e map f (map g xs)
viene chiamato in modo ricorsivo. Questa è esattamente la definizione di map (f . g) xs
. L'algoritmo per questa conversione automatica sembra essere piuttosto semplice. Quindi, perché non implementare questo invece di regole di riscrittura?
Sarebbe utile se potesse mostrarmi un esempio concreto di come le regole di riscrittura sono migliori della inlining. –
Hai visto il documento collegato su Stream Fusion? –