There are many uses of the Join-patterns with different languages. Some languages use join-patterns as a base of theirs implementations, for example the
Polyphonic C# or MC# but others languages integrate join-pattern by a library like Scala Joins for Scala or the Joins library for VB. Moreover, the join-pattern is used through some languages like
Scheme to upgrade the join-pattern.
Join Java Join Java is a language based on the
Java programming language allowing the use of the join calculus. It introduces three new language constructs: •
Join methods is defined by two or more Join fragments. A Join method will execute once all the fragments of the Join pattern have been called. If the return type is a standard Java type then the leading fragment will block the caller until the Join pattern is complete and the method has executed. If the return type is of type signal then the leading fragment will return immediately. All trailing fragments are asynchronous so will not block the caller. Example: class JoinExample { int fragment1() & fragment2(int x) { // Will return value of x to caller of fragment1 return x; } } •
Asynchronous methods are defined by using the signal return type. This has the same characteristics as the void type except that the method will return immediately. When an asynchronous method is called a new thread is created to execute the body of the method. Example: class ThreadExample { signal thread(SomeObject x) { // This code will execute in a new thread } } •
Ordering modifiers Join fragments can be repeated in multiple Join patterns so there can be a case when multiple Join patterns are completed when a fragment is called. Such a case could occur in the example below if B(), C() and D() then A() are called. The final A() fragment completes three of the patterns so there are three possible methods that may be called. The ordered class modifier is used here to determine which Join method will be called. The default and when using the unordered class modifier is to pick one of the methods at random. With the ordered modifier the methods are prioritised according to the order they are declared. Example: class ordered SimpleJoinPattern { void A() & B() { } void A() & C() { } void A() & D() { } signal D() & E() { } } The closest related language is the
Polyphonic C#.
JErlang In
Erlang coding synchronisation between multiple processes is not straightforward. That's why the JErlang, an extension of
Erlang was created, The J is for Join. Indeed, To overcome this limitation JErlang was implemented, a
Join-Calculus inspired extension to
Erlang. The features of this language are: •
Joins allows first Match semantics and the possibility of having multiple patterns with a preservation of the messages's order. operation() -> receive {ok, sum} and {val, X} and {val, Y} -> {sum, X + Y}; {ok, mult} and {val, X} and {val, Y} -> {mult, X * Y}; {ok, sub} and {val, X} and {val, Y} -> {sub, X - Y}; end end •
Guards provides additional filtering not expressing in terms of patterns. Limited number of expression without side-effects receive {Transaction, M} and {limit, Lower, Upper} when (Lower commit_transaction(M, Transaction) end • With
Non-linear patterns, messages can match multiple joins receive {get, X} and {set, X} -> {found, 2, X} end ... receive {Pin, id} and {auth, Pin} and {commit, Id} -> perform_transaction(Pin, Id) end •
propagation allows for copying correct messages instead of removing them. receive prop({session, Id}) and {act, Action, Id} -> perform_action(Action, Id); {session, Id} and {logout, Id} -> logout_user(Id) end ... receive {Pin, id} and {auth, Pin} and {commit, Id} -> perform_transaction(Pin, Id) end •
Synchronous calls receive {accept, Pid1} and {asynchronous, Value} and {accept, Pid2} -> Pid1 ! {ok, Value}, Pid2 ! {ok, Value} end
C++ Yigong Liu has written some classes for the join pattern including all useful tools like
asynchronous and synchronous channels,
chords, etc. It was apparently integrated into
Boost however does not seem to exist as of 2025. import std; template class Buffer: public Joint { public: Async put; Synchronous get; Buffer() { chord(get, put, &Buffer::chordBody); } V chordBody(void_t g, V p) { return p; } }; This example demonstrates a thread safe buffer and message queue with the basic operations put and get.
C# Polyphonic C# Polyphonic C# is an extension of the C# programming language. It introduces a new concurrency model with synchronous and asynchronous (which return control to the caller) methods and chords (also known as ‘synchronization patterns’ or ‘join patterns’). public class Buffer { public String get() & public async put(String s) { return s; } } This is a simple buffer example.
MC# MC# language is an adaptation of the Polyphonic C# language for the case of concurrent distributed computations. public handler Get2 long () & channel c1 (long x) & channel c2 (long y) { return (x + y); } This example demonstrates the using of chords as a synchronization tool.
Parallel C# Parallel C# is based Polyphonic C# and they add some new concepts like movables methods, high-order functions. using System; class Test13 { int Receive() & async Send(int x) { return x * x; } public static void Main(string[] args) { Test13 t = new Test13(); t.Send(2); Console.WriteLine(t.Receive()); } } This example demonstrates how to use joins.
Cω Cω adds new language features to support
concurrent programming (based on the earlier
Polyphonic C#). The Joins Concurrency Library for C# and other .NET languages is derived of this project.
Scalable Join Patterns It is an easy-to-use declarative and scalable join-pattern library. In opposite to the Russo library, use three improvements for the join-pattern : • Stealing message for unused resources (allowing barging); • Lazy queue saves both on allocation and potentially on interprocessor communication by avoiding allocate or enqueue with an optimistic fast-path; • A status "WOKEN" : ensures that a blocked synchronous caller is woken only once.
JoCaml JoCaml is the first language where the join-pattern was implemented. Indeed, at the beginning all the different implementation was compiled with the JoCaml Compiler. JoCaml language is an extension of the
OCaml language. It extends OCaml with support for concurrency and synchronization, the distributed execution of programs, and the dynamic relocation of active program fragments during execution. type coins = Nickel | Dime and drinks = Coffee | Tea and buttons = BCoffee | BTea | BCancel;; (* def defines a Join-pattern set clause * "&" in the left side of = means join (channel synchronism) * "&" in the right hand side means: parallel process * synchronous_reply :== "reply" [x] "to" channel_name * synchronous channels have function-like types (`a -> `b) * asynchronous channels have types (`a Join.chan) * only the last statement in a pattern rhs expression can be an asynchronous message * 0 in an asynchronous message position means STOP ("no sent message" in CSP terminology). *) def put(s) = print_endline s ; 0 (* STOP *) ;; (* put: string Join.chan *) def serve(drink) = match drink with Coffee -> put("Cofee") | Tea -> put("Tea") ;; (* serve: drinks Join.chan *) def refund(v) = let s = Printf.sprintf "Refund %d" v in put(s) ;; (* refund: int Join.chan *) let new_vending serve refund = let vend (cost:int) (credit:int) = if credit >= cost then (true, credit - cost) else (false, credit) in def coin(Nickel) & value(v) = value(v+5) & reply () to coin or coin(Dime) & value(v) = value(v+10) & reply () to coin or button(BCoffee) & value(v) = let should_serve, remainder = vend 10 v in (if should_serve then serve(Coffee) else 0 (* STOP *)) & value(remainder) & reply () to button or button(BTea) & value(v) = let should_serve, remainder = vend 5 v in (if should_serve then serve(Tea) else 0 (* STOP *)) & value(remainder) & reply () to button or button(BCancel) & value(v) = refund( v) & value(0) & reply () to button in spawn value(0) ; coin, button (* coin, button: int -> unit *) ;; (* new_vending: drink Join.chan -> int Join.chan -> (int->unit)*(int->unit) *) let ccoin, cbutton = new_vending serve refund in ccoin(Nickel); ccoin(Nickel); ccoin(Dime); Unix.sleep(1); cbutton(BCoffee); Unix.sleep(1); cbutton(BTea); Unix.sleep(1); cbutton(BCancel); Unix.sleep(1) (* let the last message show up *) ;; gives Coffee Tea Refund 5
Hume Hume is a
strict,
strongly typed functional language for limited resources platforms, with concurrency based on asynchronous message passing,
dataflow programming, and a
Haskell like syntax. Hume does not provide synchronous messaging. It wraps a join-pattern set with a channel in common as a
box, listing all channels in an
in tuple and specifying all possible outputs in an
out tuple. Every join-pattern in the set must conform to the
box input tuple type specifying a '*' for non required channels, giving an expression whose type conform to the output tuple, marking '*' the non fed outputs. A
wire clause specifies • a tuple of corresponding input origins or sources and optionally start values • a tuple of output destinations, being channels or sinks (stdout, ..). A
box can specify exception handlers with expressions conforming to the output tuple. data Coins = Nickel | Dime; data Drinks = Coffee | Tea; data Buttons = BCoffee | BTea | BCancel; type Int = int 32 ; type String = string ; show u = u as string ; box coffee in ( coin :: Coins, button :: Buttons, value :: Int ) -- input channels out ( drink_outp :: String, value’ :: Int, refund_outp :: String) -- named outputs match -- * wildcards for unfilled outputs, and unconsumed inputs ( Nickel, *, v) -> ( *, v + 5, *) in ( *, 0, refund v) ; vend drink cost credit = if credit >= cost then ( serve drink, credit - cost, *) else ( *, credit, *); serve drink = case drink of Coffee -> "Cofee\n" Tea -> "Tea\n" ; box control in (c :: char) out (coin :: Coins, button:: Buttons) match 'n' -> (Nickel, *) | 'd' -> (Dime, *) | 'c' -> (*, BCoffee) | 't' -> (*, BTea) | 'x' -> (*, BCancel) | _ -> (*, *) ; stream console_outp to "std_out" ; stream console_inp from "std_in" ; -- dataflow wiring wire cofee -- inputs (channel origins) (control.coin, control.button, coffee.value’ initially 0) -- outputs destinations (console_outp, coffee.value, console_outp) ; wire control (console_inp) (coffee.coin, coffee.button) ;
Visual Basic Concurrent Basic – CB An extension of Visual Basic 9.0 with asynchronous concurrency constructs, called Concurrent Basic (for short CB), offer the join patterns. CB (builds on earlier work on Polyphonic C#, Cω and the Joins Library) adopts a simple event-like syntax familiar to VB programmers, allows one to declare generic concurrency abstractions and provides more natural support for inheritance, enabling a subclass to augment the set of patterns. CB class can declare method to execute
when communication has occurred on a particular set of local channels asynchronous and synchronous, forming a join pattern.
Joins library (C# and VB) This library is a high-level abstractions of the Join Pattern using objects and generics. Channels are special delegate values from some common Join object (instead of methods). using Microsoft.Research.Joins; class Buffer { public readonly Asynchronous.Channel Put; public readonly Synchronous.Channel Get; public Buffer() { Join join = Join.Create(); join.Initialize(out Put); join.Initialize(out Get); join.When(Get).And(Put).Do(delegate(string s) { return s; }); } } This example shows how to use methods of the Join object.
Scala The Scala Joins library uses the Join-Pattern. The
pattern matching facilities of this language have been generalized to allow representation independence for objects used in pattern matching. So now it's possible to use a new type of abstraction in libraries. The advantage of join patterns is that they allow a declarative specification of the synchronization between different threads. Often, the join patterns corresponds closely to a finite state machine that specifies the valid states of the object. In Scala, it's possible to solve many problem with the pattern matching and Scala Joins, for example the Reader-Writer. with the join pattern. For example, an unbounded buffer: val Put = new Join1[Int] val Get = new Join class Buffer extends JoinActor { def act() { receive { case Get() & Put(x) => Get reply x } } } Scala Join and Chymyst are newer implementations of the Join Pattern, improving upon Dr. Philipp Haller's Scala Joins.
Haskell Join Language is an implementation of the Join Pattern in Haskell.
Scheme The Join Patterns allows a new programming type especially for the multi-core architectures available in many programming situations with a high-levels of abstraction. This is based on the Guards and Propagation. So an example of this innovation has been implemented in Scheme . Guards is essential to guarantee that only data with a matching key is updated/retrieved. Propagation can cancel an item, reads its contents and puts backs an item into a store. Of course, the item is also in the store during the reading. The guards is expressed with shared variables. And so the novelty is that the join pattern can contains now propagated and simplified parts. So in Scheme, the part before / is propagated and the part after / is removed. The use of the Goal-Based is to divise the work in many tasks and joins all results at the end with the join pattern. A system named "MiniJoin" has implemented to use the intermediate result to solve the others tasks if it's possible. If is not possible it waits the solution of others tasks to solve itself. So the concurrent join pattern application executed in parallel on a multi-core architecture doesn't guarantee that parallel execution lead to conflicts. To Guarantee this and a high degree of parallelism, a Software Transactional Memory (STM) within a highlytuned concurrent data structure based on atomic compare-and-swap (CAS) is use. This allows to run many concurrents operations in parallel on multi-core architecture. Moreover, an atomic execution is used to prevent the "false conflict" between CAS and STM. == Other similar design patterns ==