2013-07-04 18 views
21

Haskell non ha la notazione a punti per i membri del record. Per ogni membro del record, un compilatore crea una funzione con lo stesso nome con un tipo RecType -> FieldType. Ciò porta a denominare i conflitti. Ci sono modi per ovviare a questo, ad esempio, come posso avere più record con gli stessi nomi di campo?Conflitti di nomi nei record Haskell

+5

Cfr. http://stackoverflow.com/questions/5775068/modeling-domain-data-in-haskell/5777042#5777042 – luqui

+0

http://stackoverflow.com/questions/6922437/ e: http://stackoverflow.com/questions/6677834/ –

+0

Questo problema potrebbe essere risolto Presto presto in GHC. Penso che sia un progetto GSoC: D – jozefg

risposta

14

Un altro modo per evitare questo problema è utilizzare il pacchetto lens. Esso fornisce una funzione template Haskell makeFields, che può essere utilizzato in questo modo:

{-# LANGUAGE FlexibleInstances  #-} 
{-# LANGUAGE FunctionalDependencies #-} 
{-# LANGUAGE MultiParamTypeClasses #-} 
{-# LANGUAGE TemplateHaskell  #-} 
{-# LANGUAGE TypeSynonymInstances #-} 
import   Control.Lens 

data A = A 
    { _aText :: String 
    } 
makeFields ''A -- Creates a lens x for each record accessor with the name _aX 

data B = B 
    { _bText :: Int 
    , _bValue :: Int 
    } 
-- Creates a lens x for each record accessor with the name _bX 
makeFields ''B 

main = do 
    let a = A "hello" 
    let b = B 42 1 

    -- (^.) is a function of lens which accesses a field (text) of some value (a) 
    putStrLn $ "Text of a: " ++ a ^. text 
    putStrLn $ "Text of b: " ++ show (b ^. text) 

Se non si desidera utilizzare TemplateHaskell e l'obiettivo, si può anche fare manualmente quello lente di automatizzare utilizzando TemplateHaskell:

{-# LANGUAGE FlexibleInstances  #-} 
{-# LANGUAGE FunctionalDependencies #-} 
{-# LANGUAGE MultiParamTypeClasses #-} 
{-# LANGUAGE TypeSynonymInstances #-} 
data A = A 
    { aText :: String 
    } 

data B = B 
    { bText :: Int 
    , bValue :: Int 
    } 

-- A class for types a that have a "text" field of type t 
class HasText a t | a -> t where 

    -- An accessor for the text value 
    text :: a -> t 

-- Make our two types instances of those 
instance HasText A String where text = aText 
instance HasText B Int where text = bText 

main = do 
    let a = A "hello" 
    let b = B 42 1 
    putStrLn $ "Text of a: " ++ text a 
    putStrLn $ "Text of b: " ++ show (text b) 

Ma posso davvero raccomandare l'apprendimento dell'obiettivo, in quanto fornisce anche molte altre utilità, come la modifica o l'impostazione di un campo.

+1

Questo non funziona se due campi hanno lo stesso nome ma tipi diversi. –

+1

@GabrielGonzalez Almeno la versione dell'obiettivo funziona per diversi tipi. Non volevo complicare la versione manuale con famiglie di tipi o dipendenze funzionali, quindi ho omesso la diversa tipologia. – bennofs

+1

Ok, in realtà, non è così difficile, quindi ho aggiunto il supporto per diversi tipi. – bennofs

17

Per i progetti di grandi dimensioni, preferisco mantenere ciascun tipo nel proprio modulo e utilizzare il sistema di moduli di Haskell per gli accessori dello spazio dei nomi per ogni tipo.

Ad esempio, potrei avere qualche tipo A nel modulo A:

-- A.hs 

data A = A 
    { field1 :: String 
    , field2 :: Double 
    } 

... e un altro tipo B con campi denominato allo stesso modo in modulo B:

-- B.hs 

data B = B 
    { field1 :: Char 
    , field2 :: Int 
    } 

Poi se io voglio usare entrambi i tipi in qualche altro modulo C Posso importarli qualificati per distinguere quale accessor intendo:

Sfortunatamente, Haskell non ha un modo per definire più spazi nome all'interno dello stesso modulo, altrimenti sarebbe necessario suddividere ciascun tipo in un modulo separato per farlo.

+0

Funziona, ma è davvero brutto e prolisso. –

+3

Questo è così imbarazzante, a volte vorrei che Haskell supportasse i moduli annidati ... – MathematicalOrchid

+2

@MathematicalOrchid Non è così imbarazzante. In realtà questo approccio è piuttosto comune in altre lingue, ad esempio, in Java ogni tipo (classe o interfaccia) deve trovarsi in un file separato. –

6

Si noti che gli sviluppatori di GHC sembrano avere un piano su come gestire questo problema in futuro. Controlla this plan, che ho trovato menzionato alla fine di this blog post.

Problemi correlati