Skip to content

Latest commit

 

History

History
4321 lines (3541 loc) · 58.2 KB

File metadata and controls

4321 lines (3541 loc) · 58.2 KB

-- Bee-3 by example

Every program here runs, and the output shown is what it produces. Generated by tools/build_examples_md.py from the same catalogue the playground ships, with outputs taken from the recorded expectations the test suite re-checks — so this file cannot drift from the language.

157 short examples in 17 groups, 18 whole programs, and 26 traps that each demonstrate a safety guarantee firing.

Open playground.html to edit and run any of them in a browser.

Contents


Basics

Hello world

rule main:
  print "Hello World";
return;
Hello World

print and write

rule main:
  write "no";
  write " newline";
  print;
  print "then one";
return;
no newline
then one

Several arguments

rule main:
  print (1, 2, 3);
  print (1, 2, sep:" ");
return;
1,2,3
1 2

Declaring a variable

rule main:
  new a ∈ Z;
  let a := 42;
  print a;
return;
42

Initial values

rule main:
  new a: 1, b: 2 ∈ Z;
  print (a, b);
return;
1,2

Type inference

rule main:
  new a := 10;
  new r := 0.5;
  new s := "text";
  new f := True;
  print (kind(a), kind(r), kind(s), kind(f));
return;
Z,R,S,B

Constants

set LIMIT: 10 ∈ Z;
rule main:
  print LIMIT;
return;
10

Zero values

rule zeros() => (z ∈ Z, r ∈ R, f ∈ B):
  -- a result starts at its type's zero and needs no assignment
  expect True;
return;
rule main:
  new a, b, c := zeros();
  print (a, b, c);
return;
0,0.0,0B0

Comments

-- a whole-line comment
-- another one
rule main:
  print 1;   -- trailing
return;
1

Parallel assignment

rule main:
  new a: 1, b: 2 ∈ Z;
  let a, b := b, a;
  print (a, b);
return;
2,1

Ending early

rule main:
  print "before";
  over;
  print "never";
return;
before

Collections

Arrays

rule main:
  new a := [1,2,3];
  print a;
  print a[0];
  print a[-1];
return;
[1,2,3]
1
3

A sized array

rule main:
  new a ∈ [Z](5);
  let a[*] := 7;
  print a;
return;
[7,7,7,7,7]

Slices share storage

rule main:
  new a := [1,2,3,4,5];
  new w := a[0..2];
  let w[*] := 0;
  print a;
return;
[0,0,0,4,5]

Lists

rule main:
  new l := (1,2,3);
  print l;
  print (l.head, l.tail);
return;
(1,2,3)
1,3

Growing and shrinking a list

rule main:
  new l := (2,3);
  let l <+ 4;
  let l +> 1;
  print l;
  let l << 1;
  let l >> 1;
  print l;
return;
(1,2,3,4)
(2,3)

Sets are sorted and unique

rule main:
  print {3,1,2,1};
return;
{1,2,3}

Set algebra

rule main:
  print {1,2} ∪ {2,3};
  print {1,2,3} ∩ {2,3};
  print {1,2,3} Δ {2,3,4};
  print {1,2} ⊂ {1,2,3};
return;
{1,2,3}
{2,3}
{1,4}
0B1

Set algebra in ASCII

rule main:
  print {1,2} union {2,3};
  print {1,2,3} inter {2,3};
  print {1,2} subset {1,2,3};
return;
{1,2,3}
{2,3}
0B1

Adding and removing

rule main:
  new s := {1,2};
  let s += 3;
  let s -= 1;
  print s;
  print 3 ∈ s;
return;
{2,3}
0B1

Maps

rule main:
  new m := {1:"a", 2:"b"};
  let m[3] := "c";
  print m;
  print m[2];
  scrap m[1];
  print m;
return;
{(1:a),(2:b),(3:c)}
b
{(2:b),(3:c)}

Walking a map

rule main:
  new m := {1:"a", 2:"b"};
  for k, v ∈ m do
    write k + "=" + v + " ";
  repeat;
  print;
return;
1=a 2=b 

Builders

rule main:
  print { x | x ∈ (1..6) ∧ (x % 2 = 0) };
  print { x² | x ∈ (1..4) };
  print [ x | x ∈ (1..9:2) ];
return;
{2,4,6}
{1,4,9,16}
[1,3,5,7,9]

A map builder

rule main:
  print { (x:x²) | x ∈ (1..4) };
return;
{(1:1),(2:4),(3:9),(4:16)}

Quantifiers

rule main:
  print ∀ (i ∈ {2,4,6}) ∧ (i % 2 = 0);
  print ∃ (i ∈ {1,3,5}) ∧ (i = 3);
return;
0B1
0B1

Sharing and copying

rule main:
  new a := [1,2];
  new shared := a;
  new copied :: a;
  let a[0] := 9;
  print (shared[0], copied[0]);
return;
9,1

Concatenation

rule main:
  print [1,2] + [3];
  new l := (1,2);
  new m := (3,);
  print l + m;
return;
[1,2,3]
(1,2,3)

Concurrency

A coroutine

rule ticker(n ∈ N) => (v ∈ N):
  cycle:
    new i ∈ N;
  for i ∈ (1..n) do
    let v := i;
    yield;
  repeat;
  let v := 0;
return;
rule main:
  begin ticker(4);
  cycle:
    new r ∈ N;
  do
    yield r << ticker;
    write r;
  repeat if r > 0;
  print;
return;
12340

Two coroutines taking turns

rule odds(n ∈ N) => (v ∈ N):
  cycle:
    new i ∈ N;
  for i ∈ (1..n) do
    let v := i * 2 - 1;
    yield;
  repeat;
  let v := 0;
return;
rule evens(n ∈ N) => (v ∈ N):
  cycle:
    new i ∈ N;
  for i ∈ (1..n) do
    let v := i * 2;
    yield;
  repeat;
  let v := 0;
return;
rule main:
  begin odds(3);
  begin evens(3);
  cycle:
    new a ∈ N;
    new b ∈ N;
  do
    yield a << odds;
    yield b << evens;
    write a + "/" + b + " ";
  repeat if a > 0;
  print;
return;
1/2 3/4 5/6 0/0 

Deferred jobs

rule scaled(x ∈ Z) => (r ∈ Z):
  let r := x * 10;
return;
rule main:
  new got ∈ (Z);
  begin got <+ scaled(1);
  begin got <+ scaled(2);
  begin got <+ scaled(3);
  wait;
  print got;
return;
(10,20,30)

Map and reduce

rule sum(a, b ∈ Z) => (r ∈ Z):
  cycle:
    new i ∈ Z;
  for i ∈ (a..b) do
    let r += i;
  repeat;
return;
rule main:
  new parts ∈ (Z);
  cycle:
    new lo ∈ Z;
  for lo ∈ (1..76:25) do
    begin parts <+ sum(lo, lo + 24);
  repeat;
  wait;
  new total ∈ Z;
  for p ∈ parts do
    let total += p;
  repeat;
  print total;
return;
5050

An independent loop

rule main:
  new squares ∈ [Z](6);
  for ∀ i ∈ (0.!6) do
    let squares[i] := i * i;
  repeat;
  print squares;
return;
[0,1,4,9,16,25]

Contracts

A precondition

rule half(n ∈ Z) => (r ∈ Z):
require n % 2 = 0;
  let r := n / 2;
return;
rule main:
  print half(8);
return;
4

A postcondition

rule bump(n ∈ Z) => (r ∈ Z):
ensure r > n;
  let r := n + 1;
return;
rule main:
  print bump(1);
return;
2

Both, naming results and parameters

rule clamp(v, lo, hi ∈ Z) => (r ∈ Z):
require lo ≤ hi;
ensure lo ≤ r ≤ hi;
  let r := v;
  if v < lo do
    let r := lo;
  else if v > hi do
    let r := hi;
  done;
return;
rule main:
  print (clamp(-5,0,10), clamp(5,0,10), clamp(99,0,10));
return;
0,5,10

