Как реорганизовать использование ProgramState в Haskell? - PullRequest
2 голосов
/ 20 апреля 2020

Одна из вещей, на которые я наткнулся, заключается в том, что я не знаю, как реорганизовать использование ProgramState. Вот некоторые определения:

data ProgramState = ProgramState {
  a :: TypeA,
  b :: TypeB,
  c :: TypeC
}

type SearchFunc = ProgramState -> String -> [Completion]

type MS = ReaderT SearchFunc (StateT ProgramState IO)

instance MonadState s m => MonadState s (InputT m) where
    get = lift get
    put = lift . put
    state = lift . state

type MS = ReaderT SearchFunc (StateT ProgramState IO)

То, что я делаю в большинстве функций, это:

func :: InputT MS ()
func = do
  ProgramState a b c <- get
  -- use a b c and generate newa, newb, newc
  put $ ProgramState newa newb newc

В программе ~ 300 подобных случаев использования ProgramState, и это настоящая боль когда я добавляю другой элемент к ProgramState, потому что тогда мне нужно изменить все 300 использований ProgramState в программе. Как реорганизовать мою программу, чтобы избежать этой боли необходимости менять похожий код в 300 разных местах?

1 Ответ

3 голосов
/ 20 апреля 2020

Базовый язык обеспечивает обновление записей по этой причине:

func :: InputT MS ()
func = do
    -- -XNamedFieldPuns  vvvvvvvvvvvvvvvvvvvvvvv can simplify this to {a, b, c}
    inState@ProgramState { a = a, b = b, c = c } <- get
    -- etc.
    put inState { a = newA, b = newB, c = newC }

RecordWildCards также делает возможным следующее, но я нахожу это менее ясным:

func = do
    ProgramState{..} <- get -- release ALL of the fields
    -- etc.
    put ProgramState { a = newA, b = newB , c = newC, .. }
    -- rebuild with certain specified values and then pass anything not mentioned through
...