Skip to content

Learning Aardvark.Media #3

ThomasOrtner edited this page Feb 2, 2018 · 13 revisions

Mods and the adaptive world

Mod versions of our values are part of the dependency graph constructed and maintained in the background. Each mod is aware of its own change and the change of its dependencies. Without going into much detail regarding the theory behind this, the mod system is the very backbone of Aardvark.Media ensuring that when we change our model only the relevant parts of our views are updated... automatically, always!

Mod.map

The data in our model and the data needed for a view are rarely directly compatible. For intance, we want to highlight selected boxes in a certain color, our data model has an isSelected state of bool, or IMod as part of the MModel, but our Sg.box needs an IMod so we can actually influence it's color. To achieve this we simply map one mod to the other.

Mod.map f x
f('a -> 'b) -> IMod<'a> -> IMod<'b>

let color =
  m.isSelected 
   |> Mod.map (fun x ->
     if x then C4b.red else C4b.white
   )
image neu

We can think of mods as boxes. We take type 'a out of the box, modify it with f to be of type 'b and then put it back into the box

Mod.bind

Often we want to use a value in our mappings that is already a mod. For example, the selection color is not just fixed at C4b.red, but also depends on the state of our domain model.

let selectionColor = 
  Mod.constant C4b.Red

let color =
  m.isSelected 
   |> Mod.map (fun x ->
     if x 
     then selectionColor 
     else Mod.Constant C4b.white
   )
   
//returns IMod<IMod<C4b>>

Color now is of type IMod<IMod>. We can't really work with that. To solve this elegantly, we have Mod.bind.

Mod.bind f x
f('a -> IMod<'b>) -> IMod<'a> -> IMod<'b>

let color =
  m.isSelected 
   |> Mod.bind (fun x ->
     if x 
     then selectionColor 
     else Mod.Constant C4b.white
   )
//returns IMod<C4b>

Recalling the box analogy, you can simply think of having a box inside a box. Mod.bind just unpacks the inner box and 'a lands in the outer box before it is transformed by f like in Mod.map.

Mod.map2

Mod.map2 f x y
f('a -> 'b -> 'c) -> IMod<'a> -> IMod<'b> -> IMod<'c>

Adaptive block

adaptive {
    let! a = a
    let! b = b    
    
    return f a b
}

alist, aset, amap builder

alist,amap,aset {
    yield ...
}

Similar to the adaptive computational expression we also provide builders for our adaptive datastructrues alist, amap, and aset. Since this is an adaptive context you can also use let! to unpack mods. Apart from that, it can be used like any other listbuilder by yielding each element.

Without going into details on these datastructures, here is quick cheat sheet on when to use which:

  • plist/alist
  • hmap/amap
  • hset/aset

Conclusion

As you might have already guessed, all these operations or transformations specify edges in our dependency graph and can be chained together naturally. The change of 'a in a Mod.map triggers f to produce a 'b. Consequently, the way you structure your dependency graph through the use of map, bind, adaptive blocks or data structures has an influence on which parts are updated and which fs have to be recomputed.

This can have implications on the performance as we have seen in adaptive blocks.

Action Lifting

  • box selection demo
  • selection from outside

Lenses

https://xyncro.tech/aether/guides/lenses.html

Config Lenses

(work in progress)

continue with Aardvark.Media Demos

Clone this wiki locally
You can’t perform that action at this time.