All Downloads are FREE. Search and download functionalities are using the official Maven repository.

libretto.examples.PingPongN.scala Maven / Gradle / Ivy

The newest version!
package libretto.examples

import libretto.scaletto.StarterApp
import scala.concurrent.duration.*

/**
 * This example implements interaction between two parties, Alice and Bob, in which
 *
 *  - when it's Alice's turn, she can choose to send "ping" to Bob or to quit;
 *  - when it's Bob's turn, he can choose to send "pong" back to Alice, or to quit;
 *  - interaction continues until one of the parties quits.
 *
 * This is an extension of the [[PingPong]] example to multiple rounds of ping-pong.
 * It further demonstrates:
 *
 *  - The choice operators [[|+|]] and [[|+|]].
 *  - Recursive protocol type.
 */
object PingPongN extends StarterApp { app =>
  type PlayF[Play] =
    (Val["ping"] |*| ((Neg["pong"] |*| Play) |&| Done)) |+|
    Done

  type Play = Rec[PlayF]

  def alice: Done -⚬ Play = rec { alice =>
    val play: Done -⚬ (Val["ping"] |*| ((Neg["pong"] |*| Play) |&| Done)) = {
      val receivePong: One -⚬ (Neg["pong"] |*| Play) =
        promise["pong"] > snd(neglect > alice)

      aliceSays("ping") > constVal("ping") > introSnd(choice(receivePong, done))
    }

    val quit: Done -⚬ Done =
      aliceSays("quit")

    randomChoice > Bool.switch(
      caseTrue = play > injectL,
      caseFalse = quit > injectR,
    ) > pack[PlayF]
  }

  def bob: Play -⚬ Done = rec { bob =>
    val play: (Done |*| (Neg["pong"] |*| Play)) -⚬ Done =
      fst(bobSays("pong") > constVal("pong")) > assocRL > elimFst(fulfill["pong"]) > bob

    val receivePing: (Val["ping"] |*| ((Neg["pong"] |*| Play) |&| Done)) -⚬ Done =
      fst(neglect > randomChoice) > Bool.switchWithR(
        caseTrue = snd(chooseL) > play,
        caseFalse = snd(chooseR) > fst(bobSays("quit")) > join,
      )

    unpack > either(receivePing, id[Done])
  }

  private def randomChoice: Done -⚬ Bool =
    delay(500.millis) > constVal(()) > mapVal(_ => randomBoolean(0.9)) > liftBoolean

  private def randomBoolean(pTrue: Double): Boolean =
    scala.util.Random.nextDouble() <= pTrue

  private def aliceSays(msg: String): Done -⚬ Done =
    printLine(Console.MAGENTA + s"Alice: $msg" + Console.RESET)

  private def bobSays(msg: String): Done -⚬ Done =
    printLine(Console.GREEN + s"Bob:   $msg" + Console.RESET)

  override def blueprint: Done -⚬ Done =
    alice > bob
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy