MultiChor: Type-safe and efficient choreographies with location-set polymorphism.

This is a package candidate release! Here you can preview how this package release will appear once published to the main package index (which can be accomplished via the 'maintain' link below). Please note that once a package has been published to the main package index it cannot be undone! Please consult the package uploading documentation for more information.

[maintain] [Publish]

MultiChor is a library for functional choreographic programming in Haskell.


[Skip to Readme]

Properties

Versions 1.0.0.0, 1.0.1.0, 1.0.1.1, 1.1.0.0, 1.1.0.0
Change log None available
Dependencies base (>=4.16 && <5), bytestring (>=0.11 && <0.13), http-client (>=0.7 && <0.8), mtl (>=2.2.2 && <3.0), servant (>=0.19 && <0.21), servant-client (>=0.19 && <0.21), servant-server (>=0.19 && <0.21), template-haskell (>=2.18 && <2.23), unordered-containers (>=0.2 && <0.3), warp (>=3.3 && <3.4) [details]
License BSD-3-Clause[multiple license files]
Author
Maintainer mako.bates@uvm.edu
Category Distributed-Computing
Source repo head: git clone https://github.com/ShapeOfMatter/MultiChor
Uploaded by ShapeOfMatter at 2025-04-07T01:38:34Z

Modules

[Index] [Quick Jump]

Downloads

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees


Readme for MultiChor-1.1.0.0

[back to package description]

MultiChor

MultiChor is a library for writing choreographic programs in Haskell.

That means you write one program, a "choreography" which seamlessly describes the actions of many communicating machines; these participants can be native Haskell threads, or various humans' laptops communicating over HTTPS, or anything in between. Each of these "endpoints" will "project" their behavior out of the choreography.

Choreographies aren't just easier to write than distributed programs, they're automatically deadlock-free!

MultiChor uses some of the same conventions and internal machinery as HasChor, but the API is incompatible and can express more kinds of choreographic behavior.

Examples

Consider the choreography game:

{- A simple black-jack-style game. The dealer gives everyone a card, face up. Each player may
 - request a second card. Then the dealer reveals one more card that applies to everyone. Each
 - player individually wins if the sum of their cards (modulo 21) is greater than 19.  -}
game :: forall players m. (KnownSymbols players) => Choreo ("dealer" ': players) (CLI m) ()
game = do
  let players = consSuper (refl @players)
      dealer = listedFirst @"dealer"
      everyone = refl @("dealer" ': players)
  hand1 <-
    ( fanIn everyone \(player :: Member player players) -> do
        card1 <- locally dealer (\_ -> getInput ("Enter random card for " ++ toLocTm player))
        (dealer, card1) ~> everyone
      )
      >>= naked everyone
  wantsNextCard <- parallel players \_ _ -> do
    putNote $ "All cards on the table: " ++ show hand1
    getInput "I'll ask for another? [True/False]"
  hand2 <- fanOut \(player :: Member player players) ->
    conclave (inSuper players player @@ dealer @@ nobody) do
      let dealer' = listedSecond @"dealer"
      choice <- broadcast (listedFirst @player, localize player wantsNextCard)
      if choice
        then do
          cd2 <- locally dealer' (\_ -> getInput (toLocTm player ++ "'s second card:"))
          card2 <- broadcast (dealer', cd2)
          return [getLeaf hand1 player, card2]
        else return [getLeaf hand1 player]
  tblCrd <- locally dealer (\_ -> getInput "Enter a single card for everyone:")
  tableCard <- (dealer, tblCrd) ~> players
  void $ parallel players \player un -> do
    let hand = un player tableCard : viewFacet un player hand2
    putNote $ "My hand: " ++ show hand
    putOutput "My win result:" $ sum hand > card 19

The type signature tells us that this choreography (computation in the Choreo monad) involves a partially-polymorphic list of participants; the first of whom is specifically "dealer" and the rest of whom are represented collectively in the type variable players. All parties can perform computations in a local monad, in this case CLI m (the details of CLI are orthogonal to choreographic programming; it's basically just IO). The return type () indicates that this choreography doesn't yield any values; it's just a program. The first thing we do is declare the values players, dealer, and everyone; these are term-level identifiers for the type level parties, but they're also proofs of subset or membership that attest that the respective parties are valid to participate in the choreography. hand1 has type Quire players Card; since these cards are supposed to be dealt face down, they're known to everyone. To accomplish this, we use a fanIn loop in which the dealer selects a card for each player and sends it to everyone. wantsNextCard is the result of a parallel computation (CLI interaction); it's type is Faceted players '[] Bool. The computation of hand2 is private between each player and the dealer, so we fanOut over the players and then conclave a sub-choreography involving only the given player and "dealer". The broadcast inside the conclave shares that player's value of wantsNextCard with everyone who's present, which is just "dealer". At the end, the players each observer if they've won or lost in parallel.

Check the source repository for many more example choreographies. You can also read the preprint of Efficient, Portable, Census-Polymorphic Choreographic Programming for more theoretical discussion of choreographic programming à la MultiChor.