Last month I got to use ArkScript, a language I’ve been developing for nearly 7 years, for the Advent of Code. And this time, I got to the end, using only my language (and a few hints from programming.dev)!
Adding attributes to functions’ arguments
Advent of code challenges are often heavy on lists, with inputs sometimes being thousands of lines long. After benchmarking some of my solutions, I noticed passing big lists to helper functions (eg a (get grid x y) that would check for width and height) was slower than doing the check in place and duplicating code. This is due to ArkScript “no hidden behaviour” rule: arguments are always passed by value, and no hidden references are used.
This was now a problem, and after years of refusing to alter the function declaration syntax, I cave and added function arguments’ attributes: we can declare an argument as mutable (all arguments are immutable by default inside a function body) or as a read-only reference, on a per argument basis:
| |
Better UX with type errors
ArkScript being a dynamic language, type checking is done at runtime. And I hit a few too many unhelpful type errors during the challenge, so I rewrote the error generator to be clearer:
Before
| |
Hard to know what were the arguments, where we messed up.
After
| |
We now show the how the function was called, the signature, and details about each argument, with source code location!
Fixing the testing library
Running advent of code solutions often takes time, and even more when you add tests to ensure your solver is correct for edge cases. That’s when I noticed some code was being run twice when a test case failed.
Test cases are declared using the test:eq, test:neq and test:expect macros. Since they are macros, they blindly paste their arguments inside a (if (= expected value) (report_success) (report_error expected value)), and now we have our solver running twice when there is an error, because we passed a function call to test:eq:
| |
Fixing this bug required evaluating expressions once with some trickery:
| |
Calls to print… sometimes fail?
ArkScript has an optimization for builtins, so that we can create proxies in the standard library .ark source files (which are then available through glob and name imports):
| |
Would be optimized to:
| |
Instead of:
| |
CALL_BUILTIN_WITHOUT_RETURN_ADDRESS is a call instruction that doesn’t push/pop arguments to/from the stack, avoiding a costly context switching. However, the optimizer tried to apply the optimization nearly everywhere, instead of just at the beginning of a bytecode page (which maps to a function). The following code would be optimized, and the RET instruction not being present would break everything:
| |
Better standard library
Doing the challenge this year was way easier than last year thanks to the state of the standard library, however there was still room for improvement:
list:forEach,list:windowandlist:enumeratecan take functors returninglist:stopIterationto abort iteration earlylist:sortByKeylist:transposeto transpose a list of lists or list of stringslist:contains?to check if a list has a given elementlist:combinations,list:combinationsWithReplacementsto generate combinations sequenceslist:selectto select elements from a list by a list of indexesio:readLinesFileunpackPairmacro to unpack a list of two elements into variables
What’s next?
I’d like to enhance the small framework I made for solving Advent of code challenges in ArkScript, and perhaps use it to complete the previous years challenges too! This is a fun way of playing with algorithms, and it helps find areas where the language could be improved.