HiveBrain v1.2.0
Get Started
← Back to all entries
snippetModerate

How to simplify the record syntax with pattern matching?

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
thewithsyntaxrecordsimplifyhowpatternmatching

Problem

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)
}

Solution

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 -> Actor -> 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).

Code Snippets

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

Context

StackExchange Code Review Q#35512, answer score: 11

Revisions (0)

No revisions yet.