Defining MMCC Model Types Effectively - bryanedds/Nu GitHub Wiki
Human-editable Model Types
Often you will want your model types to serialize with their field names included. Instead of having a model be represented with this s-expression in the editor -
[Enemy [] [] [] [] 3 NormalState [0 0] [] 0.05 0.05 5 [Gameplay GreatSword]]]]
...which can be succinct but very hard to read and edit, you will instead want your model represented with something like this -
[[CharacterType Enemy]
[PositionPrevious []]
[RotationPrevious []]
[LinearVelocityPrevious []]
[AngularVelocityPrevious []]
[HitPoints 3]
[ActionState NormalState]
[JumpState [0 0]]
[WeaponCollisions []]
[WalkSpeed 0.05]
[TurnSpeed 0.05]
[JumpSpeed 5]
[WeaponModel [Gameplay GreatSword]]]]]
By decorating your model type with the [] attribute, the underlying serialization system will know to use the latter representation.
You should probably use this approach for any model type that will have more than a few fields. And you should make this decision early in the project because the serializer cannot implicitly convert one already serialized form to the other (altho perhaps this is a feature we can try to find a way to support).
Limitation on Model Types
Unfortunately, not all .NET types have a serialization routine exposed for them. The underlying serializer works for most types you'll want to use in a model, but some are still missing. For example, we have yet to write the serializer for the imperative System.Collections.Generic collections (because a model type is generally expected to be purely functional, altho because there are cases it makes sense it's not, we plan to write these yet). If you need additional serialization support, you can write your own TypeConverter subclass for each type and do one of the following -
- add a conversion for an existing type by writing your own type converter subclass and use the
assignTypeConverter
function to plug it into the runtime, or - decorate your own type with the
[<TypeConverter (...)>]
attribute.
If it's a common type you think others will needed, however, please file an issue requesting that the type become serializable out of the box.
Efficient MMCC Model Equality Checks
Because model values often require frequent equality checks in an system (to determine whether the Content function needs to be run), you will want to make sure that they are fast enough for your use case. If your model type is relatively small and only contains small collections, you can disregard any of this advice because it's simply not going to matter. But when they grow to many fields or larger collections, you may want to make sure your model type's equality checks are as cheap as possible, particularly when small or no changes happen.
The first approach you can take is to just define your model's equality type as reference equality
. You can do this by decorating your model's type definition with the [] attribute like so -
type [<ReferenceEquality>] Model =
...
This will ensure that all equality checks on your model type are as fast as comparing .NET reference handles. An unchanged model will almost always have the exact same reference handle because with a purely functional data object, a new instance is required to change it.
However, there is also an alternative approach. One thing you will want to understand about F# is that the equality checks on its built-in collection types are very slow with linear complexity. This is because F# collections choose to look out for values that can be the same yet unequal at the same time. An example is the Single.NaN value. Two Single.NaN values - even those with the same binding! - are always considered unequal! Because of this nuance, F# collections cannot simply compare the reference handles of two Maps or two Sets to make equality checking efficient. However, as game developers, we sometimes want to sacrifice semantic perfection for speed, as is the case here. Therefore, Nu provides FCollection alternatives for your use in your model as an alternative to F#'s. By following these rules, you can ensure your model equality checks are as fast as possible -
- Use
FMap
instead of the F#Map
type. - Use
FSet
instead of the F#Set
type. - Use
FList
,FStack
, orFDeque
instead of the F#list
type. - Use
SArray
instead of the F#array
type.