←back to thread

88 points joecobb | 1 comments | | HN request time: 0.202s | source
Show context
Jtsummers ◴[] No.46183821[source]
Perhaps the most prominent example of literate programming missed by the author: https://www.pbrt.org/ Physically Based Rendering by Pharr, Jakob, and Humphreys.

Responding directly to a couple things the author wrote:

> When programming, it’s not uncommon to write a function that’s “good enough for now”, and revise it later. This is impossible to adequately do in literate programming.

It's not impossible in literate programming. There's nothing about LP that impedes this, I do it all the time. I have a quick obvious implementation (perhaps a naive recursive solution) and throw it in to get things working. I revisit it later when I need to make that naive recursive one faster (memoization, DP, or just another algorithm all together). It's no harder than what I'd do with an ordinary approach to programming.

> Unit testing is not supported one bit in WEB, but you can cobble something together in CWEB.

WEB was designed for use with Pascal and CWEB for C and C++. At the time the tools were developed, "unit testing" as it means today was not really a widespread thing. Use other tools if you find that WEB is impeding your use of unit tests in your Pascal programs. With other tools (org-mode and org-babel are what I use), it's easy to do. Like with writing good enough functions, you just do it, and it's done. You write a unit test in a block of code and when it gets tangled you execute your unit tests. This can be more cumbersome in some languages than with others, but in Python it's as easy as:

  #+BEGIN_SOURCE python :noweb yes :tangle test/test_foo.py
    from hypothesis import ...
    from pytest import ...
    <<name_of_specific_test>>
    <<name_of_other_test>>
  #+END_SOURCE

  #+NAME: name_of_specific_test
  #+BEGIN_SOURCE
    def test_frob(...):
        ...
  #+END_SOURCE
When I used LP regularly I had a little script I wrote that would tangle source from my org files, and because I had the names and paths specified everything would end up in the right place. This is followed by running `pytest` (or whatever test utility) as normal. I used this in makefiles and other scripts. This is only slightly harder than the normal approach, but not hard. I added a `tangle` step into my build and test process and it was good to go.

If your unit test system requires more ceremony then you'll need to include that as well, but you'd have to include that in your conventionally written code as well.

replies(1): >>46196594 #
cxr ◴[] No.46196594[source]
> It's not impossible in literate programming. There's nothing about LP that impedes this, I do it all the time. I have a quick obvious implementation (perhaps a naive recursive solution) and throw it in to get things working. I revisit it later when I need to make that naive recursive one faster (memoization, DP, or just another algorithm all together). It's no harder than what I'd do with an ordinary approach to programming.

Do you do it as literate program, which is to say, your text first expounds on a naive solution to the problem, and then later outlines for the reader the complete solution?

Or do you write one literate program, and then update the file containing the text of your program after you realize the better solution—no different from anyone writing conventional programs—thus thwarting the fundamental promise of LP (since the "why" of changes like that—and the absence of answers to those whys—is exactly what makes it such a battle to understand unfamiliar programs).

The author is very obviously talking about the first problem. Your comments and failure to elaborate strongly suggest you're doing the latter.

replies(1): >>46196882 #
Jtsummers ◴[] No.46196882[source]
I've done both.

> your text first expounds on a naive solution to the problem

That's not how every literate program is written, nor is it the entire purpose of literate programming.

> Or do you write one literate program, and then update the file containing the text of your program after you realize the better solution—no different from anyone writing conventional programs—thus thwarting the fundamental promise of LP (since the "why" of changes like that—and the absence of answers to those whys—is exactly what makes it such a battle to understand unfamiliar programs). [emphasis added]

I think you misunderstand LP. Keeping history is not essential to literate programming. Having a series of evolving programs from naive to fastest possible is not the point of LP. Having an explanation of the program with the code is the point of LP.

Sometimes that means leaving history, sometimes it means removing it. A literate program is not a dead program. It's a living object that can be edited and changed over time. Keep the elements that are essential to the text. If the naive version is clearer (often it is) then retain it so you can explain the more complex fast version as it relates to the naive version. If it has no explanatory value, then drop it. If you implemented something incorrectly, it's not necessarily worth keeping unless the wrongness of it has value in itself.

replies(2): >>46197144 #>>46197219 #
1. ◴[] No.46197144[source]