Question

To practise a little F#, I'm building myself a simple game. The game involves resources that players can spend. There are 3 kinds of resources. Items and actions in the game have an associated cost that can combine amounts of any number of these resources (or none, for free actions). I started implementing this much along the lines of : Creating a list with multiple units of measurements of floats in F#

[<Measure>] type gold
[<Measure>] type wood
[<Measure>] type stone

type Resource =
    | Gold of int<gold> 
    | Wood of int<wood>
    | Stone of int<stone>

Now I need a collection data type to represent a cost. I want it to :

  • Contain Resources. Ideally it would be constrained to no more than 1 Resource of each type, but that safety I could do without.

  • Be unordered. (1<gold>, 2<wood>) needs to equal (2<wood>, 1<gold>) ideally without redefining equality for the type.

  • Be easily summable with another collection of the same type (actions may have optional costs which will add up to the normal cost) and subtractable (from a player's pool of resources).

What would be a good F# collection type to do that ? I realized not many are unordered. I was looking at Set<'T> but the "based on binary trees" part has me a little confused and I'm not sure it suits my needs.

What do you think ? Did I miss something obvious in my design ?

Was it helpful?

Solution

If you need to represent resources containing some amount of gold, wood and stone, then it might make more sense to use a record type rather than a collection (e.g. a map or list) of discriminated unions.

For example, if you define your record like this:

type Resources =
  { Gold : int<gold> 
    Wood : int<wood>
    Stone : int<stone> }

Then a value of Resources satisfies all your criteria - it contains at most one filed for each kind of resource (it contains exactly one field of each kind, but the value can be zero). The fields are ordered, but the order does not matter (when creating the value) and you can also easily define + operator on the type:

type Resources =
  { Gold : int<gold> 
    Wood : int<wood>
    Stone : int<stone> }
  static member (+) (r1:Resources, r2:Resources) =
    { Gold = r1.Gold + r2.Gold
      Wood = r1.Wood + r2.Wood
      Stone = r1.Stone + r2.Stone }
  static member Zero = 
    { Gold = 0<gold>; Stone = 0<stone>; Wood = 0<wood> }

I also added Zero member, which makes it easier to create the record if you only want to set one of the resources. For example:

let r1 = { Resources.Zero with Gold = 2<gold> }
let r2 = { Resources.Zero with Wood = 4<wood> }
r1 + r2
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top