Reputation: 745
I have a data type to keep track of state. Let's say it looks like this:
data State = State String String Int
Several functions take a State
and return a new one. For example:
convert :: State -> Int -> State
convert (State a b c) 0 = State "new" b c
convert (State a b c) 1 = State a "new" c
convert (State a b c) 2 = State a b 0
(1) When I edit the State
data type (to hold an additional Int
, for instance) I need to edit the pattern-matching in all functions. Is it better to use record syntax in this case?
data State = State { a :: String, b :: String, c :: Int }
convert :: State -> Int -> State
convert state 0 = State "new" (b state) (c state)
convert state 1 = State (a state) "new" (c state)
convert state 2 = State (a state) (b state) 0
Now I don't have to edit the pattern-matching when changing the data type State
. The construcor calls on the right hand side suffer from the same problem though. Is there a way around this? I could define some functions to create State
s (only those needed be manipulated when the State
constructor changes).
(2) I guess there are other ways of handling this...
Upvotes: 3
Views: 61
Reputation: 54058
You can use record update syntax:
convert state 0 = state { a = "new" }
convert state 1 = state { b = "new" }
convert state 2 = state { c = 0 }
Or you could use lenses (in particular Edward's lens library):
{-# LANGUAGE: TemplateHaskell #-}
import Control.Lens
data State = State
{ _a :: String
, _b :: String
, _c :: Int
} deriving (Eq, Show, Read)
makeLenses ''State
convert :: Int -> State -> State
convert 0 = set a "new"
convert 1 = set b "new"
convert 2 = set c 0
-- Or using the infix version of set
-- convert 0 = a .~ "new"
-- convert 1 = b .~ "new"
-- convert 2 = c .~ 0
If you want to know more about the black magic that occurs in the lens library, there are many, many tutorials out there and the documentation is generally pretty good, but that is outside of the scope of this question
Upvotes: 4