Make explicit in the type system when evaluation of a computation should be delayed. This gives the user fine-grained control over selective laziness (for example, once we have sum types and recursive types, one could use this to define lazy infinite data structures). It also allows us to guarantee that certain commands such as `build` and `reprogram` delay evaluation of their arguments, and lets the user e.g. define their own modified versions of `build` without compromising those guarantees.
- Delay is indicated by curly braces both at the value and type levels, that is, if `t : ty` then `{t} : {ty}`.
- `force : {ty} -> ty` is now exposed in the surface language.
- Change from a CEK machine to a CESK machine. Recursive `let` and `def` delay via allocating a cell in the store. For now, there is no other way to allocate anything in the store, but see discussion at #150 for some possible future directions.
- change the types of `build` and `reprogram` to require a delayed program, e.g. `build : string -> {cmd a} -> cmd string`
- `if` and `try` also require delayed arguments.
- don't elaborate Build and Reprogram with extra Delay wrappers since one is now required by the type
- Division by zero, negative exponents, and bad comparisons now throw exceptions.
Closes#150. Closes#226.
Resolves#228.
I've redefined the missing lens operators in `Utils.hs` in order to keep the diff of the function bodies minimal. Hopefully that will make merges and rebases less painful. Most of the type signatures are modified though, we can't avoid this.
Closes#180
Add `Integration.hs` which reads files from `example` folder and runs `processTerm` on the contents. The test passes if creating a term is successful.
Currently only one example i.e. `zigzag.sw` is failing.
Adds sum types (e.g. `int + bool`) along with constants `inl : a -> a + b`, `inr : b -> a + b`, and `case : a + b -> (a -> c) -> (b -> c) -> c`.
I considered whether to automatically wrap the arguments to `case` in `delay`, like the arguments to `if`, but they are already functions anyway so we get a lot of laziness for free. It would only make a difference if you wrote something like `case foo (let x = blah in \y. ...) (...)` in which case the `let x = blah` is going to be evaluated eagerly, *before* we know which branch we are going to take. I don't think it's worth bothering about. In the normal case that you write `case foo (\y. ...) (\z. ...)` then only one of the two functions gets run at all, without us having to do anything special with `delay`.
I also considered whether to make special syntax for case expressions, like `case foo of { inl x -> ... ; inr y -> ... }` but doing it as a simple built-in function is both easier to implement and feels like it fits better with the aesthetic of the language so far.
Closes#46 .
Since the base was not put into the active robots set, it never made
any progress on making the recipe.
Fixes#227. This just goes to prove @polux's point in #217.
There are still some things missing (e.g. operators) but this is a start. Some remaining issues:
* We should look into auto-generating the lists of keywords so we don't have to manually update every time we add new commands. Or (fancier) maybe the swarm executable can have a special flag to make it spit out lists of command names, so the emacs mode can dynamically find out what to highlight?
* I am not sure how to get it to highlight both single-line and multi-line comments at the same time. It must be possible since e.g. `java-mode` does it. We could also change the comment syntax, I really don't care that much.
Cap the maximum number of ticks per frame (arbitrarily) at 30, to
prevent the "spiral of death" (see
https://gafferongames.com/post/fix_your_timestep/).
To see this in effect, go into Creative mode, crank up the ticks per
second to 32 or so, then (for suitable definitions of `repeat` and
`forever`) execute
`repeat 1000 { build "" {forever {move; turn left}}}`
Without this PR, it should eventually grind to a halt. With this PR,
the game should keep running, though with lots of lag.
Closes#42 .
Resolves#19. This is a big win for idle robots, but decreases the performance of active robots a bit.
Benchmark results on the main branch:
```
benchmarking run 1000 game ticks/idlers/40
time 18.93 ms (18.63 ms .. 19.34 ms)
0.999 R² (0.998 R² .. 1.000 R²)
mean 18.82 ms (18.69 ms .. 19.00 ms)
std dev 419.6 μs (284.8 μs .. 642.9 μs)
benchmarking run 1000 game ticks/trees/40
time 10.27 ms (10.21 ms .. 10.32 ms)
1.000 R² (0.999 R² .. 1.000 R²)
mean 10.16 ms (10.12 ms .. 10.21 ms)
std dev 142.0 μs (105.9 μs .. 211.4 μs)
benchmarking run 1000 game ticks/circlers/40
time 260.2 ms (257.9 ms .. 262.4 ms)
1.000 R² (1.000 R² .. 1.000 R²)
mean 257.5 ms (256.5 ms .. 258.9 ms)
std dev 1.817 ms (1.235 ms .. 2.674 ms)
variance introduced by outliers: 11% (moderately inflated)
benchmarking run 1000 game ticks/movers/40
time 785.9 ms (620.8 ms .. 987.0 ms)
0.992 R² (0.975 R² .. 1.000 R²)
mean 847.7 ms (802.5 ms .. 892.8 ms)
std dev 57.17 ms (30.38 ms .. 69.01 ms)
variance introduced by outliers: 20% (moderately inflated)
```
Benchmark results after removing idle robots from the active robots set:
```
benchmarking run 1000 game ticks/idlers/40
time 888.4 μs (877.9 μs .. 904.7 μs)
0.998 R² (0.996 R² .. 1.000 R²)
mean 870.3 μs (865.6 μs .. 878.8 μs)
std dev 22.49 μs (14.50 μs .. 39.52 μs)
variance introduced by outliers: 17% (moderately inflated)
benchmarking run 1000 game ticks/trees/40
time 4.075 ms (4.023 ms .. 4.118 ms)
0.999 R² (0.999 R² .. 1.000 R²)
mean 3.993 ms (3.974 ms .. 4.016 ms)
std dev 68.87 μs (55.92 μs .. 87.58 μs)
benchmarking run 1000 game ticks/circlers/40
time 362.5 ms (264.8 ms .. 492.7 ms)
0.902 R² (0.838 R² .. 1.000 R²)
mean 285.1 ms (266.2 ms .. 340.9 ms)
std dev 47.45 ms (1.859 ms .. 68.36 ms)
variance introduced by outliers: 41% (moderately inflated)
benchmarking run 1000 game ticks/movers/40
time 815.1 ms (642.8 ms .. 1.042 s)
0.991 R² (0.973 R² .. 1.000 R²)
mean 866.1 ms (819.0 ms .. 913.2 ms)
std dev 58.40 ms (28.79 ms .. 73.29 ms)
variance introduced by outliers: 20% (moderately inflated)
```
Idlers are much faster. Trees are also faster because they become idle after they're done growing. Circlers and movers are slower. Inlining the active robot check helps with the circlers:
```
benchmarking run 1000 game ticks/idlers/40
time 941.6 μs (932.4 μs .. 951.3 μs)
0.999 R² (0.999 R² .. 1.000 R²)
mean 933.1 μs (926.8 μs .. 939.9 μs)
std dev 25.30 μs (20.66 μs .. 32.12 μs)
variance introduced by outliers: 19% (moderately inflated)
benchmarking run 1000 game ticks/trees/40
time 3.925 ms (3.901 ms .. 3.948 ms)
1.000 R² (0.999 R² .. 1.000 R²)
mean 3.902 ms (3.888 ms .. 3.919 ms)
std dev 57.08 μs (47.38 μs .. 76.42 μs)
benchmarking run 1000 game ticks/circlers/40
time 266.8 ms (260.1 ms .. 275.4 ms)
0.999 R² (0.998 R² .. 1.000 R²)
mean 263.2 ms (261.6 ms .. 266.1 ms)
std dev 3.170 ms (1.914 ms .. 4.360 ms)
variance introduced by outliers: 11% (moderately inflated)
benchmarking run 1000 game ticks/movers/40
time 829.2 ms (692.9 ms .. 1.023 s)
0.994 R² (0.982 R² .. 1.000 R²)
mean 862.0 ms (822.4 ms .. 901.8 ms)
std dev 50.46 ms (21.92 ms .. 66.59 ms)
variance introduced by outliers: 19% (moderately inflated)
```
I'm not sure why the movers don't seem to benefit from the inlining.
The last commit that removes non-sensical tests (waiting after self destruction) has not effect on the performance, but cleans up the logic.
Once an entity has been discovered, it stays in the inventory, even if its count becomes 0.
I think I just deleted the code @fryguybob carefully fixed in #157... =)
Fixes#163.
The original main goals of this PR are to (1) solve #36 , that is, make the bottom-left info area fully wrapped and scrollable; and (2) solve #40 , that is, persist the info in that bottom-left info area even when switching to other panels. In order to address those I ended up making a rather large constellation of related changes, which in the end I hope work well with each other. Eager to hear others' feedback either from looking at the code or just trying out the game with this PR.
- In order to make the bottom-left area scrollable, split it out into its own panel. Now we can focus it and then use normal keybindings to scroll it.
- The bottom-left info panel now indicates whether it has additional content scrolled off the top or bottom.
- The bottom-left panel used to display a global list of log/error messages when the inventory was not focused. So where will those messages be viewable now, if the information displayed in the bottom-left panel persists? My answer: there is a special `logger` device (#39) which, when focused, now displays the log messages.
- This means that logs are now per-robot instead of global.
- The base starts with a `logger` installed.
- A `log` command can be used to write to a logger.
- Uncaught exceptions also now write to a logger (instead of using `say`), or are simply discarded if there is no `logger` device installed.
- The first time a robot's inventory is viewed after a message has been appended to its log, the `logger` device will be automatically focused and the log scrolled all the way to the bottom to show the new entry. This means that e.g. typing `move` at the REPL will immediately show an exception in the lower left.
- Add a `salvage` command, making it possible to access the log of a dead robot after the fact, once we implement #17 .
There are still a few issues to address which I think can be done in separate PRs:
- The global message log still exists, and the `say` command still appends to it, but there is no way to view it. Obviously we need to do something with this --- either get rid of it, or figure out a sensible way to view it, etc.
- Exception messages should probably be reformatted to not include any newlines, now that we know they will be wrapped.
Closes#36. Closes#40. Closes#39. Closes#21.
Improve the benchmark and add more scenarios.
Improvement are:
- Run each benchmark for 10 seconds to get enough samples in more costly scenarios
- Run benchmark on a blank terrain to avoid crashing into mountain and boulders
- Take advantage of the seed in tests using randomness
New scenarios are:
- A bunch of robots doing nothing. Will be useful for measuring the impact of removing idle robots from the active robots set.
- A bunch of robots moving in circles.
- A bunch of robots moving forward. This surprisingly is slower than moving in circles.
Resolves#20 by introducing an active robots set and a waiting robots queue as discussed in the issue.
Growing trees benchmark before change:
```
benchmarking run 1000 game ticks/10 trees
time 141.1 ms (137.3 ms .. 146.4 ms)
0.997 R² (0.995 R² .. 1.000 R²)
mean 140.1 ms (138.5 ms .. 142.2 ms)
std dev 3.807 ms (2.703 ms .. 5.189 ms)
benchmarking run 1000 game ticks/20 trees
time 298.9 ms (281.7 ms .. 326.0 ms)
0.989 R² (0.972 R² .. 0.998 R²)
mean 301.7 ms (295.0 ms .. 311.0 ms)
std dev 13.93 ms (10.02 ms .. 19.92 ms)
benchmarking run 1000 game ticks/30 trees
time 470.9 ms (432.0 ms .. 513.0 ms)
0.991 R² (0.978 R² .. 1.000 R²)
mean 450.1 ms (441.9 ms .. 468.4 ms)
std dev 18.85 ms (5.941 ms .. 30.21 ms)
variance introduced by outliers: 11% (moderately inflated)
```
After change:
```
benchmarking run 1000 game ticks/10 trees
time 4.666 ms (4.529 ms .. 4.955 ms)
0.963 R² (0.899 R² .. 0.999 R²)
mean 4.447 ms (4.361 ms .. 4.668 ms)
std dev 539.2 μs (185.5 μs .. 1.097 ms)
variance introduced by outliers: 80% (severely inflated)
benchmarking run 1000 game ticks/20 trees
time 6.955 ms (6.826 ms .. 7.122 ms)
0.996 R² (0.993 R² .. 0.999 R²)
mean 6.855 ms (6.795 ms .. 6.936 ms)
std dev 262.8 μs (197.0 μs .. 340.6 μs)
variance introduced by outliers: 25% (moderately inflated)
benchmarking run 1000 game ticks/30 trees
time 10.53 ms (9.738 ms .. 11.47 ms)
0.954 R² (0.914 R² .. 0.988 R²)
mean 9.504 ms (9.226 ms .. 9.974 ms)
std dev 1.211 ms (809.9 μs .. 1.948 ms)
variance introduced by outliers: 76% (severely inflated)
```
- adds CLI option `--seed` and propagates it to `testWorld2` (Closes#14)
- moves the base in the tree shade (Closes#90)
- makes the `random` command depend on initial seed (Closes#13)
This pull request combines two changes:
1. When the user hits "Enter" in the REPL and the entry is validated, any previous error is removed from the screen.
2. When the user hits "Enter" while the base is actively running a computation, this is ignored now without triggering a redraw. This change is based on the review comment https://github.com/byorgey/swarm/pull/181#pullrequestreview-770727562 . While using ``continueWithoutRedraw`` for this case is more verbose than factoring out ``continue`` as I did before and the performance impact is most certainly negligible, I think that this solution is semantically cleaner. Of course I am happy to roll back this change if there is a disagreement about this point.
Closes#76
Now that `constInfo` provides the name of Const values, there is no
need to re-list each command in the reserved words list. Also
represent reserved words as Text instead of String.
When you hit Enter at the REPL the REPL event handler now checks whether the base is currently running a computation. If this is the case, then the Enter key event is ignored.
Fixes#139
- allow HLS to use Cabal instead of Stack
- Cabal works fine, and I do not see any advantage to using Stack
- someone using Stack should test this does not switch him to Cabal ⚠️
- ignore `cabal.project.local` so I can put there `ghc-options: -haddock`
- this flag could lead to errors before GHC 9, but it gives me [docs on hover](https://haskell-language-server.readthedocs.io/en/latest/features.html)
This change enables the LSP server to report diagnostic on
the virtual file, without waiting for a save event.
Not sure how to correctly configure emacs (and why it is not needed
for HLS), but this only works when `flycheck-check-syntax-automatically`
has `idle-change`, e.g.:
(setq flycheck-check-syntax-automatically '(save mode-enable idle-change))