How to simplify the record syntax with pattern matching? I've just finished reading Learn You a Haskell and I've started to experiment on my own. I just wanted to create a very simple game system where player A can attack player B.

Now I have 2 questions:


My autoAttack function was very daunting to write. How could I improve it?
How would I format my autoAttack function correctly? I don't find it very pleasing to read.




main = print $ createActor `autoAttack` createActor
type Vector2= (Float,Float)
type Vector3 = (Float,Float,Float)
data StatsSystem = StatsSystem {physicalDamage :: Float,
                                spellPower     :: Float,
                                health         :: Float,
                                mana           :: Float,
                                attackRange    :: Float,
                                castRange      :: Float,
                                attackSpeed    :: Float
                               } deriving(Show)

data MovementSystem = MovementSystem {direction :: Vector2,
                                      speed     :: Float,
                                      position  :: Vector3
                                     } deriving(Show)
data Actor = Actor {stats :: StatsSystem,
                    ms    :: MovementSystem
                   } deriving(Show)
createActor :: Actor
createActor = Actor {stats = StatsSystem {physicalDamage = 1.0,
                                          spellPower = 1.0,
                                          health = 10.0,
                                          mana = 1.0,
                                          attackRange = 1.0,
                                          castRange = 1.0,
                                          attackSpeed = 1.0
                                         },
                     ms = MovementSystem{direction = (1.0,1.0),
                                         speed = 50.0,
                                         position = (0,0,0)
                                        }
                    }
autoAttack :: Actor -&gt; Actor -&gt; Actor
autoAttack Actor {stats = StatsSystem {physicalDamage = p,
                                       spellPower = _,
                                       health = _,
                                       mana = _,
                                       attackRange = _,
                                       castRange = _,
                                       attackSpeed = _
                                      },
                   ms = m
                 } 
           Actor {stats = s2,
                   ms = m2
                 } 
           = Actor {stats = StatsSystem {physicalDamage = physicalDamage s2,
                                         spellPower = spellPower s2,
                                         health = health s2 - p,
                                         mana = mana s2,
                                         attackRange = attackRange s2,
                                         castRange = castRange s2,
                                         attackSpeed = attackSpeed s2
                                        },
                    ms = m2
                   }
First, when you are not using fields in a record, you don't have to use _ to mark that they should be ignored;  you only need to include the fields in the pattern that you are actually using.

Second, it is possible to update a couple of fields in a record without respecifying the whole thing from scratch.  If x is the name of the old record and you want to set the field a to 42 and b to 24, then the expression x { a = 42, b = 24 } is equal to the record x with a set to 42 and b set to 24.

Putting these ideas together, autoAttack could be rewritten like so:

autoAttack :: Actor -&gt; Actor -&gt; Actor
autoAttack actor@(Actor {stats = stats@(StatsSystem {physicalDamage = p, health = h}})) =
    actor {stats = stats { health = h - p } }


Where we have used the syntax actor@(Actor ..) to bind the Actor to the name actor, which is a useful pattern for when you want to both pattern match on an argument and also have access to its value.



Just to be clear, it is worth mentioning that, because data is immutable, changing a couple of fields involves making a full copy of the data structure.  (Thanks to Daniel Wagner for bringing this up).