A trait's promise is inherited

trait Sized:
  rule size() => (s ∈ Z)
  ensure s > 0;
done;
rule Box(n ∈ Z) => (self ∈ Box <: Sized):
  new self.n := n;
  rule .size() => (s ∈ Z):
    let s := self.n;
  return;
return;
rule main:
  print Box(4).size();
return;
4

A rule that writes through its argument

rule bump(n ∈ @Z):
  let n += 1;
return;
rule main:
  new counter: 10 ∈ Z;
  apply bump(@counter);
  print counter;
return;
11

A promise about what changed

rule bump(n ∈ @Z):
ensure n = old n + 1;
  let n += 1;
return;
rule main:
  new counter: 10 ∈ Z;
  apply bump(@counter);
  print counter;
return;
11

old works on a collection too

rule grow(items ∈ [Z], by ∈ Z):
require by ≥ 0;
ensure items.length = old items.length + by;
  let items ++ by;
return;
rule main:
  new xs ∈ [Z](2);
  print xs.length;
  apply grow(xs, 3);
  print xs.length;
return;
2
5

Exact money, with a promise it went up

rule deposit(balance ∈ @Q, amount ∈ Q):
require amount > 0;
ensure balance > old balance;
  let balance += amount;
return;
rule main:
  new balance: 10 ∈ Q;
  apply deposit(@balance, 1\4);
  print balance;
return;
10.25

Control

if and else

rule main:
  new n := 7;
  if n > 10 do
    print "big";
  else
    print "small";
  done;
return;
small

An else-if ladder

rule main:
  new n := 0;
  if n > 0 do
    print "positive";
  else if n < 0 do
    print "negative";
  else
    print "zero";
  done;
return;
zero

A scoped block

rule main:
  start:
    new hidden := 1;
  do
    print hidden;
  done;
return;
1

cycle with a condition

rule main:
  new n := 3;
  cycle:
  do
    write n;
    let n -= 1;
  repeat if n > 0;
  print;
return;
321

while

rule main:
  new n := 0;
  cycle:
  while n < 3 do
    write n;
    let n += 1;
  repeat;
  print;
return;
012

then runs once at the end

rule main:
  new n := 0;
  cycle:
  while n < 3 do
    let n += 1;
  then
    print "finished at " + n;
  repeat;
return;
finished at 3

stop and next

rule main:
  for i ∈ (1..10) do
    next if i % 2 = 0;
    stop if i > 7;
    write i;
  repeat;
  print;
return;
1357

Labels

rule main:
  cycle outer:
    new i ∈ Z;
  for i ∈ (1..3) do
    for j ∈ (1..3) do
      stop outer if i * j > 4;
      write i * j;
    repeat;
  repeat;
  print;
return;
12324

match, first hit

rule main:
  match 2:
  when 1 do
    print "one";
  when 2, 3 do
    print "two or three";
  other
    print "other";
  done;
return;
two or three

match, every hit

rule main:
  match all 4:
  when 4 do
    print "exactly four";
  when (0..9) do
    print "a digit";
  done;
return;
exactly four
a digit

A total match needs no default

type Small: (0..3) <: Z;
rule main:
  new v: 2 ∈ Small;
  match v:
  when 0, 1 do
    print "low";
  when (2..3) do
    print "high";
  done;
return;
high

Errors

expect

rule main:
  new n := 4;
  expect n > 0;
  print "held";
return;
held

A trial that succeeds

rule main:
  trial:
  try:
    print "first";
  try:
    print "second";
  final
    print "always";
  done;
return;
first
second
always

Recovering and carrying on

rule main:
  trial:
  try:
    print "a";
  try:
    raise 300, "boom";
  try:
    print "c";
  case $error.code = 300 do
    print "handled " + $error.message;
    resume;
  done;
return;
a
handled boom
c

Retrying

rule main:
  trial:
    new tries: 0 ∈ Z;
  try:
    let tries += 1;
    raise 400, "flaky" if tries < 3;
    print "ok after " + tries;
  case $error.code = 400 do
    retry;
  done;
return;
ok after 3

miss catches the rest

rule main:
  trial:
  try:
    raise 999, "unknown";
  case $error.code = 1 do
    print "wrong";
  miss
    print "missed " + $error.code;
  done;
return;
missed 999

pass, fail and abort

rule main:
  trial:
  try:
    print "a";
    pass;
    print "skipped";
  try:
    fail 500, "noted";
  try:
    print "code is " + $error.code;
  final
    print "done";
  done;
return;
a
code is 500
done

Generics

One rule, any element type

rule first_of[T](items ∈ [T]) => (r ∈ T):
  let r := items[0];
return;
rule main:
  print first_of([1,2,3]);
  print first_of(["alpha","beta"]);
  print first_of([1.5, 2.5]);
return;
1
alpha
1.5

The result keeps the type

rule first_of[T](items ∈ [T]) => (r ∈ T):
  let r := items[0];
return;
rule main:
  new n := first_of([10,20]);
  new s := first_of(["x"]);
  print (kind(n), kind(s));
return;
Z,S

A variable used more than once

rule same[T](a, b ∈ T) => (r ∈ B):
  let r := a = b;
return;
rule main:
  print same(1, 1);
  print same("x", "y");
  print same(1.5, 1.5);
return;
0B1
0B0
0B1

A variable in the result

rule pair[T](a, b ∈ T) => (r ∈ [T]):
  let r := [a, b];
return;
rule main:
  print pair(7, 8);
  print pair("l", "r");
return;
[7,8]
[l,r]

Two parameters at once

rule label[K, V](key ∈ K, value ∈ V) => (r ∈ S):
  let r := key + "=" + value;
return;
rule main:
  print label("count", 3);
  print label(1, "one");
return;
count=3
1=one

Counting occurrences

rule occurrences[T](items ∈ [T], wanted ∈ T) => (n ∈ Z):
  cycle:
    new item ∈ T;
  for item ∈ items do
    let n += 1 if item = wanted;
  repeat;
return;
rule main:
  print occurrences([1,2,2,3], 2);
  print occurrences(["a","b","a"], "a");
return;
2
2

Lists work too

rule head_of[T](items ∈ (T)) => (r ∈ T):
  let r := items.head;
return;
rule main:
  print head_of((1,2,3));
  print head_of(("a","b"));
return;
1
a

A generic rule calling another

rule first_of[T](items ∈ [T]) => (r ∈ T):
  let r := items[0];
return;
rule twice[T](items ∈ [T]) => (r ∈ [T]):
  let r := [first_of(items), first_of(items)];
return;
rule main:
  print twice([9,8]);
  print twice(["q"]);
return;
[9,9]
[q,q]

A bound says what T provides

trait Sized:
  rule size() => (n ∈ Z);
done;
rule Box(width ∈ Z) => (self ∈ Box <: Sized):
  new self.width := width;
  rule .size() => (n ∈ Z):
    let n := self.width;
  return;
return;
rule bigger[T <: Sized](thing ∈ T) => (n ∈ Z):
  let n := thing.size() * 2;
return;
rule main:
  print bigger(Box(21));
return;
42

Swapping a pair

rule swapped[T](a, b ∈ T) => (x, y ∈ T):
  let x := b;
  let y := a;
return;
rule main:
  new p, q := swapped(1, 2);
  print (p, q);
  new r, s := swapped("left", "right");
  print (r, s);
return;
2,1
right,left

Library

Absolute value, whatever the type

rule main:
  print (abs(-3), abs(3), abs(-2.5));
return;
3,3,2.5

Smallest and largest

rule main:
  print (min(4, 2), max(4, 2));
  print min(9, 3, 7, 1);
  print max(1.5, 2.5);
return;
2,4
1
2.5

Holding a value between bounds

rule main:
  print (clamp(-5, 0, 10), clamp(5, 0, 10), clamp(15, 0, 10));
return;
0,5,10

Rounding, three ways

rule main:
  print (floor(3.7), ceil(3.2), round(3.5));
  print (floor(-3.2), ceil(-3.7), round(-3.5));
