Con estensioni del linguaggio, siamo in grado di creare situazioni in cui f x
must essere valutato più volte:
{-# LANGUAGE GADTs, Rank2Types #-}
module MultiEvG where
data BI where
B :: (Bounded b, Integral b) => b -> BI
foo :: [BI] -> [Integer]
foo xs = let f :: (Integral c, Bounded c) => c -> c
f x = maxBound - x
g :: (forall a. (Integral a, Bounded a) => a) -> BI -> Integer
g m (B y) = toInteger (m + y)
x :: (Integral i) => i
x = 3
in map (g (f x)) xs
Il punto cruciale è quello di avere f x
polimorfico anche come argomento di g
, e dobbiamo creare una situazione in cui il i tipi ai quali è necessario non possono essere previsti (la mia prima coltellata utilizzava uno Either a b
invece di BI
, ma quando si ottimizzava, ciò comportava, naturalmente, solo due valutazioni di f x
al massimo).
Un'espressione polimorfa deve essere valutata almeno una volta per ogni tipo in cui viene utilizzata. Questa è una delle ragioni per la restrizione del monomorfismo. Tuttavia, quando l'intervallo di tipi a cui può essere necessario è limitato, è possibile memorizzare i valori per ciascun tipo e, in alcune circostanze, GHC lo fa (necessita di ottimizzazione, e mi aspetto che il numero di tipi coinvolti non sia troppo grande). Qui lo confrontiamo con quello che è fondamentalmente un elenco non omogeneo, quindi in ogni invocazione di g (f x)
, può essere necessario in un tipo arbitrario che soddisfi i vincoli, quindi il calcolo non può essere revocato all'esterno dello map
(tecnicamente, il compilatore potrebbe ancora creare una cache dei valori ad ogni tipo usato, quindi sarebbe valutato solo una volta per tipo, ma GHC non lo fa, con ogni probabilità non ne varrebbe la pena).
- Le espressioni monomorfologiche devono essere valutate solo una volta, possono essere condivise. Se sono all'altezza dell'implementazione; per purezza, non cambia la semantica del programma. Se l'espressione è legata a un nome, in pratica puoi fare affidamento sul fatto che sia condivisa, dal momento che è facile e ovviamente ciò che il programmatore vuole. Se non è legato a un nome, è una questione di ottimizzazione. Con il generatore bytecode o senza ottimizzazioni, l'espressione sarà spesso valutata ripetutamente, ma con l'ottimizzazione ripetuta la valutazione indicherebbe un errore del compilatore.
- Le espressioni polimorfiche devono essere valutate almeno una volta per ogni tipo in cui vengono utilizzate, ma con ottimizzazioni, quando GHC può vedere che può essere utilizzato più volte allo stesso tipo, sarà (di solito) ancora condiviso per quello digita durante un calcolo più grande.
Bottom line: compilare sempre con ottimizzazioni, aiutare il compilatore associando le espressioni che si desidera condividere con un nome e assegnare firme di tipo monomorfico laddove possibile.
fonte
2012-02-25 02:05:12
Avete un esempio in cui 'f x' è in corso di valutazione più di una volta? – hammar
@hammar: ho aggiunto un esempio del genere. –
@Grzegorz Dovresti menzionare ciò che vale solo se non lo ottimizzi. Se permetti tipi di rango più alti, posso darti un esempio in cui l'ottimizzazione non può eliminare la valutazione ripetuta. Interessato? –