📜 ⬆️ ⬇️

Obtics - functional reactive programming on .Net

This article is a translation of the main page of the Obtics project to Codeplex with minor changes.

Project Description


The goal of this project is to create a library that allows applying the principles of functional reactive programming (FRP) in .Net.

In the FER, your calculations automatically react to changes in the data used in them.
')

Story


The idea of ​​the project arose while working on a large administrative application that uses XAML to describe the interface. In this application were extensive domain models of data and display.

One of the problems was that it was rather difficult to determine when the view should be updated. A simple recalculation of the entire display for each user action is not a good enough idea, since it takes too much time and, moreover, the data can be updated by some background process.

As a solution, I created a set of converters that created objects that track changes in their source with the ability to update the calculated data. I found this concept to be very useful, because you no longer need to subscribe and unsubscribe to events or remember the need to update controls when a click event occurs. It is enough just to designate the dependencies and specify how to calculate the result.

WPF binding was not a good option for this task because:

Although I thought that the transformations of the collections were really useful, the idea almost immediately failed for the following reasons:
  1. Their use required additional words in the code, which complicated its perception by people not familiar with this technology. And although the “register-event-handler-update” code was actually more complicated, it was more convenient for my colleagues to read.
  2. The objects worked completely asynchronously, and each had its own message queue and buffer, which made them very “heavy”. Due to their success, they were often used in the project and therefore created a significant load on the application.
  3. Debugging pipeline transformations in the classic debugger becomes very difficult. It is not so easy to follow the update process step by step.

This experience led me to clarify some additional requirements for the next version of conversion objects:

Opportunities


Obtics allows you to create reactive and observable objects, freeing you from the need to update the result and keep track of when you need to update the result in a dynamic application. Having the opportunity to worry less about it, you can quickly build rich and more reliable applications.

Less abstract


A simple piece of code, like the one below, will produce a static result using the standard Object LINQ. If you add a bind to the PeopleNames property of XAML, you will get a one-time result, regardless of how you modify the _People collection. Using Obtics, the PeopleNames result will be completely reactive and reviewable. It means:
  1. If you change the _People collection or the LastName property of a single People object, the PeopleNames value will be automatically updated (reactivity).
  2. Your bound (XAML app) will automatically display changes in PeopleNames (observability).
public class Test
{
ObservableCollection<Person> _People;

public IEnumerable < string > PeoplesNames_Static
{
get
{
return
from p in _People
orderby p.LastName
select p.LastName ;
}
}

public IEnumerable < string > PeoplesNames
{
get
{
return
ExpressionObserver.Execute(
this ,
t =>
from p in t._People
orderby p.LastName
select p.LastName
).Cascade();
}
}
}

* This source code was highlighted with Source Code Highlighter .

With Obtics, you can write the code below and make a binding on the Value value of the FullName property. Any changes to the LastName or FirstName properties will be automatically and immediately displayed in the application.
public class Test
{
Person _Person;

public IValueProvider< string > FullName
{ get { return ExpressionObserver.Execute( this , t => t._Person.LastName + ", " + t._Person.FirstName); } }
}

* This source code was highlighted with Source Code Highlighter .

(The Person class from these examples is the observable class. This means that an instance of this class sends change notifications each time a property value changes. Read more here .)

Conversion Types


Implicit value conversions.


The methods used form the Obtics.Values.ExpressionObserver class. Just write a lambda function that returns the desired result, ExpressionObserver will analyze it, extract all dependencies and create an expression that will be completely reactive if you change any visible value on which this expression depends. This will automatically rewrite the standard Object LINQ into a reactive and observable form, which is a very convenient way to create bindableobject LINQ queries.
using Obtics.Values;

class Test
{
Person _Person = new Person( "Glenn" , "Miller" );

Person Person
{ get { return _Person; } }

public IValueProvider< int > PersonFullNameLength
{
get
{
return
ExpressionObserver.Execute( this , t => t.Person.FirstName.Length + t.Person.LastName.Length + 1);

//the below line is even simpler but because the lambda expression depends on the external 'this' variable the
//expression is re-compiled for every Test class instance. Better use lambda's without external variables.
//return ExpressionObserver.Execute(() => Person.FirstName.Length + Person.LastName.Length + 1);
}
}
}

* This source code was highlighted with Source Code Highlighter .

Explicit conversion of values.


Uses methods from the Obtics.Values.ValueProvider class. You can build your own transformation pipeline, personally specifying methods that will allow you to easily control the process of transformation. You can specify exactly the changeable values ​​that correspond to your calculations, so that you can prevent resources from being spent on unchanged or non-viewable dependencies. This approach can be useful when working with large amounts of data.

using Obtics.Values;

class Test
{
Person _Person = new Person( "Glenn" , "Miller" );

Person Person
{ get { return _Person; } }

public IValueProvider< int > PersonFirstNameLength
{
get
{
return
//select the never changing Person property make a (static) IValueProvider for it.
ValueProvider.Static(Person)
//select the FirstName property of Person. This is a classic
//property and observably mutable
.Property<Person, string >(Person.FirstNamePropertyName)
//Calculate the result
.Select(
//fn is a string and Length is an
//immutable property of string
fn => fn.Length
);
}
}
}

* This source code was highlighted with Source Code Highlighter .

Collection transformations (LINQ).


Uses methods from Obtics.Collections.ObservableEnumerable. As with explicit transformations, this approach allows you to specify how your collection will react. This will complicate the use of the library, but you can manually describe the transformations and be notified of dependencies that need to be monitored for changes. This allows you to prevent the use of resources on dependencies that will never be changed. This style can be useful when working with large collections to prevent unnecessary resource costs. To take full advantage of using this method, it is necessary in the code to replace “using System.Linq;” with “usingObtics.Collections;” otherwise there will be many errors.
using SL = System.Linq;
using Obtics.Values;
using Obtics.Collections;

class Test
{
ObservableCollection<Person> _People = new ObservableCollection<Person>();

public ReadOnlyObservableCollection<Person> People
{ get { return new ReadOnlyObservableCollection<Person>(_People); } }

public IEnumerable < string > LastNames
{
get
{
return
from p in People
select ValueProvider.Static(p).Property<Person, string >( "LastName" ) into ln
orderby ln
select ln;
}
}
}

* This source code was highlighted with Source Code Highlighter .


ExpressionObserver (implicit conversions) is extensible. A library has been created ( ObticsToXml ), which is built on its extension and allows you to create fully dynamic Linq toXML expressions.

Obtics offers fully visible support for all ObjectLinq and most of Linq to XML .

More examples can be found at these links: Transformation Examples , ObticsExaml and ObticsRaytracer .

The ObticsWpfHelper project contains a solution based on a partial combination of Obtics and WPF.

It is very important that the functions, lambda expressions, values ​​and objects that are used with Obtics behave as expected from them.

Future opportunities


Version 2.0 is on its way. Plans for future versions:

Project site: http://obtics.codeplex.com/

Source: https://habr.com/ru/post/85111/


All Articles