return;
3,4,4
-4,-3,-4

Sign and greatest common divisor

rule main:
  print (sign(-7), sign(0), sign(7));
  print (gcd(12, 18), gcd(35, 64));
return;
-1,0,1
6,1

Reducing a fraction

rule reduce(top, bottom ∈ Z) => (a, b ∈ Z):
  new by := gcd(top, bottom);
  let a := top / by;
  let b := bottom / by;
return;
rule main:
  new a, b := reduce(84, 132);
  print a + "/" + b;
return;
7/11

Characters and code points

rule main:
  print (ord('A'), chr(66));
  cycle:
    new i ∈ Z;
  for i ∈ (0..5) do
    write chr(ord('a') + i);
  repeat;
  print;
return;
65,B
abcdef

Changing case, and trimming

rule main:
  print upper("bee");
  print lower("BEE");
  print "[" + trim("   spaced   ") + "]";
return;
BEE
bee
[spaced]

Searching text

rule main:
  print find("hello world", "world");
  print find("hello", "z");
  print contains("hello", "ell");
return;
6
-1
0B1

Replacing and reversing

rule main:
  print replace("2026-08-18", "-", "/");
  print reverse("stressed");
return;
2026/08/18
desserts

Splitting and joining

rule main:
  new parts := split("alpha,beta,gamma", ",");
  print parts;
  print parts.length;
  print join(parts, " -> ");
return;
(alpha,beta,gamma)
3
alpha -> beta -> gamma

Splitting into characters

rule main:
  print split("bee", "");
return;
(b,e,e)

Reading numbers out of text

rule main:
  print parse_z("42") + 1;
  print parse_r("2.5") * 2.0;
  new fields := split("3,4", ",");
  print parse_z(fields.head) + parse_z(fields.tail);
return;
43
5.0
7

Adding a collection up

rule main:
  new xs := [1,2,3,4,5];
  print sum(xs);
  print sum(xs) / xs.length;
return;
15
3

Sorting

rule main:
  print sorted([3,1,2]);
  print sorted(["pear","apple","fig"]);
  print reverse(sorted([3,1,2]));
return;
[1,2,3]
[apple,fig,pear]
[3,2,1]

Ends, and emptiness

rule main:
  new xs := [9,8,7];
  print (first(xs), last(xs), empty(xs));
  new nothing ∈ (Z);
  print empty(nothing);
return;
9,7,0B0
0B1

A word counter

rule main:
  new text := "the bee flies the field";
  new words := split(text, " ");
  print words.length;
  print sorted(words);
return;
5
(bee,field,flies,the,the)

A rule of your own wins

-- a declared name shadows a library one, so adding to the library
-- cannot break a program that already used that name
rule sum(a, b ∈ Z) => (r ∈ Z):
  let r := a + b;
return;
rule main:
  print sum(2, 3);
return;
5

Logic

Booleans

rule main:
  new t := True;
  new f := False;
  print (t, f);
return;
0B1,0B0

Connectives

rule main:
  print (True ∧ False, True ∨ False, ¬True, True ⊕ True);
return;
0B0,0B1,0B0,0B0

The same in ASCII

rule main:
  print (True and False, True or False, not True);
return;
0B0,0B1,0B0

Comparison binds tighter than logic

rule main:
  new a: 1, b: 2 ∈ Z;
  print a = 1 ∧ b = 2;
return;
0B1

Chained comparison

rule main:
  new a: 1, b: 2, c: 3 ∈ Z;
  print a < b < c;
  print a < c < b;
return;
0B1
0B0

Value and type equality

rule main:
  print 1 = 1;
  print 1 ≡ 1;
  print 1 ≠ 2;
return;
0B1
0B1
0B1

Type tests

rule main:
  new a := 10;
  print a ∈ Z;
  print a ∈ R;
return;
0B1
0B0

Suffix conditions

rule main:
  new n := 5;
  print "big" if n > 3;
  print "small" if n ≤ 3;
return;
big

Numbers

Integers and naturals

rule main:
  new z: -5 ∈ Z;
  new n: 5 ∈ N;
  print (z, n);
return;
-5,5

Arithmetic

rule main:
  print (7 + 2, 7 - 2, 7 * 2);
return;
9,5,14

Integer division truncates

rule main:
  print 7 / 2;
  print -7 / 2;
return;
3
-3

Modulo takes the dividend's sign

rule main:
  print 7 % 3;
  print -7 % 3;
return;
1
-1

Real division needs a cast

rule main:
  new a: 7 ∈ Z;
  print (a :> R) / 2.0;
return;
3.5

Powers

rule main:
  new x := 2;
  print x ^ 10;
  print x²;
  new n := 3;
  print xⁿ;
return;
1024
4
8

Roots

rule main:
  print 9 √ 2;
  print 27 root 3;
return;
3.0
3.0

Widening is implicit

rule main:
  new n: 5 ∈ N;
  new z ∈ Z;
  let z := n;
  new r ∈ R;
  let r := z;
  print r;
return;
5.0

Narrowing needs a cast

rule main:
  new r: 9.7 ∈ R;
  print r :> Z;
return;
9

Divides

rule main:
  print 3 ÷ 9;
  print 4 divides 9;
return;
0B1
0B0

Objects

A constructor

rule Point(x, y ∈ Z) => (self ∈ Point):
  new self.x := x;
  new self.y := y;
return;
rule main:
  new p := Point(3,4);
  print (p.x, p.y);
  print kind(p);
return;
3,4
Point

Inheritance

rule Base(tag ∈ S) => (self ∈ Base):
  new self.tag := tag;
  new self.level := 1;
return;
rule Derived() => (self ∈ Derived <: Base):
  let self := super("child");
  let self.level := 2;
return;
rule main:
  new d := Derived();
  print (d.tag, d.level);
return;
child,2

Methods

rule Counter() => (self ∈ Counter):
  new self.n := 0;
  rule .bump():
    let self.n += 1;
  return;
  rule .value() => (v ∈ Z):
    let v := self.n;
  return;
return;
rule main:
  new c := Counter();
  apply c.bump();
  apply c.bump();
  print c.value();
return;
2

A trait

trait Named:
  rule name() => (n ∈ S);
done;
rule Dog() => (self ∈ Dog <: Named):
  new self.legs := 4;
  rule .name() => (n ∈ S):
    let n := "dog";
  return;
return;
rule main:
  print Dog().name();
return;
dog

One rule, any implementer

trait Named:
  rule name() => (n ∈ S);
done;
rule Dog() => (self ∈ Dog <: Named):
  new self.legs := 4;
  rule .name() => (n ∈ S):
    let n := "dog";
  return;
return;
rule Bird() => (self ∈ Bird <: Named):
  new self.legs := 2;
  rule .name() => (n ∈ S):
    let n := "bird";
  return;
return;
rule announce(x ∈ Named):
  print x.name();
return;
rule main:
  apply announce(Dog());
  apply announce(Bird());
return;
dog
bird

Objects share, :: copies

rule main:
  new a := {v: 1};
  new shared := a;
  new copied :: a;
  let a.v := 9;
  print (shared.v, copied.v);
return;
9,1

Ranges

Inclusive

rule main:
  for i ∈ (1..5) do
    write i;
  repeat;
  print;
return;
12345

Excluding an end

rule main:
  for i ∈ (1.!5) do
    write i;
  repeat;
  print;
  for i ∈ (1!.5) do
    write i;
  repeat;
  print;
return;
1234
2345

Excluding both

rule main:
  for i ∈ (1!!5) do
    write i;
  repeat;
  print;
return;
234

With a step

rule main:
  for i ∈ (0..10:2) do
    write i;
    write ",";
  repeat;
  print;
return;
0,2,4,6,8,10,

Membership

rule main:
  print 5 ∈ (1..9);
  print 5 in (1..4);
return;
0B1
0B0

Character ranges

rule main:
  for c ∈ ('a'..'e') do
    write c;
  repeat;
  print;
return;
abcde

Rationals

The p\q literal

