\input texinfo @c -*-texinfo-*- @c %**start of header @setfilename join-python.info @settitle join-python @afourpaper @documentencoding UTF-8 @documentlanguage en @finalout @c %**end of header @dircategory Python @direntry * join-python: (join-python). Join-calculus for Python @end direntry @copying Copyright @copyright{} 2014 Mattias Andrée @quotation Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. A copy of the license is included in the section entitled ``GNU Free Documentation License''. @end quotation @end copying @ifnottex @node Top @top Join Python -- Join-calculus for Python @insertcopying @end ifnottex @titlepage @title Join Python @subtitle Join-calculus for Python @author by Mattias Andrée (maandree) @page @vskip 0pt plus 1filll @insertcopying @end titlepage @contents @menu * Overview:: Brief overview of Join Python. * Signals:: The signal construct. * Fragments:: The fragment join construct. * Join-switches:: Advanced joining techniques. * GNU Free Documentation License:: Copying and sharing this manual. @end menu @node Overview @chapter Overview Join Python is a Python 3 library that implements join-calculus. Join Python can be used by importing the module @code{join} in any Python 3 program, provided that Join Python is installed. Join Python provides the ability to write more concise code using almost any published concurrency pattern with explicit monitor calls. @node Signals @chapter Signals A signal it as function that runs asynchronously. It is put in its own thread. To declare a signal, add the @code{@@signal} decorator to a function definition or pass function to the the constructor of the class @code{signal}. @cartouche @example >>> from join import * >>> import time >>> >>> @@signal >>> def sig(delay, value): >>> time.sleep(delay) >>> print(value) >>> >>> sig(0.25, 'first') >>> print('last') last first @end example @end cartouche @cartouche @example >>> from join import * >>> import time >>> >>> def f(delay, value): >>> time.sleep(delay) >>> print(value) >>> >>> signal(f)(0.25, 'first') >>> print('last') last first @end example @end cartouche As an extension to join-calculus, signals in Join Java can be joined with the returned value of the function can be fetched when joined. @cartouche @example >>> from join import * >>> import time >>> >>> def f(delay, value): >>> time.sleep(delay) >>> return value ** 2 >>> >>> sig = signal(f)(0.25, 2) >>> print('between') >>> print(sig.join()) between 16 @end example @end cartouche In the next chapter fragments will be introduced. If you want a signal without a fragment capability use @code{@@puresignal} or @code{puresignal} instead of @code{@@signal} or @code{signal}. @node Fragments @chapter Fragments A fragment is a partial function. What this means is that you can make functions that block until all its fragments have returned. Signals return immediately. Waiting for a fragment is called joining,@footnote{Yes, it is a bit inconvenient that waiting for a thread or signal is also called joining.} when joining when a fragment you receive the arguments in was invoked with and the value it returned, the latter being an extension to join-calculus. Remember that a signal returns an object with an argumentless method name @code{join} that joins with the signal and returns that value the signal function returned. @cartouche @example >>> from join import * >>> >>> @@fragment >>> def fragment(value): >>> return value ** 2 >>> >>> def f(value): >>> ((fragment_value,), _kwargs, rc) = join(fragment) >>> return rc + fragment_value + value >>> >>> fragment(2) >>> print(f(3)) 10 @end example @end cartouche The function @code{join} returns a tuple of the positional arguments, the named arguments and the returned value. But you can also join with multiple fragemnts, in which case @code{join} returns a list of these tuples, one tuple for each fragment, in the same order as they appear as arguments for the @code{join} call. @cartouche @example >>> from join import * >>> >>> @@fragment >>> def f1(value): >>> return value ** 2 >>> >>> @@fragment >>> def f2(value): >>> return value ** 3 >>> >>> def f(value): >>> ((_args1, _kwargs1, rc1), (_args2, _kwargs2, rc2)) = join(f1, f2) >>> return value + rc1 + rc2 >>> >>> f1(2) >>> f2(2) >>> print(f(2)) 14 @end example @end cartouche As with @code{@@signals}, you can use constructor of @code{fragment} instead of a the @code{@@fragment} decorator. @node Join-switches @chapter Join-switches Join-switches is an advanced joining technique. It lets join join with whatever fragment group that is joined first. There are two versions of this: ordered joining and unordered joining. Ordered joining and unordered joining behaves the same if none of the fragment group have already joined when the joining is requested. But if there are more than one fragment group that have already joined that will behave differently: unordered joining will select a fragment group at random, whereas ordered joining will select the fragment group with highest precedence, that is, the fragment group specified first in the joining request. Ordered joining is done with the function @code{ordered_join} and unordered joining is done with the function @code{unordered_join}. They work similarly to @code{join}, except each group is specified as a tuple of fragments and the returned value is a tuple of the index of the selected join case (fragment group) and what @code{join} returned for that fragment group. @node GNU Free Documentation License @appendix GNU Free Documentation License @include fdl.texinfo @bye