Up to Main Index Up to Journal for April, 2023 JOURNAL FOR SUNDAY 30TH APRIL, 2023 ______________________________________________________________________________ SUBJECT: Debugging, examples, testing and comparisons DATE: Sun 30 Apr 19:42:38 BST 2023 Just time for one last post before the end of the month. I’ve had some more comments on my “What makes a minimal programming language?”[1]. Specifically I was told I missed out debugging. Being able to debug is incredibly useful, more so when developing a language. However, I still tend to use my preferred technique most of the time — printf. If it’s good enough for Brian[2] it’s good enough for me :) Saying that, Mere does have dump and trace built-ins. I had a lot of comments to do with the example I included last time[3]. I think people missed the whole point of the example. I know the code was horribly inefficient. It was intended to show how variables holding a label could be used with a generic handler subroutine. If it’s just efficiency you want, here you go: >cat timesTable.mr /* A mere multiplication table generator... This program displays a multiplication table of size ROWS×COLS. */ ROWS=12 COLS=12 print " ×" x = 1; y = 0 yloop: xloop: v = y * x if y == 0; v = x if x == 0; v = y printf "%4d" v if x++ <= COLS; goto xloop x = 0 println if y++ <= ROWS; goto yloop >mere timesTable.mr × 1 2 3 4 5 6 7 8 9 10 11 12 1 1 2 3 4 5 6 7 8 9 10 11 12 2 2 4 6 8 10 12 14 16 18 20 22 24 3 3 6 9 12 15 18 21 24 27 30 33 36 4 4 8 12 16 20 24 28 32 36 40 44 48 5 5 10 15 20 25 30 35 40 45 50 55 60 6 6 12 18 24 30 36 42 48 54 60 66 72 7 7 14 21 28 35 42 49 56 63 70 77 84 8 8 16 24 32 40 48 56 64 72 80 88 96 9 9 18 27 36 45 54 63 72 81 90 99 108 10 10 20 30 40 50 60 70 80 90 100 110 120 11 11 22 33 44 55 66 77 88 99 110 121 132 12 12 24 36 48 60 72 84 96 108 120 132 144 This shaves off 6499 instructions executed — about a 70% reduction, compared to the original version. Average runtime of this version on the command line, not Mere ICE, is 715μs (0.000715s). The original took 1579μs, or 1077μs using the dim built-in. Interestingly a Go equivalent[4] of this version still takes an average of 414μs. I’m very surprised the difference between the Mere and Go versions isn’t larger[5]. But as I said, efficiency wasn’t the point of the example… Finally, people want to know why the tests are still holding everything up :( First of all, adding the new unsigned integer type effects nearly every single operator and built-in. This means all of the tests also have to be updated. Secondly, the tests increase exponentially. For example, if I have some simple multiply tests: // multiply literal with literal x = 3 * 5; assert x 15 x = 3.3 * 5.5; assert x 18.15 x = "a" * 3; assert x "aaa" x = 3 * "a"; assert x "aaa" We now need to add the permutations for unsigned integer as well: // multiply literal with literal x = 3 * 5; assert x 15 x = 3u * 5u; assert x 15u x = 3.3 * 5.5; assert x 18.15 x = "a" * 3; assert x "aaa" x = 3 * "a"; assert x "aaa" x = "a" * 3u; assert x "aaa" x = 3u * "a"; assert x "aaa" For this simple example that’s another 3 tests or an increase of 75%. There are tests for multiplying literals with anonymous values, literals with variables, anonymous values with variables, variables with variables… Now apply that to all of the tests, for all of the operators and all of the built-ins. That’s a lot of work! I also don’t like the grind of writing tests, so that does not help. I do know the benefit of having the tests, which is why I force myself to write them :| Comparing the equivalent Mere and Go versions of the code side by side, I think I actually prefer the Mere version… Writing this has been fun, but now it’s back to updating those damn tests. -- Diddymus [1] What makes a minimal programming language?: /journal/2023/4/23.html [2] “The most effective debugging tool is still careful thought, coupled with judiciously placed print statements.” — Brian Kernighan, Unix for Beginners. [3] You want more examples!?: /journal/2023/4/27.html [4] Go equivalent using for loops and switch: package main import ( "fmt" ) const ( rows = 12 cols = 12 ) func main() { var ( v int X = 1 ) fmt.Print(" ×") for y := 0; y <= rows; y++ { for x := X; x <= cols; x++ { switch { case y == 0: v = x case x == 0: v = y default: v = y * x } fmt.Printf("%4d", v) } X = 0 fmt.Println() } } [5] Full disclosure: I did write versions of this program for Mere and Go that concatenated a string and printed the results only at the end. Mere averaged 402μs and Go 132μs. If the Go version used string.Builder instead of concatenation it dropped to 60μs. However, can the versions still considered equivalent at that point? Up to Main Index Up to Journal for April, 2023