rule main:
  print (1\2, 1\4, 1\8);
return;
0.5,0.25,0.125

Exact arithmetic

rule main:
  print 1\4 + 1\8;
  print 1\4 * 1\2;
  print 1\4 / 1\8;
return;
0.375
0.125
2

Eight eighths make one

rule main:
  new total ∈ Q;
  cycle:
    new i ∈ Z;
  for i ∈ (1..8) do
    let total += 1\8;
  repeat;
  print total;
  expect total = 1;
return;
1

Choosing a container

rule main:
  new tiny: 31.75 ∈ Q(5,2);
  print (tiny, kind(tiny));
return;
31.75,Q5.2

Approximate comparison

rule main:
  print (0.33333 ≈ 1\3);
  print (0.25 ≈ 1\3);
  print (0.25 ≈ 1\3 ± 0.1);
return;
0B1
0B0
0B1

Seeing the exact value

rule main:
  print 1\10;
  print exact(1\10);
return;
0.1
0.09999847412109375

Q converts to R freely

rule main:
  new r ∈ R;
  let r := 1\2;
  print r;
  print 0.75 :> Q;
return;
0.5
0.75

Longitude and latitude

rule main:
  new lon: 2.3522 ∈ Λ;
  new lat: 48.8566 ∈ Φ;
  print (lat, lon);
  print kind(lat);
return;
48.8566,2.3522
Φ

Rules

A rule with a result

rule double(n ∈ Z) => (r ∈ Z):
  let r := n * 2;
return;
rule main:
  print double(21);
return;
42

Several results

rule split(n ∈ Z) => (lo, hi ∈ Z):
  let lo := n / 2;
  let hi := n - lo;
return;
rule main:
  new a, b := split(9);
  print (a, b);
return;
4,5

Collecting results into a list

rule split(n ∈ Z) => (lo, hi ∈ Z):
  let lo := n / 2;
  let hi := n - lo;
return;
rule main:
  new both := split(9);
  print both;
return;
(4,5)

Optional parameters

rule greet(name ∈ S, times: 1 ∈ Z):
  for i ∈ (1..times) do
    write name;
  repeat;
  print;
return;
rule main:
  apply greet("hi");
  apply greet("ho", 3);
return;
hi
hohoho

Named arguments

rule box(width: 1, height: 1 ∈ Z) => (area ∈ Z):
  let area := width * height;
return;
rule main:
  print box(height: 5);
  print box(width: 2, height: 3);
return;
5
6

Recursion

rule fact(n ∈ N) => (r ∈ N):
  if n ≤ 1 do
    let r := 1;
  else
    let r := n * fact(n - 1);
  done;
return;
rule main:
  print fact(6);
return;
720

Mutual recursion

rule odd(n ∈ Z) => (r ∈ B);

rule even(n ∈ Z) => (r ∈ B):
  if n = 0 do
    let r := True;
  else
    let r := odd(n - 1);
  done;
return;

rule odd(n ∈ Z) => (r ∈ B):
  if n = 0 do
    let r := False;
  else
    let r := even(n - 1);
  done;
return;

rule main:
  print (even(10), odd(10));
return;
0B1,0B0

Modifying a caller's variable

rule bump(n ∈ [Z]):
  let n += 1;
return;
rule main:
  new count := 0;
  apply bump(@count);
  print count;
return;
1

Passing a collection

rule total(xs ∈ [Z]) => (sum ∈ Z):
  for x ∈ xs do
    let sum += x;
  repeat;
return;
rule main:
  print total([1,2,3,4]);
return;
10

Lambdas

rule main:
  new square := λ(x ∈ Z) => x² ∈ Z;
  print square(7);
return;
49

A lambda as a callback

rule twice(x ∈ Z, f: λ(v ∈ Z) => Z) => (r ∈ Z):
  let r := f(f(x));
return;
rule main:
  print twice(3, λ(v ∈ Z) => v + 1 ∈ Z);
return;
5

Lambdas in a map

rule main:
  new ops := {"inc": λ(x ∈ Z) => x + 1 ∈ Z};
  print ops["inc"](41);
return;
42

Discarding results deliberately

rule value() => (r ∈ Z):
  let r := 7;
return;
rule main:
  apply _ := value();
  print "discarded";
return;
discarded

Safety

Arithmetic stays in range or stops

rule main:
  new big: 9223372036854775806 ∈ Z;
  let big += 1;
  print big;
  print "one more would trap, not wrap";
return;
9223372036854775807
one more would trap, not wrap

A domain is enforced

type Digit: (0..9) <: Z;
rule main:
  new d: 9 ∈ Digit;
  print d;
return;
9

Ownership transfer

rule consume(xs ∈ [Z]) => (total ∈ Z):
  for x ∈ xs do
    let total += x;
  repeat;
return;
rule main:
  new numbers := [1,2,3,4];
  print consume(numbers!);
  let numbers := [10,20];
  print consume(numbers!);
return;
10
30

Every operator has an ASCII spelling

rule main:
  new a: 1, b: 2 ∈ Z;
  print a = 1 and b = 2;
  print 2 in (1..3);
  print forall (i in {2,4}) and (i % 2 = 0);
  print {1} union {2};
return;
0B1
0B1
0B1
{1,2}

Ownership moves out of a name

rule consume(data ∈ [Z]) => (total ∈ Z):
  cycle:
    new i ∈ Z;
  for i ∈ data do
    let total += i;
  repeat;
return;
rule main:
  new numbers := [1,2,3,4];
  print consume(numbers!);
  -- the name holds nothing until it is given a new value
  let numbers := [10,20];
  print consume(numbers!);
return;
10
30

Strings

Strings and characters

rule main:
  new s := "text";
  new c := 'x';
  print (s, c);
return;
text,x

Concatenation coerces

rule main:
  print "n = " + 42;
  print "r = " + 0.5;
return;
n = 42
r = 0.5

Replication

rule main:
  print '-' * 20;
  print "ab" * 3;
return;
--------------------
ababab

Length and indexing

rule main:
  new s := "hello";
  print s.length;
  print s[0];
  print s[-1];
return;
5
h
o

Slices

rule main:
  new s := "abcdef";
  print s[0..2];
  print s[2..5];
return;
abc
cdef

Templates

rule main:
  print "n=#(z) s=#(s)" ? (5, "hi");
return;
n=5 s='hi'

Formatting numbers

rule main:
  print "#(r:0.3)" ? (3.14159);
  print "#(>0:6)" ? (42);
  print "#(<_:6)|" ? (42);
return;
3.142
000042
42    |

Bases and quoting

rule main:
  print "#(b)" ? (10);
  print "#(h)" ? (255);
  print "#(q)" ? ("q");
return;
1010
ff
"q"

Whole collections

rule main:
  print "all: #[*]" ? ([1,2,3]);
  print "one: #[1]" ? ([1,2,3]);
return;
all: 1,2,3
one: 2

Strings are shared

rule main:
  new a := "x";
  new b := a;
  let a += "y";
  print b;
return;
xy

Types

A type alias

type Row: [Z](3);
rule main:
  new r ∈ Row;
  let r[*] := 1;
  print r;
return;
[1,1,1]

A constrained domain

type Digit: (0..9) <: Z;
rule main:
  new d: 7 ∈ Digit;
  print d;
  print kind(d);
return;
7
Digit

A record

type Point: {x ∈ Z, y ∈ Z} <: Object;
rule main:
  new p ∈ Point;
  let p.x := 3;
  let p.y := 4;
  print p;
return;
{x: 3, y: 4}

Object literals

rule main:
  new p := {x: 1, y: 2};
  print p;
  print p.x;
return;
{x: 1, y: 2}
1

Nested objects

rule main:
  new o := {inner: {v: 7}};
  print o.inner.v;
return;
7

kind labels, ∈ asks, ≡ compares

rule main:
  new n := 1;
  new m := 1;
  -- a label, for reading
  print kind(n);
  -- a checked question
  print n ∈ Z;
  -- value and type together
  print n ≡ m;
