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