I'm trying to make a collection that holds multiple possible types. This is an example of how I'd want it to look:
type Position = Vector2
type Velocity = Vector2
type Appearance = String
type Component = Position | Velocity | Appearance
let components = List<Dictionary<string, Component>>()
let pos = Dictionary<string, Position>()
components.Add(pos) // Type "Position" does not match with type "Component"
I want to declare specific types that still fit under a general type. Is there a way that I can write my code like this? Is there a more idiomatic way to do this?
There are are a couple of things you have in your code here:
Type Abbreviations (Link)
type Position = Vector2
type Velocity = Vector2
type Appearance = String
Type abbreviations just define another name for an existing type, the type on the left and right and precisely equivalent and can be used interchangeably.
To give an example pertinent to this question, in F# there is a type abbreviation for the standard .NET List
, it's called ResizeArray
. It's defined like this:
type ResizeArray<'T> = System.Collections.Generic.List<'T>
It saves you having to open System.Collections.Generic
in order to use it and it helps to avoids confusion with the list
type in F# but it doesn't do anything other than add a new name for an existing type.
Disciminated Unions (Link)
type Component = Position | Velocity | Appearance
Here you have a single type called Component
, you can think of it as a single type with three constructors: Position
, Velocity
and Appearance
. You can also deconstruct the type again using the same three cases by pattern matching.
e.g.
match comp with
|Position -> ..
|Velocity -> ..
|Appearance -> ..
Hopefully, it should now come as no surprise that the type abbreviation Position
that you declared has no connection with the union case Position
that you declared as part of the Component
type. They are entirely independent of one another.
Position
means Vector2
and Component
is a completely seperate union type.
Assuming you wanted a Component
type that could contain multiple things, you would need to associate some values with the cases. Here is an example of creating such a Discriminated Union:
type Component =
| Position of Vector2
| Velocity of Vector2
| Appearance of string
Now, let's look at the next problem.
If we delete the type abbreviations and try the rest of the code with our new Discriminated Union
let components = List<Dictionary<string, Component>>()
let pos = Dictionary<string, Position>()
We now have a new error:
The type
Position
is not defined.
Well, remember what I said earlier about Component
. Component
is the type, Position
is not a type, it's a union case of Component
.
If you wanted to contain entire dictionaries of the same one of these options, you might be better changing your definition to something like this:
type ComponentDictionary =
|PositionDictionary of Dictionary<string, Vector2>
|VelocityDictionary of Dictionary<string, Vector2>
|AppearanceDictionary of Dictionary<string, string>
Then you could create a ResizeArray
/List
of these.
let components = ResizeArray<ComponentDictionary>()
Now, in order to populated this collection, we then just need use the appropriate case constructor for ComponentDictionary
let pos = PositionDictionary (Dictionary<string, Vector2>())
Now, pos is of type ComponentDictionary
so we can add it to components:
components.Add(pos) // No error here!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With