return;
Z
0B1
0B1

Whole programs

The conformance demos: complete programs with recorded output, run by run_tests.py on every change. Sources are in tests/demos/.

What Bee-3 changed

tests/demos/bee3.bee

+-------------------------------------------
|  what Bee-3 changed, and why             |
-------------------------------------------+
-- Every line here is either impossible or wrong in Bee-2.

-- D71: mutual recursion, which Bee-2 could not express at all
rule odd(n ∈ Z) => (r ∈ B);

rule even(n ∈ Z) => (r ∈ B):
  if n = 0 do
    let r := True;
  else
    let r := odd(n - 1);
  done;
return;

rule odd(n ∈ Z) => (r ∈ B):
  if n = 0 do
    let r := False;
  else
    let r := even(n - 1);
  done;
return;

rule main:
  -- D66: a compound condition means what it looks like
  new a: 1, b: 2 ∈ Z;
  print a = 1 ∧ b = 2;
  print a > 0 ∨ b > 9;

  -- D68: the same program, typed on an ordinary keyboard
  print a = 1 and b = 2;
  print 2 in (1..3);
  print forall (i in {2,4}) and (i % 2 = 0);
  print {1,2} union {3};

  -- D69: == compares values, as everywhere else
  new x := "abc";
  new y := "abc";
  print x == y;

  -- D71 again
  print even(10);
  print odd(10);

  -- D72: a match covers every selector
  match 99:
  when 1 do
    print "one";
  other
    print "anything else";
  done;
return;
0B1
0B1
0B1
0B1
0B1
{1,2,3}
0B1
0B1
0B0
anything else

Contracts

tests/demos/contracts.bee

+-------------------------------------------
|  contracts: what a rule promises         |
-------------------------------------------+
-- A precondition is the caller's obligation, a postcondition the rule's.
-- Both sit in the signature, where a reader looking only at the interface
-- still sees them, and both must be free of side effects (D75).

-- A pure helper, so it may be used in a contract.
rule sorted_pair(lo, hi ∈ Z) => (r ∈ B):
  let r := lo ≤ hi;
return;

-- The caller must pass an even, non-negative number; the rule promises
-- the result doubles back to it.
rule half(n ∈ Z) => (r ∈ Z):
require n ≥ 0;
require n % 2 = 0;
ensure r * 2 = n;
  let r := n / 2;
return;

-- A contract may name the results and the parameters together.
rule clamp(v, lo, hi ∈ Z) => (r ∈ Z):
require sorted_pair(lo, hi);
ensure lo ≤ r ≤ hi;
  let r := v;
  if v < lo do
    let r := lo;
  else if v > hi do
    let r := hi;
  done;
return;

rule main:
  print half(8);
  print half(0);

  print clamp(5, 1, 10);
  print clamp(-3, 1, 10);
  print clamp(99, 1, 10);

  -- the postcondition of clamp uses a chained comparison (D74)
  expect clamp(7, 1, 10) = 7;
return;
4
0
5
1
10

Safety features

tests/demos/safety.bee

+-------------------------------------------
|  three safety features Bee-2 lacked      |
-------------------------------------------+

-- D80: a rule with results cannot be called for effect alone, unless the
-- discard is written down.
rule checked(n ∈ Z) => (ok ∈ B):
  let ok := n > 0;
return;

rule announce(n ∈ Z):
  print n;
return;

-- D81: taking ownership.  The caller cannot touch `data` afterwards, so a
-- job may mutate it with nothing to race against and no lock to forget.
rule consume(data ∈ [Z]) => (total ∈ Z):
  cycle:
    new i ∈ Z;
  for i ∈ data do
    let total += i;
  repeat;
return;

rule main:
  -- D79: an accumulator relies on the zero value, and stays legal
  new total ∈ Z;
  cycle:
    new i ∈ Z;
  for i ∈ (1..4) do
    let total += i;
  repeat;
  print total;

  -- D80: results are bound, or discarded on purpose
  new ok := checked(5);
  print ok;
  apply announce(7);
  apply _ := checked(-1);

  -- D81: ownership moves out of `numbers`
  new numbers := [1,2,3,4];
  print consume(numbers!);

  -- and a new value revives the name
  let numbers := [10,20];
  print consume(numbers!);
return;
10
0B1
7
10
30

Parallel loops, geospatial

tests/demos/parallel.bee

+-------------------------------------------
|  ∀ means the iterations are independent  |
-------------------------------------------+
-- Bee-2 treated ∀ in a for header as decoration (D20).  Bee-3 makes it a
-- claim the compiler checks (D82): an iteration may write only what it
-- owns — its own locals, and the element its control variable selects.
-- -- Nothing runs in parallel yet.  The claim is checked, which is the part
-- that has to be true before the rest is worth building.

-- Λ and Φ are constrained Q domains, not a new numeric kind (D83).
rule east_of(here, there ∈ Λ) => (r ∈ B):
  let r := here > there;
return;

rule main:
  -- data-parallel: each iteration writes a different element
  new squares ∈ [Z](6);
  for ∀ i ∈ (0.!6) do
    let squares[i] := i * i;
  repeat;
  print squares;

  -- a plain for has no such restriction, and may accumulate
  new total ∈ Z;
  for n ∈ squares do
    let total += n;
  repeat;
  print total;

  -- geospatial: about 0.85 m of resolution at the equator
  new paris_lon: 2.3522 ∈ Λ;
  new paris_lat: 48.8566 ∈ Φ;
  print (paris_lat, paris_lon);
  print exact(paris_lon);

  new london_lon: -0.1276 ∈ Λ;
  print east_of(paris_lon, london_lon);
  print kind(paris_lat);
return;
[0,1,4,9,16,25]
55
48.8566,2.3522
2.352203369140625
0B1
Φ

Traits

tests/demos/traits.bee

+-------------------------------------------
|  traits: a promise, not just a shape     |
-------------------------------------------+
-- A trait names the methods a type must provide, and may carry the
-- contracts every implementation inherits (D84).  A type declares which
-- trait it satisfies on its constructor's result, and the compiler checks
-- it — nothing is satisfied by accident.
-- -- Methods live inside the constructor, which is where upstream's own
-- generator demo writes them.

trait Shape:
  rule area() => (a ∈ R)
  ensure a > 0;
  rule name() => (n ∈ S);
done;

rule Circle(radius ∈ R) => (self ∈ Circle <: Shape):
  new self.radius := radius;
  rule .area() => (a ∈ R):
    let a := 3.14159 * self.radius * self.radius;
  return;
  rule .name() => (n ∈ S):
    let n := "circle";
  return;
return;

rule Square(side ∈ R) => (self ∈ Square <: Shape):
  new self.side := side;
  rule .area() => (a ∈ R):
    let a := self.side * self.side;
  return;
  rule .name() => (n ∈ S):
    let n := "square";
  return;
return;

-- one rule, any Shape
rule describe(s ∈ Shape):
  print s.name() + " has area " + s.area();
return;

rule main:
  apply describe(Circle(1.0));
  apply describe(Square(3.0));
return;
circle has area 3.14159
square has area 9.0

Generics

tests/demos/generics.bee

+-------------------------------------------
|  generics: type parameters on rules      |
-------------------------------------------+
-- Deliberately small (D86): parameters on rules only, no variance, no
-- higher kinds, bounds through the existing traits, and monomorphised —
-- one compiled function per set of type arguments.
-- -- This is what the standard library wanted: 17 of its 27 rules are
-- built-ins purely because `rule abs(n ∈ Z)` could not also serve R.

trait Sized:
  rule size() => (n ∈ Z);
done;

rule Box(width ∈ Z) => (self ∈ Box <: Sized):
  new self.width := width;
  rule .size() => (n ∈ Z):
    let n := self.width;
  return;
return;

-- one rule, any element type
rule first_of[T](items ∈ [T]) => (r ∈ T):
  let r := items[0];
return;

-- the variable may appear more than once
rule occurrences[T](items ∈ [T], wanted ∈ T) => (n ∈ Z):
  cycle:
    new item ∈ T;
  for item ∈ items do
    let n += 1 if item = wanted;
  repeat;
