2015-06-28 12 views
18

Se ho un tipo come data T = T Int String e una funzione in questo modo:GHC crea una nuova copia di un oggetto quando lo decostruisce e lo ricostruisce?

identity :: T -> T 
identity (T a b) = T a b 

Dopo la decostruzione nel pattern matching, fa GHC creare un nuovo oggetto T che contiene riferimenti alla stessa Int e String? O restituisce esattamente lo stesso oggetto (con lo stesso indirizzo di memoria) che ha ricevuto? Capisco che sono semanticamente equivalenti, sono solo curioso.

risposta

15

In generale GHC assegnerà un nuovo valore anziché riutilizzare l'argomento in quella situazione. In questo caso specifico potresti scrivere qualcosa come

f :: T -> T 
f [email protected](T x y) = t 

per riutilizzare esplicitamente l'argomento. Purtroppo, in uno dei casi in cui ci si vuole veramente questo -

fmap :: (a -> b) -> Either e a -> Either e b 
fmap f (Right x) = Right (f x) 
fmap f (Left x) = Left x 

- GHC allocherà un nuovo valore Left, e non si può semplicemente riutilizzare l'argomento, perché il risultato ha un tipo diverso . Per quanto ne so non c'è modo di dire a GHC di riutilizzare l'argomento in questo caso diverso da unsafeCoerce.

+0

Vedo. Questo è un po 'deludente. In realtà stavo pensando al caso in cui si ha una struttura di dati persistente nidificata (come un albero binario) e si voleva inserire un valore che era già presente. Avresti bisogno di una funzione di supporto come "insert :: (Ord a) => a -> Tree a -> Maybe (Tree a)" che restituiva un Nothing se l'elemento era già presente per evitare di ricostruire il percorso del valore . –

+4

@Ramith Che distrugge la pigrizia purtroppo; tutte le chiamate sul percorso fino al nodo foglia devono attendere la restituzione della chiamata definitiva prima di sapere se restituiscono un riferimento a un nodo costruttore esistente o ne allocano uno nuovo, il che significa forzare la radice il passo costringe tutto. Quindi, se mai vuoi scegliere tra queste proprietà (la pigrizia o la ricostruzione di alberi identici), allora deve essere programmato esplicitamente (come tramite l'helper Maybe (Tree a)) piuttosto che il comportamento implicito del compilatore. – Ben

+1

In questo caso, 'unsafeCoerce' è sicuro? Voglio dire "O una b" e "O una c" hanno lo stesso argomento di "Sinistra", quindi sembra plausibile che il costruttore di 'Sinistra' possa essere forzato senza problemi (e probabilmente lo è), tuttavia esiste una * garanzia * su questo? – Bakuriu

15

È possibile testare questo facilmente con -ddump-simpl. Un'allocazione di un valore di un ADT verrà visualizzata come un'applicazione di un costruttore di dati.

In questo caso, GHC La Spot che si può riutilizzare il valore, e anche che non è necessario eseguire il pattern matching:

module I where 

data T = T Int String 

identity :: T -> T 
identity (T a b) = T a b 

-

[email protected]:/tmp$ ghc -ddump-simpl I 
[1 of 1] Compiling I    (I.hs, I.o) 

==================== Tidy Core ==================== 
Result size of Tidy Core = {terms: 3, types: 3, coercions: 0} 

I.identity :: I.T -> I.T 
[GblId, Arity=1, Caf=NoCafRefs, Str=DmdType] 
I.identity = \ (ds_dHN :: I.T) -> ds_dHN 

Questo accade anche senza ottimizzazioni abilitate e funziona anche su ADT con più di un costruttore.

+0

La corrispondenza del caso influisce sulla severità. La specifica originale per "identità" richiede che sia rigorosa, quindi GHC mantiene tale rigore. – MathematicalOrchid

Problemi correlati