Receiving, Where It Shines
The previous section showed the value[variable]
receive command — but it didn’t feel essential.
Let’s now explore a use-case where this command really shines: polling values from an
infinite sequence.
You may remember the Sequence<a>
type from earlier. Here’s the definition again:
type Sequence<a> = iterative choice {
.close => !,
.next => (a) self,
}
A Sequence
can generate an unbounded number of values, one by one, and eventually be closed.
A Fibonacci sequence, as a value
Let’s start by building a sequence of Fibonacci numbers.
dec Fibonacci : Sequence<Nat>
def Fibonacci =
let (a) b = (0) 1
in begin case {
.close => !
.next =>
let (a) b = (b) Nat.Add(a, b)
in (a) loop
}
The internal state of the sequence is a pair (a) b
. On each .next
, we emit a
and update
the pair. This is a clean and elegant use of corecursive iteration, as we’ve covered it in the
section on iterative types.
So far so good — but how do we use this value?
Goal: print the first 30 Fibonacci numbers
Let’s say we want to print the first 30 Fibonacci numbers to the terminal.
That means:
- Repeating an action 30 times,
- Receiving a number from the sequence each time,
- Converting it to a string,
- Sending it to the output.
We’ll need a way to loop exactly 30 times. But there’s a catch: In Par,
looping is only possible on recursive types. There’s no built-in recursion on Nat
, so we need
to convert the number 30 into a recursive.
Good news: there’s a built-in helper for that! It’s called Nat.Repeat
, here’s its type:
dec Nat.Repeat : [Nat] recursive either {
.end!,
.step self,
}
Calling Nat.Repeat(30)
gives us a recursive value that can be stepped through exactly 30 times.
Precisely what we need.
Now for the second part: printing.
Console
output
Par comes with a built-in definition for working with standard output:
def Console.Open : Console
The Console
type itself is defined like this:
type Console = iterative choice {
.close => !,
.print(String) => self,
}
You can think of it as a “sink” object that receives strings and sends them to the terminal.
Much like String.Builder
, but the output appears directly in your console.
Let’s tie it all together!
def Program: ! = do {
let console = Console.Open
let fib = Fibonacci
Nat.Repeat(30).begin.case {
.end! => {}
.step remaining => {
fib.next[n]
console.print(Int.ToString(n))
remaining.loop
}
}
fib.close
console.close
} in !
Here’s what’s happening:
Nat.Repeat(30)
becomes the subject of the.begin
and.case
commands.- In each
.step
, we receive a number from thefib
sequence using thefib.next[n]
command. - We convert it to a string using
Int.ToString(n)
and print it to the console. - Then we
loop
.
The fib.next[n]
line is where receive command truly shines. It receives the payload
of the .next
branch — a number — and updates fib
to the rest of the sequence. Note, that
it’s a combination of two commands: a selection and a receive.
Once done, we close both the sequence and the console. We must, they are linear.