return;

-- and in the result
rule pair[T](a, b ∈ T) => (r ∈ [T]):
  let r := [a, b];
return;

-- a bound is a promise: T must satisfy Sized
rule doubled[T <: Sized](thing ∈ T) => (n ∈ Z):
  let n := thing.size() * 2;
return;

rule main:
  -- the same rule, three element types
  print first_of([1,2,3]);
  print first_of(["alpha","beta"]);
  print first_of([1.5, 2.5]);

  -- and the result keeps that type
  new n := first_of([10,20]);
  new s := first_of(["x"]);
  print (kind(n), kind(s));

  print occurrences([1,2,2,3], 2);
  print occurrences(["a","b","a"], "a");

  print pair(7, 8);
  print pair("l", "r");

  print doubled(Box(21));
return;
1
alpha
1.5
Z,S
2
2
[7,8]
[l,r]
42

Standard library

tests/demos/library.bee

+-------------------------------------------
|  the standard library                    |
-------------------------------------------+
-- Bee-3 had five built-ins, so every program began by rebuilding abs and
-- min.  These are built-ins rather than a library written in Bee because
-- Bee has no generics: `rule abs(n ∈ Z)` could not also serve R or Q, and
-- a library would need a copy per type (D85).

rule main:
  -- numbers, whatever their type
  print (abs(-3), abs(-3.5), min(4,2,9), max(4,2,9));
  print (sign(-7), floor(3.7), ceil(3.2), round(3.5), round(-3.5));
  print (clamp(15, 1, 10), gcd(12, 18));

  -- characters
  print (ord('A'), chr(66));

  -- text
  print (upper("bee"), lower("BEE"), trim("  spaced  "));
  print (find("hello", "ll"), contains("hello", "ell"));
  print replace("a-b-c", "-", "+");
  print reverse("stressed");

  -- text into numbers
  print parse_z("42") + 1;
  print parse_r("2.5") * 2.0;

  -- collections
  new xs := [5,3,9,1];
  print (sum(xs), first(xs), last(xs), empty(xs));
  print sorted(xs);
  print reverse(xs);

  -- splitting and joining
  new parts := split("alpha,beta,gamma", ",");
  print parts;
  print join(parts, " -> ");
  print parts.length;
return;
3,3.5,2,9
-1,3,4,4,-4
10,6
65,B
BEE,bee,spaced
2,0B1
a+b+c
desserts
43
5.0
18,5,1,0B0
[1,3,5,9]
[1,9,3,5]
(alpha,beta,gamma)
alpha -> beta -> gamma
3

Contracts about change

tests/demos/oldvalue.bee

+-------------------------------------------
|  contracts about change                  |
-------------------------------------------+
-- `@T` marks a parameter the rule may write through (D88).  Bee-2 spelled
-- this `[T]` — the same as an array — so `let a += 1` incremented a boxed
-- scalar but appended to an array, and a contract could say nothing about
-- either.
-- -- `old n` is what a parameter held at entry (D89), so a postcondition can
-- describe the change rather than only the result.

rule bump(n ∈ @Z):
ensure n = old n + 1;
  let n += 1;
return;

rule grow(items ∈ [Z], by ∈ Z):
require by ≥ 0;
ensure items.length = old items.length + by;
  let items ++ by;
return;

rule deposit(balance ∈ @Q, amount ∈ Q):
require amount > 0;
ensure balance > old balance;
  let balance += amount;
return;

rule main:
  -- a scalar the rule writes through
  new counter: 10 ∈ Z;
  apply bump(@counter);
  print counter;

  -- a collection: old deep-copies, so the length before is knowable
  new xs ∈ [Z](2);
  print xs.length;
  apply grow(xs, 3);
  print xs.length;

  -- exact money, with a promise it went up
  new balance: 10 ∈ Q;
  apply deposit(@balance, 1\4);
  print balance;
return;
11
2
5
10.25

Hello world

tests/demos/hello_world.bee

-- hello world demo
rule main:
  print "Hello World";
return;
Hello World

Fibonacci

tests/demos/fibonacci.bee

+----------------------------
|  Demo Fibonacci rule      |
----------------------------+
-- Ported from demo/fibonacci.bee
-- Note: fib(0) = fib(1) = 1, so the sequence is offset by one
--       from the conventional Fibonacci numbering. fib(5) = 8.

rule fib(n ∈ N) => (y ∈ N):
  if (n = 1) ∨ (n = 0) do
    let y := 1;                      -- first value
  else
    let y := fib(n-1) + fib(n-2);
  done;
return;

rule main:
  -- call fib rule using a named argument
  new r := fib(n: 5);
  print r;
return;
8

Bubble sort

tests/demos/bubble_sort.bee

+--------------------------------------
|  Bubble sort with Array of integers |
--------------------------------------+
-- Ported from demo/bubble_sort.bee
-- Two bugs fixed: comparison direction (was descending) and
-- an off-by-one that read this[n] past the end of the array.

rule sort(this ∈ [Z]):
  new n := length(this);
  new swap := True;                                     -- inferred B
  cycle:
  do
    let swap := False;                                    -- reset flag
    for i ∈ (0 .! n-1) do
      if this[i] > this[i+1] do
        let (this[i], this[i+1]) := (this[i+1], this[i]);  -- swap
        let swap := True;
      done;
    repeat;
  repeat if swap;
return;

rule main:
  new test   := [1,4,1,5,9,2,6,5,3,5];
  new result := [1,1,2,3,4,5,5,5,6,9];

  apply sort(test);
  print test;

  expect test = result;
return;
[1,1,2,3,4,5,5,5,6,9]

Rationals (Q)

tests/demos/rationals.bee

+-----------------------------------
|  fixed point, compiled           |
-----------------------------------+
-- Kept inside the C backend's subset so both implementations run it.

rule payment(total ∈ Q, parts ∈ Z) => (each ∈ Q):
  let each := total / parts;
return;

rule main:
  -- the inch divisions: all exact in binary fixed point
  print 1\2;
  print 1\4;
  print 1\8;
  print 1\16;
  print 1\32;

  -- arithmetic does not drift
  new total: 0 ∈ Q;
  new step: 1\8 ∈ Q;
  cycle:
    new i ∈ Z;
  for i ∈ (1..8) do
    let total += step;
  repeat;
  print total;
  expect total = 1;

  -- mixed with integers, and division
  print 1\4 + 1\8;
  print 1\4 * 2;
  print payment(3\4, 3);

  -- conversions both ways
  new r ∈ R;
  let r := 1\2;
  print r;
  new back := 0.75 :> Q;
  print back;
  print (1\2 + 1) :> Z;

  -- a narrower container
  new tiny: 31.75 ∈ Q(5,2);
  print tiny;
return;
0.5
0.25
0.125
0.0625
0.03125
1
0.375
0.5
0.25
0.5
0.75
1
31.75

Lists

tests/demos/lists.bee

+-----------------------------------
|  lists, in both implementations  |
-----------------------------------+
-- Kept inside the C backend's subset, so the differential harness compares
-- the interpreter against a native binary on every line of this.

rule drain(queue ∈ (Z)) => (total ∈ Z):
  cycle:
  while queue.length > 0 do
    let total += queue.head;
    let queue << 1;
  repeat;
return;

rule main:
  new l := (1,2,3);
  print l;
  print (l.head, l.tail, l.length);
  print l[1];
  print l[-1];

  -- the ends grow and shrink independently
  let l <+ 4;
  let l +> 0;
  print l;
  let l << 2;
  let l >> 1;
  print l;

  -- concatenation and equality are by value
  new m := (9,8);
  print l + m;
  new a := (1,2);
  new b := (1,2);
  print a = b;

  -- a list is shared by :=, copied by ::
  new shared := a;
  new copied :: a;
  let a <+ 3;
  print (shared.length, copied.length);

  -- iteration walks the live window, not the buffer
  new sum ∈ Z;
  for x ∈ m do
    let sum += x;
  repeat;
  print sum;

  -- passed by reference, so the rule consumes the caller's list
  new queue := (5,10,15);
  print drain(queue);
  print queue.length;
