-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(examples:99_bottles): rewamped with while and relay
- Loading branch information
Showing
2 changed files
with
45 additions
and
52 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# About | ||
|
||
"99 Bottles of Beer" is a classical programming task that utilizes loops, conditions and io. You can see the details [here](https://www.99-bottles-of-beer.net). | ||
|
||
## Implementation | ||
|
||
1. It seems obvious to use `range` with `for`, but without topology-level loop we will have concurrency at the level of `Next2Lines`, even though it's a pipeline. See [github issue](https://github.com/nevalang/neva/issues/754) for details. That's why we used `While` instead, it was implemented exactly to solve this problem with truly sequential looping. | ||
2. Because of use of `While` we need to implement our `FirstLine` and `SecondLine` in a way that they receive data, perform side-effect and _then_ pass that data further downstream. However, because of impossibility to same sender twice, we would have to use explicit locks. That's why we use `Relay` - it's a HOC that does that for us under the hood. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,69 +1,54 @@ | ||
// https://www.99-bottles-of-beer.net | ||
|
||
def Main(start) (stop) { | ||
// we use explicit lock to implement fan-in to printNext2Lines | ||
Switch<int>, PrintNext2Lines, Lock<int> | ||
--- | ||
:start -> lock:sig | ||
99 -> lock:data | ||
[lock:data, switch:else] -> printNext2Lines | ||
printNext2Lines -> switch:data | ||
-1 -> switch:case[0] -> :stop | ||
While<int>{Next2Lines} | ||
--- | ||
:start -> { 99 -> while:from } | ||
0 -> while:to | ||
while:res -> :stop | ||
while:err -> panic | ||
} | ||
|
||
def PrintNext2Lines(n int) (n int) { | ||
Dec<int>, PrintFirstLine, PrintSecondLine | ||
--- | ||
// printFirstLine and printSecondLine won't work in parallel | ||
// because they are in the loop at the level of Main | ||
:n -> printFirstLine -> dec -> printSecondLine -> :n | ||
def Next2Lines(data int) (res int, err error) { | ||
first Relay<int>{FirstLine}? | ||
dec Dec<int> | ||
second Relay<int>{SecondLine}? | ||
--- | ||
:data -> first -> dec -> second -> :res | ||
} | ||
|
||
// === First Line === | ||
|
||
const firstLine1 string = '$0 bottles of beer on the wall, $0 bottles of beer.\n' | ||
const firstLine2 string = '1 bottle of beer on the wall, 1 bottle of beer.' | ||
const firstLine3 string = 'No more bottles of beer on the wall, no more bottles of beer.' | ||
|
||
def PrintFirstLine(n int) (n int) { | ||
Switch<int>, p1 Println, p2 Println, Printf, Lock<int>, Panic | ||
--- | ||
:n -> [switch:data, lock:data] | ||
|
||
0 -> switch:case[0] -> { $firstLine3 -> p1 } | ||
1 -> switch:case[1] -> { $firstLine2 -> p2 } | ||
switch:else -> [ | ||
printf:args[0], | ||
{ $firstLine1 -> printf:tpl } | ||
] | ||
|
||
[p1, p2, printf:sig] -> lock:sig | ||
printf:err -> panic | ||
lock -> :n | ||
def FirstLine(n int) (sig any, err error) { | ||
p1 Println, p2 Println, p3 Printf? | ||
--- | ||
:n -> switch { | ||
0 -> { $firstLine3 -> p1 } | ||
1 -> { $firstLine2 -> p2 } | ||
else -> [ | ||
p3:args[0], | ||
{ $firstLine1 -> p3:tpl } | ||
] | ||
} | ||
[p1, p2, p3] -> :sig | ||
} | ||
|
||
// === Second Line === | ||
|
||
const secondLine1 string = 'Take one down and pass it around, $0 bottles of beer on the wall.\n\n' | ||
const secondLine2 string = 'Take one down and pass it around, 1 bottle of beer on the wall.\n' | ||
const secondLine3 string = 'Take one down and pass it around, no more bottles of beer on the wall.\n' | ||
const secondLine4 string = 'Go to the store and buy some more, 99 bottles of beer on the wall.' | ||
|
||
def PrintSecondLine(n int) (n int) { | ||
Switch<int>, p1 Println, p2 Println, p3 Println, Printf, Lock<int>, Panic | ||
--- | ||
:n -> [switch:data, lock:data] | ||
|
||
-1 -> switch:case[0] -> { $secondLine4 -> p1 } | ||
0 -> switch:case[1] -> { $secondLine3 -> p2 } | ||
1 -> switch:case[2] -> { $secondLine2 -> p3 } | ||
|
||
switch:else -> [ | ||
printf:args[0], | ||
{ $secondLine1 -> printf:tpl } | ||
] | ||
|
||
[p1, p2, p3, printf:sig] -> lock:sig | ||
printf:err -> panic | ||
lock -> :n | ||
def SecondLine(n int) (sig any, err error) { | ||
p1 Println, p2 Println, p3 Println, p4 Printf? | ||
--- | ||
:n -> switch { | ||
-1 -> { $secondLine4 -> p1 } | ||
0 -> { $secondLine3 -> p2 } | ||
1 -> { $secondLine2 -> p3 } | ||
else -> [ | ||
p4:args[0], | ||
{ $secondLine1 -> p4:tpl } | ||
] | ||
} | ||
[p1, p2, p3, p4] -> :sig | ||
} |