Как "изменить" отдельные значения в больших объектах элегантным способом?

Например, у меня есть

data ShipDesign = ShipDesign {
      offense :: Offense
    , defense :: Defense
    , maxHealth :: Integer
    , repairRate :: Integer
    , stealth :: Integer
    , radar :: Integer
    , speed :: Integer
    , shipType :: String
    ...
    }

Теперь я хочу изменить защиту. Известный способ сделать это:

changeDefense :: (Defense -> Defense) -> ShipDesign -> ShipDesign
changeDefense fDef [email protected](ShipDesign o d m rr s r sp st ...) = ShipDesign o (fDef d) m rr s r sp st ...

который не является элегантным. Особенно в играх его общий для изменения всего нескольких значений за шаг.

Мой вопрос: есть ли библиотека, шаблон дизайна или другой способ изменить одно значение более элегантным образом?

Ответ 1

Да, вы можете использовать нотацию записи записи:

 changeDefense :: (Defense -> Defense) -> ShipDesign -> ShipDesign
 changeDefense fDef sd = sd { defense = fDef (defense sd) }

По мере того как вы будете работать с ним, ограничения нот записи обновления покажутся сами собой, и вы захотите что-то более мощное. В этот момент вы должны начать изучать lenses.

Ответ 2

Интересно, почему никто не предлагал линзы?

Я рекомендую это краткое введение Габриэля Гонсалеса: http://www.haskellforall.com/2013/05/program-imperatively-using-haskell.html?m=1

Изменить: о... Я прочитал последнее предложение. Но предложение все еще стоит.