|
|||||||||||||||||||||||||||
Page Content
SetupSetup | |
Once you've download slim and liquid, setup slim for loading the module (its easy, more details here), using liquids is as easy as 1-2-3 (4-5 ;-) .
1. Create a new node type: | |
Since we actually want to do something usefull in this example, lets make a new node type which adds all of its inputs.
** The !plug has a shared object model, so that all instances of any plug type share all their class code and data. Basically this is both MUCH faster and memory efficient (by a factor of more than 100 times !). **
Lets start with the code:
!sum: make !plug [
valve: make valve [
type: 'sum
process: func [
plug inputs
/local sum value
][
sum: 0
foreach value inputs [
if number? value [
sum: sum + value
]
]
plug/liquid: sum
]
]
]
2. Allocate plugs: | |
Code example to start with
sum-plug: liquify !sum
This creates a new plug of type !sum (the plug type we just defined).
Obviously we want to be able to supply the sum plug with values so it can add them up. Thus we will allocate two new plugs which we will use as containers for those values.
aplug: liquify !plug bplug: liquify !plug
3. Filling plug with data: | |
** A feature of liquid is that we can directly fill any plug with some liquid. This makes it easy to merge normal code with the use of liquid. To make code interact with a liquid network, simply fill any of its plugs with new data. This will make the node behave like an input for your network. **
There are alternative in order to fill a plug with values. Either use the fill function which is available within the liquid module, use the fill method of any plug's valve, or fill it with data directly when being allocated.
here is an example of each method:
; using fill function fill aplug 10 fill bplug 3 ; using valve's fill method aplug/valve/fill aplug 10 bplug/valve/fill bplug 10 ; directly on allocation aplug: liquify/fill !plug 10 bplug: liquify/fill !plug 3
4. Link your nodes | |
This is probably the easiest part of any liquid programming.
Here we'll tell the sum-plug to listen to any change in values coming from aplug and bplug by linking them to it. In liquid speak, we would say that we are now "observing" them.
link sum-plug aplug link sum-plug bplug
For basic usage, its as easy as that.
5. Get a plug's content, (start processing). | |
Again, this is effortless, all you need to do is call the content function and you will get the value of that !plug.
What actually happens, is that at that precise moment, the plug will force its input to compute themselves, and return their data, at which point the node you asked for content can then compute its own value.
When a plug is finished processing, it returns its computed value and you can now use it as you would any value returned by a function.
probe content sum-plug == 13
What must be explained here is that until you actually ask for sum-plug's value, it will never be computed, even if you filled new values into pluga or plugb 100 times.
This is commonly known as lazy evaluation. Furthermore, liquid caches those values by default, so that unless any of your observers changes, you will not have to recompute the value.
Any new call to sum-plug's content will immediately return the previous value (also saving memory, since it returns THE SAME value, if it where a series, for example.
Conclusion | |
So, if we recap the whole application (and add slim linking) we get the following application:
The full code example:
rebol []
slim/open/expose/prefix 'liquid 0.1 [!plug liquify content link] none
; define new !sum class
!sum: make !plug [
valve: make valve [
type: 'sum
process: func [
plug inputs
/local sum value
][
sum: 0
foreach value inputs [
if number? value [
sum: sum + value
]
]
plug/liquid: sum
]
]
]
; allocate our nodes and fill them with data
sum-plug: liquify !sum
aplug: liquify/fill !plug 10
bplug: liquify/fill !plug 3
; link up our plugs so sum has inputs
link sum-plug aplug
link sum-plug bplug
; cause sum to be processed by asking for its content
probe content sum-plug
ask "press enter to quit"
Switching on slim verbosity:
Now if you want to have fun, you should enable verbosity within the liquid module
replace the line:
slim/open/expose/prefix 'liquid 0.1 [!plug liquify content link] none
by:
lqd: slim/open/expose/prefix 'liquid 0.1 [!plug liquify content link] none lqd/von
Here is what you should get as an output.
liquid/sum[1]/init [
liquid/sum[1]/setup [
]
liquid/sum[1]/cleanse [
]
]
liquid/!plug[2]/init [
liquid/!plug[2]/setup [
]
liquid/!plug[2]/cleanse [
]
]
liquid/!plug(2)/fill [
liquid/!plug[2]/dirty [
liquid/!plug[2]/propagate [
]
]
]
liquid/!plug[3]/init [
liquid/!plug[3]/setup [
]
liquid/!plug[3]/cleanse [
]
]
liquid/!plug(3)/fill [
liquid/!plug[3]/dirty [
liquid/!plug[3]/propagate [
]
]
]
liquid/sum(1)/link [
liquid/sum(1)/link? [
true
]
liquid/sum[1]/dirty [
liquid/sum[1]/propagate [
]
]
]
liquid/sum(1)/link [
liquid/sum(1)/link? [
true
]
liquid/sum[1]/dirty [
liquid/sum[1]/propagate [
]
]
]
liquid/sum[1]/cleanup [
liquid/sum[1]/instigate [
liquid/sum[1]/linked? [
(true)
]
liquid/!plug[2]/cleanup [
liquid/!plug(2)/purify [
]
]
liquid/!plug[3]/cleanup [
liquid/!plug(3)/purify [
]
]
]
liquid/sum(1)/filter [
]
liquid/sum(1)/purify [
]
]
13
press enter to quit
As you can see, the value is only computed when we actually call the content method (the 'CONTENT method is an alias for the internal 'CLEANUP method).
Caching anyone?
Now if we where to duplicate the call to 'CONTENT in the code example above (before the ask) and leave the verbosity on, we will notice a powerfull feature of liquid's processing management:
probe content sum-plug probe content sum-plug ask "press enter to quit"
The end of your console session should look like so:
;--8<--8<-- previous output cut for docs 8<--8<--
liquid/sum[1]/cleanup [
liquid/sum[1]/instigate [
liquid/sum[1]/linked? [
(true)
]
liquid/!plug[2]/cleanup [
liquid/!plug(2)/purify [
]
]
liquid/!plug[3]/cleanup [
liquid/!plug(3)/purify [
]
]
]
liquid/sum(1)/filter [
]
liquid/sum(1)/purify [
]
]
13
liquid/sum[1]/cleanup [
]
13
press enter to quit
Hugh? Why is only one function called the second time, when the previous content call provoked NINE!?
Simply because the !plug knows none of its dependencies have changed, thus it understands not to recompute its value, which will not have changed either, obviously!
This demonstrates the gains in speed such an engine can procure when used properly. Network access or time intensive calculations can be prevented, when it is known that they are still up to date!
page last updated: 17-Oct-2006