Question

I'm interested in trying literate programming. However, I often that the requirements are stated in a general but then exceptions are given much later.

For example in one section it will say something like Students are not allowed in the hallways while classes are in session.

But then later there will be section where it says something like Teachers may give a student a hall pass at which point the student may be in the hall while class is in session.

So I'd like to be able to define allowedInTheHall following the first section so that it doesn't allow students in the hall, but then after the second section redefines allowedInTheHall so that it first checks for the presence of a hall pass, and if it's missing then delegates back to the previous definition.

So the only way I can imagine this working would be a language where:

  1. you can redefine a method/function/subroutine in terms of it's previous definition
  2. where only the latest version of a function gets called even if the caller was defined before the latest redefinition of the callee (I believe this is called "late binding").

So which languages fulfill support these criteria?


PS- my motivation is that I am working with existing requirements (in my case game rules) and I want to embed my code into the existing rules so that the code follows the structure of the rules that people are already familiar with. I assume that this situation would also arise trying to implement a legal contract.

Was it helpful?

Solution

Well to answer the direct question,

you can redefine a method/function/subroutine in terms of it's previous definition

...in basically any language, as long as it supports two features:

  1. mutable variables that can hold function values
  2. some kind of closure forming operator, which effectively amounts to the ability to create new function values

So you can't do it in C, because even though it allows variables to store function pointers, there's no operation in C that can compute a new function value; and you can't do it in Haskell because Haskell doesn't let you mutate a variable once it's been defined. But you can do it in e.g. JavaScript:

var f1 = function(x) {
    console.log("first version got " + x);
};

function around(f, before, after) {
    return function() {
        before(); f.apply(null, arguments); after();
    };
}

f1 = around(f1,
            function(){console.log("added before");},
            function(){console.log("added after");});
f1(12);

or Scheme:

(define (f1 x) (display "first version got ") (display x) (newline))
(define (around f before after)
   (lambda x  
      (before) (apply f x) (after) ))
(set! f1 (around
  f1
  (lambda () (display "added before") (newline))
  (lambda () (display "added after") (newline))))
(f1 12)

...or a whole host of other languages, because those are really rather common features. The operation (which I think is generally called "advice") is basically analogous to the ubiquitous x = x + 1, except the value is a function and the "addition" is the wrapping of extra operations around it to create a new functional value.

The reason this works is that by passing the old function in as a parameter (to around, or just a let or whatever) the new function is closing over it referred to through a locally scoped name; if the new function referred to the global name, the old value would be lost and the new function would just recurse.

Technically you could say this is a form of late binding - the function is being retrieved from a variable rather than being linked in directly - but generally the term is used to refer to much more dynamic behaviour, such as as JS field access where the field might not even actually exist. In the above case the compiler can at least be sure the variable f1 will exist, even if it turns out to hold null or something, so lookup is fast.

Other functions that call f1 would work the way you expect assuming that they reference it by that name. If you did var f3 = f1; before the around call, functions defined as calling f3 wouldn't be affected; similarly objects that got a hold of f1 by having it passed in as a parameter or something. Basic lexical scoping rule applies. If you want such functions to be affected too, you could pull it off using something like PicoLisp... but you're also doing something you probably shouldn't (and that's not any kind of binding any more: that's direct mutation of a function object).


All that aside, I'm not sure this is in the spirit of literate programming at all - or for that matter, a program that describes rules. Are rules supposed to change depending on how far you are through the book or in what order you read the chapters? Literate programs aren't - just as a paragraph of text usually means one thing (you may not understand it, but its meaning is fixed) no matter whether you read it first or last, so should a declaration in a true literate program, right? One doesn't normally read a reference - such as a book of rules - from cover to cover like a novel.

Whereas designed like this, the meaning of the program is highly dependent on being read with the statements in one specific order. It's very much a machine-friendly series-of-instructions... not so much a reference book.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top