return;
(1,2,3)
1,3,3
2
3
(0,1,2,3,4)
(2,3)
(2,3,9,8)
0B1
3,2
17
30
0

Sets, maps, builders

tests/demos/collections.bee

+-----------------------------------
|  sets, maps and builders         |
-----------------------------------+
-- New in Bee-1a. Nothing in this file exists in Bee-0.

rule main:
  -- sets are sorted, unique and unindexed
  new small := {3,1,2,1};
  print small;                                  -- duplicates collapse

  new bigger := {2,3,4};
  print small ∪ bigger;
  print small ∩ bigger;
  print small Δ bigger;
  print {1,2} ⊂ small;

  let small += 9;
  let small -= 1;
  print small;
  print 9 ∈ small;

  -- maps are keyed and kept in key order
  new roman := {1:"I", 2:"II"};
  let roman[3] := "III";
  print roman[3];
  scrap roman[1];

  for key, value ∈ roman do
    write key + "=" + value + " ";
  repeat;
  print;

  -- builders draw from a source and may filter
  print { x | x ∈ (1..5) ∧ (x % 2 = 1) };
  print { x² | x ∈ (1..3) };
  print [ x | x ∈ (1..9:2) ];
  print { (x:x²) | x ∈ (0.!10) ∧ (x % 2 = 0) };

  -- logic quantifiers over a collection
  print ∀ (i ∈ {2,4,6}) ∧ (i % 2 = 0);
  print ∃ (i ∈ {1,3,5}) ∧ (i = 3);
return;
{1,2,3}
{1,2,3,4}
{2,3}
{1,4}
0B1
{2,3,9}
0B1
III
2=II 3=III 
{1,3,5}
{1,4,9}
[1,3,5,7,9]
{(0:0),(2:4),(4:16),(6:36),(8:64)}
0B1
0B1

Lambdas, match, trial

tests/demos/advanced.bee

+-----------------------------------------
|  lambdas, templates, match, trial,     |
|  objects — everything added in Bee-1b  |
-----------------------------------------+

type Point: {x ∈ Z, y ∈ Z} <: Object;

-- a lambda passed as a callback
rule combine(a, b ∈ Z, op: λ(p, q ∈ Z) => Z) => (r ∈ Z):
  let r := op(a, b);
return;

-- a rule that fails on bad input, for the trial below
rule halve(n ∈ Z) => (r ∈ Z):
  let r := n / 2;
return;

rule main:
  -- lambdas are values
  new square := λ(v ∈ Z) => v² ∈ Z;
  print square(7);
  print combine(3, 4, λ(p, q ∈ Z) => p * q ∈ Z);

  -- string templates
  print "#(z) and #(z) make #(z)" ? (2, 3, 5);
  print "pi is about #(r:0.3)" ? (3.14159);
  print "padded:#(>0:5)" ? (42);
  print "everything: #[*]" ? ([1,2,3]);

  -- match, one variant: the first hit wins
  new grade := 87;
  match grade:
  when (90..100) do
    print "A";
  when (80.!90) do
    print "B";
  other
    print "C or below";
  done;

  -- match, all variant: every hit runs
  match all 4:
  when 4 do
    print "exactly four";
  when (0..9) do
    print "a single digit";
  done;

  -- objects are records with named fields
  new here ∈ Point;
  let here.x := 3;
  let here.y := 4;
  print here;
  print here.x + here.y;

  new there := {x: 10, y: 20};
  print there.y;

  -- a trial: one job fails, a case resolves it, the rest continue
  trial:
    new done_jobs: 0 ∈ Z;
  try:
    let done_jobs += 1;
    print "checked input";
  try:
    let done_jobs += 1;
    raise 300, "value rejected";
  try:
    let done_jobs += 1;
    print "finished work";
  case $error.code = 300 do
    print "recovered from: " + $error.message;
    resume;
  miss
    print "no handler matched";
  final
    print "jobs run: " + done_jobs;
  done;
return;
49
12
2 and 3 make 5
pi is about 3.142
padded:00042
everything: 1,2,3
B
exactly four
a single digit
{x: 3, y: 4}
7
20
checked input
recovered from: value rejected
finished work
jobs run: 3

Coroutines

tests/demos/producer_consumer.bee

+-------------------------------------------
|  producer and consumer, two coroutines   |
|  taking turns over one channel           |
-------------------------------------------+
-- Ported from demo/producer_consumer.bee, which is written in the legacy
-- dialect and uses an operator (`-?`) that appears in no upstream table.
-- The shape is kept: a producer fills a bounded channel, a consumer drains
-- it, and neither runs while the other does.

-- The producer stops filling once the channel reaches its batch size.
rule produce(channel ∈ (N), last, batch ∈ N) => (made ∈ N):
  cycle:
    new mark: 1 ∈ N;
  do
    -- fill until the channel is full or the numbers run out
    cycle:
    while (channel.length < batch) ∧ (mark ≤ last) do
      let channel <+ mark;
      let made += 1;
      let mark += 1;
    repeat;
    yield;
  repeat if mark ≤ last;
  let made := 0;
return;

-- The consumer drains whatever is waiting, then hands control back.
rule consume(channel ∈ (N)) => (taken ∈ N):
  cycle:
  do
    cycle:
    while channel.length > 0 do
      write channel.head + " ";
      let channel << 1;
      let taken += 1;
    repeat;
    yield;
  repeat;
return;

rule main:
  new channel ∈ (N);

  begin produce(channel, 9, 4);
  begin consume(channel);

  -- declared out here so the totals survive the cycle
  new made ∈ N;
  new taken ∈ N;
  cycle:
  do
    yield made << produce;
    yield taken << consume;
  repeat if made > 0;
  print;

  print "consumed " + taken;
  expect channel.length = 0;
return;
1 2 3 4 5 6 7 8 9 
consumed 9

Map-reduce

tests/demos/map_reduce.bee

+-------------------------------------------
|  map-reduce: four jobs, then a fold      |
-------------------------------------------+
-- Ported from the concurrency chapter. Every job here is isolated (D64):
-- `sum` reads only its arguments and writes only its own result, so the
-- four could run in parallel with identical results.
-- --   python3 -m bee --isolation tests/demos/map_reduce.bee

rule sum(a, b ∈ Z) => (r ∈ Z):
  cycle:
    new i ∈ Z;
  for i ∈ (a..b) do
    let r += i;
  repeat;
return;

rule main:
  new partials ∈ (Z);
  cycle:
    new lo ∈ Z;
  for lo ∈ (1..76:25) do
    begin partials <+ sum(lo, lo + 24);
  repeat;
  wait;

  new total ∈ Z;
  for part ∈ partials do
    let total += part;
  repeat;
  print total;
  expect total = 5050;
return;
5050

Everything

tests/demos/complete.bee

+-------------------------------------------
|  rationals, constructors, inheritance,   |
|  coroutines and deferred jobs            |
-------------------------------------------+
set $precision: 0.001;

type Digit: (0..9) <: Z;

-- a constructor: its single result is named self, and its name is its type
rule Shape(name ∈ S) => (self ∈ Shape):
  new self.name := name;
  new self.sides := 0;
return;

-- a child constructor chains through super
rule Square(size ∈ Q) => (self ∈ Square <: Shape):
  let self := super("square");
  let self.sides := 4;
  let self.area := size * size;
return;

-- a bare yield makes this a coroutine
rule halves(n ∈ N) => (part ∈ Q):
  cycle:
    new i ∈ N;
  for i ∈ (1..n) do
    let part := 1\2 ^ i;
    yield;
  repeat;
  let part := 0;
return;

rule scaled(x ∈ Z) => (r ∈ Z):
  let r := x * 10;
return;

