Boon

Timeless & Playful Language
noob or hacker, web or chip,
boon will guide your coding trip
First Look
1...  2...  3...  every second

1...  2...  3...  on a button click

You can try out the examples on play.boon.run
Super Counter
- Do you like open source?
- Do you like small computers like Raspberry Pi?
- Do you like hardware or FPGA boards like iCESugar?

- Would you like to program and configure everything in one language?
- Would you like to unplug it whenever you want to without losing data?

What's on that diagram? Super counter!
- You press a button on the iCESugar dev board.
- The board sends a message to the Raspberry Pi.
- The computer increments the counter in its database.
- The board, the CLI (command-line interface), and the web app are notified that the counter value has changed.
- The board turns on the LED for a moment to signal that the counter has been incremented.
- The web app updates the rendered counter value and changes its color according to the defined script rules (e.g. green when the counter is less than 10, otherwise red).
- The CLI displays the new counter value and runs a configured command (e.g. show a notification or write something into the terminal/console).
- You can watch how data flows through the entire Super counter on the Monitor.
Flow Combinators
LATEST

- The first value flying in is the first one flying out.
- When it's not possible to determine which value arrived first, the order in the LATEST block matters: LATEST { first, second }.
- The LATEST combinator also works across code changes. That means when you run the code LATEST { 1, 2 }, change the code toLATEST { 3, 2 }, and run the program again, the values coming out of that combinator will be:1, then2, and then3. Number2hasn't changed, so it doesn't fly out on the second run.

- EXTRA: Math/sum(increment<Number>) -> Numberand other function calls, operators and combinators are actually actors. It means a stream of data flows inside and outside of them and they keep their internal state. In the case ofMath/sum, it remembers the current sum and outputs it on every change, that is, on every new incoming value.

WHILE

- Let's say you have sensors counting cars on two roads:- input_ais the changing sum of the cars passed on road- A
- input_bis the changing sum of the cars passed on road- B
 
- Both inputs are connected to your fancy computer, where you can switch between their:- Addition- to know how many cars were on both roads
- Subtraction- to know which road is more used
 
- That means when input_ais15andinput_bis7, you can see either22or8, and the displayed value changes in real time with every car that passes by.

WHEN

- Bob doesn't have such a fancy computer. Bob has a paper and a pen.
- Every time he wants to record AdditionorSubtraction, he has to write the result down.
- That means he loses input values between entries, but the written results never change.
- Everything inside the WHEN { ... }block is "frozen in time" when an input value arrives (SuborAddon the diagram below,operationin the code above) - values are copied, and they no longer change according to their dependencies (AandBin the diagram,input_aandinput_bin the code).

THEN

- Alice only has paper and a pen like Bob, but she writes down only additions.
- THENis basically- WHENwithout arms (- Abc => 123). You can replace- THEN { 123 }with- WHEN { __ => 123 }; that means you don't care about the actual input value, you just want to do something when a new input arrives.

Summary
- Data flow continuously WHILE an arm is selected.
- Data are copied WHEN an arm is selected.
- Input arrives, and THEN data are copied.
Real-world examples
- When the user presses Enter or clicks a send button in your chat application, use WHEN or THEN to copy the message written in the new-message input. With the WHILE combinator, you'd risk changing already sent messages with every change to the text input.
- Use WHILE to update texts in your multilingual application. With WHEN/THEN you'd update all dynamic texts only when the user decides to switch languages.
Durable State & Code Changes
Every program is a living organism, sooner or later you'll want to change its code but ideally not lose any data during the process or tell your users to not use your app during the weekend while you are "migrating data".
Let's say we want to upgrade our simple counter example - rename the variable counter to counter_2 and increment its value by 2 on a button click.
It would be a pretty straightforward operation, but we don't want to reset our counter value while deploying a new app version.
You can go to play.boon.run, click the button counter.bn in the header and follow the steps below with me.
NOTE: You'll write pipe operator ▷ by typing | and >. Boon Playground uses the JetBrains Mono font with enabled ligatures to combine them visually. I would also like to add support for infinite length ligatures to that font to have nice continuous lines for comments / dividers.
- Look at the original counter code, run it and press the +button to change counter state a bit. 
- Add the counter_2definition, replacecounterwithcounter_2in HTML document items, and prevent the oldcounterfrom listening to button-press events. Notice oldcounterincounter_2's LATEST block to do the actual state migration from the old counter to the new one. Then run the example again. Nothing should change visually, but you're already using the upgraded app. 
- Remove the references to the old counter, delete its definition, and we are done! Run the example and click+to verify that it's incremented by 2. 
- When you want to reset the counter, click the Clear saved states button just above the preview pane on the playground to remove all states stored in the browser's LocalStorage, and then click the Run button to restart the app; the counter resets back to 0.
EXTRA: Variables in our playground examples are stored in the browser. However, the general idea is that some variables in your app will be stored in the browser, some variables in a standard database, and some of them nowhere to save memory or quickly forget things like passwords or tokens.
All roads lead to Rome document

