!plug Quickstart

Page Content

Setup
1. Create a new node type:
2. Allocate plugs:
3. Filling plug with data:
4. Link your nodes
5. Get a plug's content, (start processing).
Conclusion
The full code example:
Switching on slim verbosity:
Caching anyone?

Setup

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