rule main:
  -- fixed point is exact, and p\q is the literal notation
  print (1\2, 1\4, 1\8);
  print 1\4 + 1\8;
  print 1\4 * 1\2;
  print 1\3;

  -- a sized container states its own range
  new tiny: 31.75 ∈ Q(5,2);
  print (tiny, kind(tiny));

  -- ≈ uses $precision unless a tolerance is given
  print (0.333 ≈ 1\3);
  print (0.25 ≈ 1\3);
  print (0.25 ≈ 1\3 ± 0.1);

  -- a range subtype constrains its values
  new d: 7 ∈ Digit;
  print d;

  -- constructors and inheritance
  new blob := Shape("blob");
  print (blob.name, blob.sides);
  new sq := Square(1\2);
  print (sq.name, sq.sides, sq.area);

  -- a coroutine keeps its state between resumes
  begin halves(4);
  cycle:
    new v ∈ Q;
  do
    yield v << halves;
    write v + " ";
  repeat if v > 0;
  print;

  -- begin defers, wait joins in start order
  new results ∈ (Z);
  begin results <+ scaled(1);
  begin results <+ scaled(2);
  begin results <+ scaled(3);
  wait;
  print results;
return;
0.5,0.25,0.125
0.375
0.125
0.33334
31.75,Q5.2
0B1
0B0
0B1
7
blob,0
square,4,0.25
0.5 0.25 0.125 0.0625 0 
(10,20,30)

Traps

Each of these fails on purpose. A language whose argument is that it catches mistakes should let you watch it catch them, so every guarantee has a program here that trips it.

Shadowing (D70)

rule main:
  new count := 1;
  start:
    new count := 2;
  do
    print count;
  done;
return;
error[E248]: "count" shadows an outer declaration
  = help: rename one of them; a reader should not have to work out which is meant (D70)

Match with no default (D72)

rule main:
  match 99:
  when 1 do
    print "one";
  done;
return;
error[E250]: this match has no `other` branch
  = help: the branches cannot be shown to cover every value of Z; add `other` (D72)

Stale slice after resize (D73)

rule main:
  new a := [1,2,3,4,5];
  new window := a[0..2];
  let a ++ 3;
  print window;
return;
runtime error: this slice was taken before the array was resized

** mid-line (D67)

rule main:
  new a := 2;
  new b := 3;
  print a ** b;
return;
error[E032]: "**" mid-line is neither a comment nor a power
  = help: a `**` comment must start a line; for exponentiation write ^ or a superscript (D67)

Truthiness (§7.4)

rule main:
  new n := 1;
  if n do
    print 1;
  done;
return;
error[E010]: the if condition must be B, found Z
  = help: Bee has no truthiness; compare explicitly, as in `x ≠ 0` (§7.4)

Implicit narrowing (§4.5)

rule main:
  new n ∈ Z;
  let n := 10.5;
  print n;
return;
error[E012]: cannot assign R to Z
  = help: narrowing is never implicit; write `... :> Z` (§4.5)

Undeclared name (§3)

rule main:
  print total;
return;
error[E013]: undeclared identifier "total"
  = help: Bee has no hoisting: declare it before use (§3)

Chain of unlike types (D74)

rule main:
  new n: 1 ∈ Z;
  new s := "two";
  print n < s < n;
return;
error[E219]: cannot compare Z with S

Integer overflow (§7.2)

rule main:
  new big: 9223372036854775807 ∈ Z;
  let big += 1;
  print big;
return;
runtime error: + overflowed a 64-bit integer

Outside a range subtype (§4.3)

type Digit: (0..9) <: Z;
rule main:
  new d: 99 ∈ Digit;
  print d;
return;
runtime error: 99 is above the domain of Digit

Index out of range (§10.1)

rule main:
  new a := [1,2,3];
  print a[9];
return;
runtime error: index 9 is outside 0..2

Broken precondition (D75)

rule half(n ∈ Z) => (r ∈ Z):
require n % 2 = 0;
  let r := n / 2;
return;
rule main:
  print half(7);
return;
runtime error: "half" requires condition 1

Contract with a side effect (D75)

new log: 0 ∈ Z;
rule noisy(n ∈ Z) => (r ∈ B):
  let log += 1;
  let r := n > 0;
return;
rule bad(n ∈ Z) => (r ∈ Z):
require noisy(n);
  let r := n;
return;
rule main:
  print bad(1);
return;
error[E252]: a require condition cannot call "noisy"
  = help: it is not isolated: writes the module variable "log" (D75)

A promise about change, broken (D89)

rule shrink(n ∈ @Z):
ensure n > old n;
  let n -= 1;
return;
rule main:
  new x: 5 ∈ Z;
  apply shrink(@x);
  print x;
return;
runtime error: "shrink" fails to ensure condition 1

old outside an ensure (D89)

rule f(n ∈ @Z):
require n > old n;
  let n += 1;
return;
rule main:
  new x: 1 ∈ Z;
  apply f(@x);
return;
error[E264]: "old" belongs in an ensure condition
  = help: in a require nothing has happened yet (D89)

The old spelling of kind (D87)

rule main:
  new n := 1;
  print type(n);
return;
error[E263]: the built-in is "kind", not "type"
  = help: write `kind(x)`; `type` declares a type (D87)

An unsatisfied type bound (D86)

trait Sized:
  rule size() => (n ∈ Z);
done;
rule doubled[T <: Sized](thing ∈ T) => (n ∈ Z):
  let n := thing.size() * 2;
return;
rule main:
  print doubled(42);
return;
error[E261]: Z does not satisfy Sized
  = help: the type parameter T is bounded (D86)

A type parameter fixed twice (D86)

rule same[T](a, b ∈ T) => (r ∈ T):
  let r := a;
return;
rule main:
  print same(1, "x");
return;
error[E261]: "same" cannot take S where its b is T
  = help: the type parameter is already fixed by an earlier argument (D86)

A trait promise broken (D84)

trait Shape:
  rule area() => (a ∈ R)
  ensure a > 0;
done;
rule Bad() => (self ∈ Bad <: Shape):
  new self.x := 0;
  rule .area() => (a ∈ R):
    let a := -1.0;
  return;
return;
rule main:
  print Bad().area();
return;
runtime error: "area" fails to ensure condition 1

A missing trait method (D84)

trait Shape:
  rule area() => (a ∈ R);
  rule name() => (n ∈ S);
done;
rule Dot() => (self ∈ Dot <: Shape):
  new self.x := 0;
  rule .area() => (a ∈ R):
    let a := 1.0;
  return;
return;
rule main:
  print Dot().area();
return;
error[E259]: "Dot" does not provide "name", which trait Shape requires
  = help: declare `rule .name(...)` inside the constructor (§16.3)

A ∀ loop that is not independent (D82)

rule main:
  new total ∈ Z;
  for ∀ i ∈ (1..4) do
    let total += i;
  repeat;
  print total;
return;
error[E258]: a ∀ loop cannot write "total", which it does not own
  = help: write only locals, or an element selected by the loop variable (D82)

Outside a geospatial domain (D83)

rule main:
  new bad: 200 ∈ Λ;
  print bad;
return;
runtime error: 200 is above the domain of Λ

Read but never assigned (D79)

rule main:
  new forgotten ∈ Z;
  print forgotten;
return;
error[E254]: "forgotten" is read but never given a value
  = help: it holds its type's zero; assign it, or give it an initial value at the declaration (D79)

Results dropped silently (D80)

rule value() => (r ∈ Z):
  let r := 7;
return;
rule main:
  apply value();
return;
error[E255]: "value" returns r, which this discards
  = help: bind the results, or write `apply _ := value(...)` to discard them on purpose (D80)

Use after move (D81)

rule main:
  new a := [1,2];
  new b := a!;
  print b;
  print a;
return;
error[E257]: "a" was moved and no longer holds a value
  = help: give it a new value before reading it again (D81)

Forward declaration never defined (D71)

rule ghost(x ∈ Z) => (y ∈ Z);
rule main:
  print 1;
return;
error[E249]: "ghost" is declared but never defined
  = help: a forward declaration needs a matching rule body (§9)