2013-07-04 20 views
21

Haskell nie ma notacji kropkowej dla członków rekordów. Dla każdego elementu rekordu kompilator tworzy funkcję o tej samej nazwie z typem RecType -> FieldType. Prowadzi to do konfliktów nazw. Czy są jakieś sposoby obejścia tego, tj. W jaki sposób mogę mieć kilka rekordów z tymi samymi nazwami pól?Konflikty nazw w zapisach Haskella

+5

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

+0

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

+0

To może być naprawione już wkrótce w GHC. Myślę, że jest to projekt GSoC: D – jozefg

Odpowiedz

14

Innym sposobem na uniknięcie tego problemu jest użycie pakietu lens. Zapewnia makeFields funkcję szablonu Haskell, którego można użyć tak:

{-# 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) 

Jeśli nie chcesz używać TemplateHaskell i obiektyw, można również zrobić ręcznie, co obiektyw automatyzuje użyciu 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) 

Ale naprawdę mogę polecić soczewkę do nauki, ponieważ zapewnia również wiele innych narzędzi, takich jak modyfikowanie lub ustawianie pola.

+1

Nie działa, jeśli dwa pola mają tę samą nazwę, ale różne typy. –

+1

@GabrielGonzalez Przynajmniej wersja obiektywu działa dla różnych typów. Nie chciałem komplikować wersji ręcznej z rodzinami typów lub zależnościami funkcjonalnymi, więc pominąłem różne typy. – bennofs

+1

OK, właściwie to nie jest takie trudne, więc dodałem obsługę różnych typów. – bennofs

17

W przypadku dużych projektów wolę utrzymywać każdy typ w jego własnym module i używać systemu modułów Haskella do akcesorów przestrzeni nazw dla każdego typu.

Na przykład, mogę mieć jakiś rodzaj A w module A:

-- A.hs 

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

... i inny rodzaj B z podobnie nazwanych pól w module B:

-- B.hs 

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

Następnie gdybym chcę użyć obu typów w innym module C Mogę zaimportować je kwalifikować, aby odróżnić który akcesor mam na myśli:

-- C.hs 
import A as A 
import B as B 

f :: A -> B -> (Double, Int) 
f a b = (A.field2 a, B.field2 b) 

Niestety, Haskell nie ma sposobu na zdefiniowanie wielu przestrzeni nazw w tym samym module, w przeciwnym razie konieczne byłoby podzielenie każdego typu na osobny moduł, aby to zrobić.

+0

Działa, ale jest naprawdę brzydki i gadatliwy. –

+3

To jest takie niezręczne, czasami chciałbym, aby Haskell obsługiwał moduły zagnieżdżone ... – MathematicalOrchid

+2

@MathematicalOrchid To nie jest takie niezręczne. W rzeczywistości takie podejście jest dość powszechne w innych językach, np. W Javie każdy typ (klasa lub interfejs) musi znajdować się w oddzielnym pliku. –

6

Pamiętaj, że deweloperzy GHC mają plan, jak poradzić sobie z tym problemem w przyszłości. Sprawdź this plan, którą znalazłem pod koniec this blog post.