Mailing List Archive


[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [tlug] Constructed state, unit tests and REPLs



On 2020-09-04 09:06 +0200 (Fri), Josh Glover wrote:

> What's a mouse? ;)

It's that big red thing between your G, H and B keys that lets you jump to
any of the dozen windows covering your 4K monitor with just a flick of your
finger. :-)

> Because I don't know what functions I need ahead of time, and I don't
> know what properties they should have.

Ah, _there's_ the problem. If you don't know what functions you need, it's
easy to figure that out. Write the code that calls the function you need
and then that code will tell you.

Switching from bottom-up to top-down programming was certainly one of the
best things I ever did as far as programming productivity. My lower-level
APIs are now a lot easier to use and they take a lot less futzing around to
write.

> As for retyping, my REPL has history and structural editing and all that
> good stuff, so to the extent that I evaluate the same or similar
> s-expressions multiple times, it's not onerous to do so. It's more onerous
> to write a test in a file....

Well, obviously I'd have to see how this works really to understand it, but
my first reaction on hearing this is that if I found editing code in the
REPL easier than editing code in a file, I'd seriously consider ditching my
editor and just typing _all_ the code in the REPL, dumping out the
functions once I'd done with them. After all, both the functions and the
calls you make to them are just code, in the same language.

But:

> In lisp, I'm not switching to the REPL per se. Often I'm just working in
> my source buffer, evaluating expressions and seeing the result.

Oh, so isn't that exactly what happens when you write unit tests? I type
something like:

    assert [] == make_some_list_from(do_stuff_to(some_data))

into my `foo.pt` file, and upon saving I immediately see what
`make_some_list_from()` returned (unless it returned `[]`, in which case I
feel pretty comfortable not seeing that actually printed out :-)). I also
see what `do_stuff_to()` returned, and what its argument `some_data` was.

And of course at any time I can stuff some print() statements into the test
and print out anything that's available in that particular test context.
Basically, it's like a REPL except that you use a real editor to edit
multi-line documents, you easily have multiple contexts available with
whatever setup you need (sometimes quite complex), and all this is still
there even on another computer after you do a `git fetch`.

> ...that might become useless moments after I write it because of a faulty
> assumption about the shape of the return value from AWS or something.

Yeah, I'm not really seeing the difference here with what I do. If I
weren't sure about the shape of a return value, I'd probably just assert it
equal to `[]` and then edit that later to whatever actually gets spit out.

> Absolutely! That's why I write the tests and docs after the code, so that I
> don't have to spend time updating them after I realise that my initial
> design and assumptions were incorrect somehow.

Yeah, but how do you write the code without an initial design in mind?

As one example, In that BASIC detokenizer/tokenzer I've been working on, I
need to deal with three character sets: Unicode, the screen character set
of the computer, and the ASCII character set that BASIC uses. In addition
there's an encoding for the screen character set in BASIC because strings
in BASIC have an additional 32 control characters they can use that are not
part of the screen character set.

There's no way any amount of futzing around with low-level functions to
convert between BASIC-encoding screen chars and screen char code points and
Unicode and all could lead me to an appropriate model for this whole thing.
To get there I had to write the user documentation explaining what the
detokenizer actually does. I didn't understand, for example, BASIC actually
uses both the screen character set _and_ ASCII until I explained to the
end-user that if you redefined screen charset code point 0x50 to be `≠`,
the detokenizer would output `PRINT"≠"`, not `≠RINT"≠"`, obviously, even
though `P` and `≠` are both represented by the number 0x50.

> > Well, we do live in such a world so long as you're willing to use a few
> > tmate windows and Telegram voice chat.
>
> This would be interesting. I'll put it on my backlog and see if my free
> time lines up with your working time. :)

Well, I definitely have a little project in mind that I'd like to see
sketched out in Racket.

cjs
-- 
Curt J. Sampson      <cjs@example.com>      +81 90 7737 2974

To iterate is human, to recurse divine.
    - L Peter Deutsch


Home | Main Index | Thread Index