Automatic diffing for immutable types using Aardvark.Compiler.DomainTypes
In order to extract incremental changes from arbitrary operations performed on a purely functional data structure, we use automatically generated types which provide:
- Computing the change set of two different states of a immutable object (e.g. the old state and the new state after applying some operations)
- Supplying those changes to a mutable target object.
For each (possible nested) immutable type t
, we create a new type mt
which has one operation:
- update(newImmutableValue : 't) : unit
In our implementation, we call immutable types which have an associated mutable type domain type
. Our F# plugin Aardvark.Compiler.DomainTypes
automatically rewrites the original types (annotated with `dommain type attribute) and creates the mutable types under the
hood.
while for each field, the new type provides a field containing the mutable representation of the original field. Let us consider a simple example:
1: 2: 3: 4: 5: 6: 7: |
|
The Generated mutable types would be:
1: 2: 3: 4: 5: 6: 7: |
|
This translation allows you to work with purely functional immutable data and feed the updated values to a mutable representation, which can be updated automatically and provides modifiables which can be used to build dependency graphs.
For some types such as lists we provide special implementations which make use of efficient adaptive implementations of those datastructures. Next we show the complete translation sheme for domain types.
Type |
Mutable Type |
---|---|
Record type |
mutable type |
persistent hash set: |
|
persistent list |
|
persistent hash map: |
|
F# option type: |
|
Union type: |
Mutable types, active patterns: |
all other types |
|
There are currently two possibilities to adjust this translation process to your needs:
-
Sometimes, it is useful to make exceptions of this sheme for some fields. For example, for implementing undo
we often use a single field
past : 'model
which contains the complete model of the previous state. Here we would like to break finegrained diffing and use a changeable valuepast : IMod<'model>
instead. This can be achieved by marking thepast
field in the type definition with the[<TreatAsValue>]
attribute. -
For custom types, contained in hash containers (e.g.
hset<'k>,hmap<'k,_>
) is necessary to provide the diff generator with a mechanism to extract a hash value from the type'k
. This can be achieved, by adding a hashable field (which has a type with identity, i.e. a reference type) and marking this field with the[<PrimaryKey>]
attribute.
{position: obj;}
Full name: DomainTypeGeneration.Object
{name: string;
object: Object;}
Full name: DomainTypeGeneration.Scene
val string : value:'T -> string
Full name: Microsoft.FSharp.Core.Operators.string
--------------------
type string = System.String
Full name: Microsoft.FSharp.Core.string
{postion: obj;}
Full name: DomainTypeGeneration.MObject
{name: obj;
object: MObject;}
member Update : n:Scene -> 'a
Full name: DomainTypeGeneration.MScene
Full name: DomainTypeGeneration.MScene.Update
Full name: Microsoft.FSharp.Core.Operators.failwith