Despite being a minor release, 0.6.1 still weighs a heavy 588 commits with a big number of fixes and many additions, among which the new reactive framework is the most notable.
Last release introduced, for the first time in the Rebol world, a reactive programming framework, as a part of the GUI engine. While working on improving it, we realized that it could actually be easily generalized beyond GUIs, with just minor changes to its design and implementation.
Let me make a short disclaimer first, this is not yet-another-FRP framework relying on event streams. The reactive model we use is known as object-oriented reactive programming (using a “push” model), which is both simple to understand and close to spreadsheet’s model (i.e. Excel formulas). That model has often been praised for its simplicity and efficiency. You can now use it directly in Red.
So, in practice, what is it? It is a way to link one or more object fields to other fields or global words, by specifying relationships in a block of code (can be a single expression or a complex multi-step computation). Each time a source field value changes, the target value is immediatly updated, you don’t have to call a function for that, it’s pretty much define-and-forget. ;-) Here’s a simple example in Red:
red>> a: make reactor! [x: 1 y: 2 total: is [x + y]] == make object! [ x: 1 y: 2 total: 3 ] red>> a/x: 5 == 5 red>> a/total == 7 red>> a/y: 10 == 10 red>> a/total == 15
In that example, the is infix function is used to create a reactive relation between the total field and the x and y ones using a simple formula. Once set, total is refreshed automatically and asynchronously each time the other fields are changed, regardless how, where or when they are changed! It’s the same concept as spreadsheet cells and formulas, just applied to object fields.
This reactive programming style belongs to the dataflow programming paradigm. It doesn’t enable you to write code, that you wouldn’t otherwise be able to write in an imperative style. Though, it helps reduce the size and complexity of your code, by abstracting away the “how” and helping you focusing more on the “what” (not dissimilar to FP). The gains of such approach become significant when you chain together many relations, creating graphs of, more or less complex dependencies. GUI programming is where it shines the most, as nodes are visible objects, and reactions produce visible effects.
Here is a comparative example with a reactive GUI vs the non-reactive version:
Let’s make a simple native GUI app using VID, Red’s graphic DSL (we call it a dialect). It will just provide 3 sliders, which control the R, G, B components of the box’s background color.
The reactive version:
to-int: function [value [percent!]][to integer! 255 * value] view [ below r: slider g: slider b: slider base react [ face/color: as-color to-int r/data to-int g/data to-int b/data ] ]
The non-reactive version:
to-int: function [value [percent!]][to integer! 255 * value] view [ below slider on-change [box/color/1: to-int face/data] slider on-change [box/color/2: to-int face/data] slider on-change [box/color/3: to-int face/data] box: base black ]
What can we say about the non-reactive version?
box), so it can be referred to, from the event handlers.
blackkeyword to force it to the right default color (as the sliders are all at position 0 on start). The reactive version sets the right color on start, no need to care about it.
Even in this simple example, we can see that the complexity, and the cognitive load are higher in the non-reactive version. The more relationships can be modeled using reactions in a GUI app, the higher the gains from using the reactive approach.
Red’s reactive framework is just ~250 LOC long, and written purely in Red (no Red/System). It relies on object events (equivalent to observables in OO languages) and the ownership system (which will be properly documented once completed in one or two releases time). Rebol does not offer any past experience in such domain to guide us, so it should still be considered experimental, and we need to put it to the test in the wild, to study the pros/cons in real-world applications. We are quite excited to see what Red users will create with it.
Full documentation for the reactive framework is available here. It also explains the important difference between static and dynamic relations.
In a nutshell, the reactive API provides 4 functions (quite big API by our standards):
reactto create or remove reactions.
isinfix function for creating reactions which result will be assigned.
react?to check if an object’s field is a reactive source.
clear-reactionsto remove all existing reactions.
Moreover, react is directly supported as a keyword from VID dialect. That’s all you need! ;-)
Here is a simple demo linking together a couple dozen balls, following each other. Source code is available here.
Let’s now have a look at other features brought by this release.
A time! datatype is now included in Red, supporting already a broad range of features, like:
Path accessors: /hour, /minute, /second. Math operators, including mixing with other scalar types. All comparison operators. Actions: negate, remainder, random, pick.
red>> t: now/time == 12:41:52 red>> t + 0:20:00 == 13:01:52 red>> t/second == 52.0 red>> t/hour: t/hour - 5 == 7 red>> t == 7:41:52
Two main additions to our View engine have enabled the writing, or porting, of some nice graphic demos (thanks to Gregg Irwin for the awesome demos!). Here are a few examples:
timeevent in View, triggered by timers.
ratefacet in face! objects for setting timers.
moveaction allows to move faces between panes in a non-destructive way.
textfor field and text faces.
defaultoption for fields (e.g. options: [default 0]).
copyon image! now accept pair! index argument.
/argbrefinement for image! datatype.
offas argument now, to make the subsequent pen-related operations invisible.
boxto accept edges in reverse order.
circlenow accepts a float! value.
ratekeyword for setting timers.
docommand now support
selfto refer to container face (window or panel).
focusoption to faces for presetting focus.
selectoption support to preselect an item in a list (using an integer index).
defaultoption support for field and text faces’ default data facet value.
The [red/code repository has also been filled with more demos using the new features, like color gradients and timers.
removealso accepts, now, a position argument.
Example using rule syntax:
a: "12abc34" alpha: charset [#"a" - #"z"] parse a [some [to alpha change [some alpha] dot]] a = "12.34"
Example using pos syntax:
a: "12abc34" alpha: charset [#"a" - #"z"] parse a [some [to alpha b: some alpha change b dot]] a = "12.34"
findon any-string! series.
/nextrefinement support for
/partrefinements added to
splitfunction (though not final version).
inputcan now read from stdin when run from a child process.
ownedproperty to be used by
A big number of tickets have also been processed, 110 bug fixes have been provided since 0.6.0 release. We have about 10% of open tickets which is more than usual, though not surprising after the last huge release, but only 22 are actual bugs. Thanks for all the contributors who reported the issues and helped fix them, Red owes you a lot!
On the road to Android support, we need to be able to properly wrap a Red app in a shared library, which is the main focus for the next release. Moreover, being able to build the Red runtime library only once, will greatly reduce compilation time (the runtime library is currently rebuilt on each compilation). As the work on this new feature is already quite advanced, we expect next release to occur during July, even if we always favor quality over arbitrary deadlines. ;-)
In the meantime, enjoy the new release!