Skip to content

Latest commit

 

History

History
150 lines (107 loc) · 4.84 KB

intro.rst

File metadata and controls

150 lines (107 loc) · 4.84 KB

Getting Started

Most of :pychainlet should feel familiar if you are used to Python. This guide covers the two key concepts you need to learn:

  • Defining a chainlink to perform a single transformation
  • Chaining several chainlinks to perform multiple transformations

A simple way to create a chainlink is to promote a function using :py~chainlet.funclet. Using a :py~chainlet.funclet is suitable whenever you can directly map input to output.

Simply decorate a regular function, which

  • takes at least one positional argument value, and
  • produces the desired result via return.
>>> from chainlet import funclet
>>>
>>> @funclet  # <== chainlet specific
... def multiply(value, by=4):
...    """Multiply all input"""
...    return value * by

There is practically no restriction by :pychainlet on what the wrapped function can do. While it has its uses, it is generally good practice to avoid changing global state, though.

By applying :py~chainlet.funclet, we have created a new type of :pylink. To use it, you must instantiate it, which allows you to fill in all parameters.

>>> chain = multiply()  # use default `by=4`
>>> chain = multiply(3)
>>> chain = multiply(by=3)  # equivalent to the above

Note that value is automatically excluded: You can use both positional and keyword parameters in a :py~chainlet.funclet.

To get some actual output, you have to feed it input. As with a generator, you can :py~chainlet.ChainLink.send individual values:

>>> chain.send(1)
3
>>> chain.send(3)
9
>>> chain.send('a')
'aaa'

You can also bulk-process values by providing an iterable to :py~chainlet.ChainLink.dispatch. This provides a lazily evaluated generator:

>>> for result in chain.dispatch(range(3)):
...     print(result)
0
3
6

Dispatching is especially useful with :pychainlet.concurrency, which computes results in parallel.

Any chainlink can be composed with others to form a chain. This is equivalent to feeding the result of each chainlink to the next1.

>>> chain_by12 = multiply(by=3) >> multiply(by=4)  # same result as `multiply(by=12)`

A chain can be used the same way as a single chainlink. You can apply the same operations to send or dispatch input along a chain:

>>> chain_by12.send(1)
12
>>> chain_by12.send(3)
36
>>> chain_by12.send('a')
'aaaaaaaaaaaa'

Notably, chains can also be chained with other chains and chainlinks. This creates a new chain, containing the individual links of each:

>>> chain_by24 = chain_by12 >> multiply(by=2)  # same as multiply(by=3) >> multiply(by=4) >> multiply(by=2)
>>> list(chain_by24.dispatch(range(5)))
[0, 24, 48, 72, 96]

Epilogue: Pulling your chain

You can not just push input to a chainlet, but also pull from it. This requires a chainlink that returns data when receiving value=None2:

>>> import random
>>>
>>> @funclet
... def generate(value, maximum=4):
...    """Generate values"""
...    if value is None:  # indicator that a new value is desired
...        return random.random() * maximum
...    return min(value, maximum)  # chainlets may provide both transformation and generation

Such a producer can be linked into a chain the same way as other elements. The resulting chain will produce values by itself if you send(None) to it:

>>> rand24 = generate(maximum=1) >> chain_by24
>>> rand24.send(1)  # use explicit starting value
24
>>> rand24.send(None)  # use generated starting value
12.013380549968177

On top of the explicit send(None), such a chain also supports regular iteration. You can iter over its values, and get the next value:

>>> next(rand24)
3.6175271892905103
>>> for count, result in enumerate(rand24):  # implicitly uses iter(rand24)
...     print(count, ':', result)
...     if result > 12:
...        break
0 : 10.786272495589447
1 : 23.653323415316734

  1. Depending on the elements used, chainlet will not actually execute this. It merely provides the same result.

  2. This replicates the generator interface, where next(gen) is equivalent to gen.send(None). See the Generator-Iterator Methods.