# Let’s make an Elemental Type System in Haskell – Part I

Hello everyone,

recently I falled in love (again) with Haskell, and I’ve decided to start a simple toy project just to stretch my Haskell muscles fibers.
Meanwhile, I’ve started Final Fantasy VII again, so I thought to realize a simple Elemental Type System. I dunno how much this project will be complex, but a lot of fun awaits.

#### The elemental cycle

The first to do is to choose an Elemental cycle i.e. how elements influence each other. I’ve choose this simple schema (don’t blame me for the source):

For now just ignore the star in the center of the picture, for us “Fire is weak against Earth, strong against Lightning”. Ok, so we need a type representing our elements, and we need a circular one, because we want be free to invoke

`pred`

and

`succ`

without having our program to crash because we reach a boundary. Unfortunately, I haven’t found a better way to implement this without having to manually enum each element:

```--We want a cyrcular enumeration for our elems
--so we can't rely on the Bounded type
data Element = Fire | Earth | Wind | Water | Lightning
deriving (Eq, Ord, Show, Read)

instance Enum Element where
toEnum 0 = Fire
toEnum 1 = Earth
toEnum 2 = Wind
toEnum 3 = Water
toEnum 4 = Lightning
toEnum i = toEnum \$ i `mod` 5

fromEnum Fire = 0
fromEnum Earth = 1
fromEnum Wind = 2
fromEnum Water = 3
fromEnum Lightning = 4

enumFromTo x y = map toEnum [a .. b']
where a = fromEnum x
b = fromEnum y
b' = if a <= b then b else b + 5

enumFromThen x1 x2 = error "enumFromThen not supported for Element"
enumFromThenTo x1 x2 y = error "enumFromThenTo not supported for Element"```

We haven’t done much yet, fun starts right on! The second step is create a monster type, through the record syntax, which allow to nicely get the type properties:

```data Monster = Monster {name :: String,
hp :: Integer,
mp :: Integer,
elemType :: Maybe Element}

instance Show Monster where
show m = "Name: " ++ name m ++ "\n" ++
"HP: " ++ show (hp m) ++ "\n" ++
"MP: " ++ show (mp m) ++ "\n" ++
"Element: " ++ show (elemType m)```

I’ve created an instance of Show for the type Monster for pretty printing purpose. Note how elemType is of type “Maybe Element”, just because not every monster is an elemental one! Using Maybe we can make our elemental system more flexible and less crash prone. The next step is to create four simple functions to play with monsters weakness and strenghts:

```weakTo :: Monster -> Maybe Element
weakTo m = case elemType m of
Just e -> Just \$ succ e
_ -> Nothing

strongAgainst :: Monster -> Maybe Element
strongAgainst m = case elemType m of
Just e -> Just \$ pred e
_ -> Nothing

isWeakTo :: Monster -> Element -> Bool
m `isWeakTo` elem = case elemType m of
Just e -> elem == (succ e)
_ -> False

isStrongAgainst :: Monster -> Element -> Bool
m `isStrongAgainst` elem = case elemType m of
Just e -> elem == (pred e)
_ -> False```

As you can see, the code is pretty straightforward. We can use our functions just like a sort of “Sense” spell, searching for monster weakness (I remember you that a spell can be more or less effective depending on the type of the target). This functions works but as you can see there is too much boilerplate code, since we are changing only a function (pred and succ) between the two. Function application operator comes in rescue:

```checkProperty :: Monster -> (Element -> Element) -> Maybe Element
checkProperty m f = case elemType m of
Just e -> Just . f \$ e
_ -> Nothing```

checkProperty is our general purpose function who checks for monster weakness and strengths, and here are the new functions versions:

```weakTo' :: Monster -> Maybe Element
weakTo' m = checkProperty m succ

strongAgainst' :: Monster -> Maybe Element
strongAgainst' m = checkProperty m pred```

Wow, only one line of code! The dirty job is done by “checkProperty”, and you can write similar and shorten function even for isWeakTo and isStrongAgainst

#### Let’s test them!

Now the funniest part, let’s have some fun with ghci:

```:l Types.hs
[1 of 1] Compiling Main             ( Types.hs, interpreted )
Ok, modules loaded: Main.
ghci> let piro = Monster{name = "Piro", hp = 70, mp = 200, elemType = Just Fire}
ghci> checkProperty piro succ
Just Earth
ghci> checkProperty piro pred
Just Lightning
ghci> :r
[1 of 1] Compiling Main             ( Types.hs, interpreted )
Ok, modules loaded: Main.
ghci> let piro = Monster{name = "Piro", hp = 70, mp = 200, elemType = Just Fire}
ghci> strongAgainst' piro
Just Lightning
ghci> weakTo' piro
Just Earth
ghci> let dragon = Monster{name = "Dragon", hp = 200, mp = 100, elemType = Nothing}
ghci> dragon `isWeakTo` Fire
False
ghci> piro `isWeakTo` Earth
True
ghci>dragon
Name: Dragon
HP: 200
MP: 100
Element: Nothing
ghci>piro
Name: Piro
HP: 70
MP: 200
Element: Just Fire```

Stay tuned for other improvement! Have fun with Haskell!

Alfredo

## 3 thoughts on “Let’s make an Elemental Type System in Haskell – Part I”

1. Interesting way to get into Haskell 🙂

Have you checked functors and applicatives? You could refactor weakTo and strongTo

This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.

 — Using Applicative weakTo :: Monster -> Maybe Element weakTo m = succ <\$> elemType m — Using functors weakTo' :: Monster -> Maybe Element weakTo' m = succ `fmap` elemType m

view raw

tmp.hs

hosted with ❤ by GitHub

🙂

• Alfredo Di Napoli ha detto:

Thanks, I’m happy that you liked this series (already took a look to part 2 and 3?)
My Haskell knowledge improves day by day thanks to a wonderful book called “Learn You a Haskell for a Great Good”, so this code will be better and better (I hope 🙂 )
Yea, I’ve already studied functors and applicative, but I wanna refactor my code with the more powerful (and awesome) Monads! 😀
Stay tuned!
Alfredo