2.4. Defining Sites in Orc

Orc has a special declaration, def class, which allows a site to be written in Orc itself. This is a convenient mechanism for writing sites whose internal behavior is best expressed by an orchestration, which might be an awkward task in Java, Scala, or other similar languages.

A def class resembles a def in its syntax, but not in its behavior. The site defined by def class is a factory which creates instances of the class. Each def within the body of the def class becomes a method of an instance. The scope expression of these declarations becomes the internal computation of the instance; it begins to run when the instance is created, and cannot be killed by the Orc program, exactly as if it were a computation of an external service.

The following two examples demonstrate some of the behavior of def class. For a more comprehensive explanation of def class and its features, see the Reference Manual.

2.4.1. Example: Stack

The following code defines a site Stack, which creates stacks with push and pop methods:

def class Stack() =
  {- The stack is initially empty -}
  val store = Ref([])

  def push(x) = 
    store? >xs> 
    store := x:xs

  {- Note that popping an empty stack simply halts, with no effect -}
  def pop() = 
    store? >h:t> 
    store := t >> 

  {- A stack instance has no ongoing computation -}

{- Test the stack -}
val st = Stack()
st.push(3) >> st.push(5) >> st.pop() >> st.pop()


2.4.2. Example: Multicast

Here is a more complex example, which creates a multicast. Whenever a value is available on the source channel, it is read, and broadcasted to all current listeners. Listeners may be added with the addListener method.

def class Multicast(source) =
  val listeners = Ref([])

  def addListener(f) = 
    listeners? >fs> 
    listeners := f:fs

  {- The ongoing computation of a multicast -}
  repeat(source) >item> each(listeners?) >sink> sink(item)

{- Test the multicast -}

val c = Channel()
val mcast = Multicast(c.get)
val listenerA = Channel()
val listenerB = Channel()

{- At n seconds, broadcast n. Stop at 9 seconds. -} 
  upto(10) >i> Rwait(1000*i) >> c.put(i) >> stop

{- Listener A joins at 1.5 seconds, hearing 2..9 -}
| Rwait(1500) >> mcast.addListener(listenerA.put) >> stop

{- Listener B joins at 6.5 seconds, hearing 7..9 -}
| Rwait(6500) >> mcast.addListener(listenerB.put) >> stop

{- Publish everything that Listener A hears -}
| repeat(listenerA.get) >a> ("A", a)

{- Publish everything that Listener B hears -}
| repeat(listenerB.get) >b> ("B", b)

{- Shortly after 10 seconds, close down the channels -}
| Rwait(10500) >> 
    listenerA.close() >> 
    listenerB.close() >> 
    c.close() >>

("A", 2)
("A", 3)
("A", 4)
("A", 5)
("A", 6)
("A", 7)
("B", 7)
("A", 8)
("B", 8)
("A", 9)
("B", 9)