A neat trick I noticed while watching the Dungeons, Dragons and Functions talk by Mathias Brandewinder is adding overloads to a discriminated union.
In his talk, he is modelling Dungeons and Dragons using F#. One of the things he needs to model is a dice roll. In D&D you have uncommon dice shapes and inside the rule book you’ll find dice rules like: 4d6+2d10+8
.
What does this formula means? 4 roles of a 6-sided dice + 2 roles of a 10-sided dice+ 8
To model this in F#, Mathias created the following discriminated union:
type Roll = | |
| Roll of int * Dice | |
| Value of int | |
| Add of Roll list |
You can then use this Roll type to create the formula above:
let example = Add [ Roll(4, D 6); Roll(2, D 10); Value 8 ] |
This works but is not very readible. To fix this, Mathias introduced some overloads on the Roll type:
type Roll = | |
| Roll of int * Dice | |
| Value of int | |
| Add of Roll list | |
// omitted for brevity | |
static member (+) (v1:Roll,v2:Roll) = | |
match v1,v2 with | |
| Add(rolls1), Add(rolls2) -> Add(rolls1 @ rolls2) | |
| Add(rolls1), roll2 -> Add(rolls1 @ [ roll2 ]) | |
| roll1, Add(rolls2) -> Add(roll1 :: rolls2) | |
| roll1, roll2 -> Add [ roll1 ; roll2 ] | |
static member (+) (roll:Roll,num:int) = roll + Value num | |
static member (+) (num:int,roll:Roll) = Value num + roll |
This nicely cleans up the formula to:
let example=Roll(2, D 6) + 10 + Roll(4, D 10) |
More information can be found in Mathias blog post here: https://brandewinder.com/2018/07/31/give-me-monsters-part-3/