2012-01-18 20 views
9

Jak miałem writing up an answer właśnie teraz, natknąłem się na ciekawy problem:Record Wartość domyślna Składnia akcesor

data Gender = Male | Female 
      deriving (Eq, Show) 

data Age = Baby | Child | PreTeen | Adult 
     deriving (Eq, Show, Ord) 

data Clothing = Pants Gender Age 
       | Shirt Gender Age 
       | Skirt Age   -- assumed to be Female 
       deriving (Show, Eq) 

Załóżmy, że chcemy napisać ostateczny typ danych ze składnią rekordu:

data Clothing = Pants {gender :: Gender, age :: Age} 
       | Shirt {gender :: Gender, age :: Age} 
       | Skirt {age :: Age} 
       deriving (Show, Eq) 

Problem polega na tym, że chcę, aby gender $ Skirt foo zawsze oceniał na Female (niezależnie od foo, który jest Age). Mogę myśleć o kilka sposobów, aby to osiągnąć, ale wymagają one, że albo

  1. stosowanie inteligentnych konstruktorzy, teoretycznie umożliwiając Skirt Male foo ale nie narażając konstruktorów
  2. definiować własne gender funkcję

Z # 1, nie ujawniając konstruktora w module, skutecznie uniemożliwiam użytkownikom modułu korzystanie z zapisu składni. Z # 2 muszę całkowicie zrezygnować ze składni lub zdefiniować dodatkową funkcję gender', która ponownie pokonuje składnię rekordów.

Czy istnieje sposób, aby skorzystać ze składni rekordów, a także zapewnić "domyślną", niezmienną wartość jednego z moich konstruktorów? Jestem także otwarty na rozwiązania nieopisane na składnię (może soczewki?), O ile są tak samo eleganckie (lub moreso).

+4

Normalny obiektyw nie działałby, ponieważ można przypisać pole 'gender' z dwoma konstruktorami, ale nie drugim. Myślę, że robienie tego jest złym pomysłem; jeśli 'foo' jest rekordzistą, a' foo x' działa, to 'x {foo = y}' również powinno. – ehird

+0

Zwykle używam wersji 2. – augustss

+0

W jaki sposób dodanie funkcji 'gender'' (czy mogę ją nazwać' totalGender' lub 'safeGender' lub coś podobnego) powoduje uszkodzenie składni rekordów? –

Odpowiedz

0

Czy istnieje sposób, aby skorzystać zarówno ze składni rekordów, a także zapewnić "domyślną", niezmienną wartość dla jednego z moich konstruktorów?

W przypadku braku przekonującego kontrprzykładu odpowiedź brzmi "nie".

0

Tak, istnieje napięcie między typami i danymi ... które przy okazji pokazuje, jak cienka jest linia.

Pratyczną odpowiedzią jest użycie domyślnej instancji, jak wskazano w Haskell Wiki. To odpowiada na twoje dokładne pytanie, ponieważ musisz zrezygnować z bezpośredniego użycia konstruktora.

Tak więc dla przykładu,

data Age = Baby | Child | PreTeen | Adult | NoAge 
data Clothing = Pants {gender :: Gender, age :: Age} 
       | Shirt {gender :: Gender, age :: Age} 
       | Skirt {gender :: Gender, age :: Age} 
       deriving (Show, Eq) 

skirt = Skirt { gender=Female, age=NoAge } 

następnie developpers mogą tworzyć nowe instancje z wartości domyślnych, korzystając z usługi kopiowania i-update składni rekordu

newSkirt = skirt { age=Adult } 

i gender newSkirt rozpoznawaną Female

Chcę podkreślić, że takie podejście prowadzi do zdefiniowania wartości domyślnych na poziomie typu, który uważam za Dobra rzecz (oczywiście konstruktorem NoAge jest Nothing typu Maybe Age).