Look at that counter example dataflow diagram again. Do all paths really lead to the document? Almost! The only exception is that blue LINK rectangle at the bottom left corner coming from the Element/button(..) function call. 
What is that LINK good for? Why does it make the only loop in the entire diagram to ruin my otherwise perfect tree?!
Look at the counter code again:

- Notice the press: LINKobject field being passed to theElement/buttonfunction call as part of theelementargument.
- Function Element/buttontransforms the arguments into a data structure compatible with the element tree and digestible by theDocument/newfunction.
- Boon browser runtime finds the global documentvariable and creates browser elements described in the passed element tree. When it findsevent.press, it links it with events produced by those browser HTML/DOM elements.
- When linked, you can listen for new events with code like my_button.event.press |> THEN { .. }
So, for our current understanding, variable_name: LINK basically means that the variable's value can be set after the variable is defined - no other variables can be set once they are defined.
Finally, perfect trees!
We cannot really get rid of all loops in dataflow graphs - they are a natural part of programs and the things we do - we can only monitor them and try to remove accidental infinite loops.
However, look at this nice forest!:

- In Boon, every piece of state has a place in the ownership hierarchy. Detach an object or a list, and all of its descendants vanish with it. Nothing is “freed” manually — the runtime simply drops what’s no longer owned, whether data or actors.
- Global variables - document,counter,increment_button- own all other items in our counter example. Everything has only one owner: the place where it was defined; everything else is just a reference marked with dashed arrows.
- That's why LINKis actually a link/reference - the variable defined withLINKdoesn't own the linked browser element (notice the blue dashed OBJECT at the bottom of the state diagram above).
Enough diagrams! More code!

PASS + PASSED
Notice in the code above:
- root_element(PASS: store)
- FUNCTION root_element()
- PASSED.elements.increment_button
The only purpose is to pass data through multiple function calls without the need to mention them explicitly among function arguments.
Without PASS + PASSED, the same lines would look like:
- root_element(store: store)or- store |> root_element()
- FUNCTION root_element(store)<- new function argument
- store.elements.increment_button
So PASS + PASSED is useful when you have deep function call tree (typically element tree) and bottom levels need something from top levels.
LINK { .. }
You already know what variable: LINK means and now you'll find out how to set it by yourself (instead of setting it by the Boon runtime). 
Notice these lines:
- decrement_button: LINK
- counter_button(label: '-') |> LINK { PASSED.elements.decrement_button }
The element data returned from the counter_button(..) function call are linked to decrement_button and returned from LINK {} without any changes. 
Where is Fibonacci??

This idea was driven by design decisions to avoid recursions and keep loops as tight and hidden as possible. However, I want the Boon design to be driven by real applications, so I'll revisit this API/example when the need for channels, (infinite) streams, generators, lazy evaluation, tail recursions, or other related concepts emerges.
See the Problem, Fix the Flow
- 
A monitor/debugger is your friend. Have you ever played a Factorio-like game? I want to: - See the problem.
- See statistics.
- Be able to immediately fix the problem.
- Want to know why the problem happened.
- Want to see slow parts.
- Want to see loops.
- Want to see what is waiting and why.
- Want to just watch and enjoy it while everything works as expected.
- Want to be notified when something fails.
 Yes, you understand correctly, monitoring and a short feedback loop have high priority for Boon tools and design in general. 
- 
A compiler is your friend. Nice error messages, fast type checking, hints. 

- A formatter is your friend. No need to think about the correct number of spaces. No confusing code diffs. Constant reading speed for a code reviewer.
Status & Future
A lot of things have to be implemented and explained, but the core is there and I don't plan to stop!
Examples on play.boon.run are guaranteed to run. I'll continuously add more there, and examples running outside the browser environment will appear as well.
Questions ▷ martin@kavik.cz
Credits
- ASCII Art: patorjk.com + Mono 12