2011-12-14 26 views
6

Mam boilerplate Haskell, który wygląda mniej więcej tak:Haskell dane typu dynamiczna zmiana

data Configuration 
    { confA :: Integer 
    , confB :: Boolean 
    , confC :: String } 

x = (\arg opt -> opt{ confA=arg }) 
y = (\arg opt -> opt{ confB=arg }) 
z = (\arg opt -> opt{ confC=arg }) 

i chciałbym, aby usunąć boilerplate, otrzymując coś wzdłuż linii:

setter :: (Config -> a) -> a -> Config -> Config 
x = setter confA 
y = setter confB 
z = setter confC 

Ale nie mam pojęcia, jak skonstruować taką funkcję setter. Czy jest to w ogóle możliwe w (nie-szablonowym) haskell, czy też mam tutaj przeciwko cukrowi składni? Jeśli tak, to jak mógłbym zrobić coś takiego w szablonie haskell?

Odpowiedz

13

Nie jest to możliwe przy użyciu systemu nagrywania Haskella. To, czego oczekujesz, to soczewki; to previous Stack Overflow question i jego górna odpowiedź są dobrym wprowadzeniem. Osobiście korzystam z omawianego tam pakietu data-lens. (Patrz również data-lens-fd go używać z klasą MonadState z MTL - jeśli nie wiesz co to jest, po prostu wiem, że powinieneś go używać, gdy chcesz używać soczewek w State monady.)

data-lens-template pakietem może być aplikacja szablonu Haskell, którą chcesz; wyprowadza definicje soczewek dla pól rekordów.

Innym popularnym pakietem obiektywów jest fclabels. Wolę soczewkę danych za jej prostotę i szybkość; fclabels (od wersji 1.0) jest nieco bardziej elastyczny, ale nie potrzebujesz tej elastyczności do tego, co chcesz robić. (Zauważ, że ze względu na jego nowo zwiększoną elastyczność, typu fclabels '(:->) nie można już bezpośrednio przetłumaczyć na prostą definicję soczewek, jak wspomniano w odpowiedzi na przepełnienie stosu I).

1

Nie jest to możliwe bez szablonu Haskell. Możesz użyć dostępu do danych i szablonu dostępu do danych, aby usunąć taką płytę kotła.

+0

Uważam, że jest to poprawne. Składnia rekordów automatycznie tworzy dla ciebie * gettery *, ale są one bezużyteczne, gdy chcesz utworzyć * settery *. –