Obtics basicly consists of 3 layers.
The first layer are those classes whose instances do the actual grunt work. These classes take one or more 'sources' being an observable value or collection and have one or more clients. Together with some helper classes these
are the 'Transformation' classes. Each Transformation class provides a 'transformed' view of it's source. What it means it takes the source values or collections, does some calculation with it and presents the result. What they also do is that when a source
sends a change notification they check if it is relevant and if it is, propagate this notification to it's clients.
The Transformation classes can be found in the Obtics.Collections.Transformations namespace (for collection transformations) and the Obtics.Values.Transformations namespace (for value transformations).
Each Transformation object can be client or source for other Transformation objects. All these transformation objects working together allow for complex transformation graphs. I often call them transformation pipelines. The a + b example would probably
consist of 2 PropertyTransformation objects for reading and listening for changes on the individual properties and one BinaryTransformation doing the '+' operation.
The second layer are those methods that create instances of the Transformation classes and glue them together into transformation pipelines or graphs. These methods are implemented as extension methods and for collections they map directly
to the object linq methods. For value transformations they are extension methods on the IValueProvider interface and defined in the Obtics.Values.ValueProvider class. For object linq (collection transformations) they are defined in Obtics.Collections.ObservableEnumerable
Your o.a + o.b example could be expressed as ValueProvider.Select( ValueProvider.Property<O,int>(o,"a"), ValueProvider.Property<O,int>(o,"b"), (aValue, bValue) => aValue + bValue). Executing this expression
would result in two PropertyTransformation objects and one BinarySelectTransformation object.
The third layer is the Obtics.Values.ExpressionObserver. It takes a lambda expression and rewrites it into calls to the mentioned extension methods. In this process each value becomes an IValueProvider. Note that the
ExpressionObserver is in the Obtics.Values namespace. This is technically correct since the hard work is in rewriting value expressions. An int value becomes an IValueProvider<int>. This requires complex rewriting of the expression. An IEnumerable<int>
remaines an IEnumerable<int>. The IEnumerable becomes observable simply because the System.Linq.Enumerable methods are directly replaced with the Obtics.Collections.ObservableEnumerable methods.
So ExpressonObserver.Rewrite((O o) => o.a + o.b) would become something like (O o) => ValueProvider.Select( ValueProvider.Property<O,int>(o,"a"), ValueProvider.Property<O,int>(o,"b"), (p1, p2) => p1 +
p2) which would be a Func<O,IValueProvider<int>>
ExpresionObserver.Rewrite((O o) =>o.l1.Concat(o.l2)) would become (O o) => ValueProvider.Select( ValueProvider.Propert<O, IEnumerable<int>>(o,"l1"), ValueProvider.Property<O.IEnumerable<int>>(o,"l2"), (p1,
p2) => ObservableEnumerable.Concat(p1,p2)) which would be a Func<O,IValueProvider<IEnumerable<int>>>
Hope this sheds some light,