I've thought some more about this.


What this is about is a need to refer to previous values in value history in a relative way rather than referring specifically to some historic value. First a simple (and perhaps not so useful) example, where we use Heron's method to compute the square root of 9 (where $ refers to the last value):

scheme@(guile-user)> 1
$1 = 1
scheme@(guile-user)> (/ (+ $ (/ 9 $)) 2.0)
$2 = 5.0
scheme@(guile-user)> (/ (+ $ (/ 9 $)) 2.0)
$3 = 3.4
scheme@(guile-user)> (/ (+ $ (/ 9 $)) 2.0)
$4 = 3.023529411764706
scheme@(guile-user)> (/ (+ $ (/ 9 $)) 2.0)
$5 = 3.00009155413138
scheme@(guile-user)> (/ (+ $ (/ 9 $)) 2.0)
$6 = 3.000000001396984
scheme@(guile-user)> (/ (+ $ (/ 9 $)) 2.0)
$7 = 3.0

We also have the more common case that we are debugging a program and need to inspect the output, e.g (where $$0 = $ and $$1 is the value before $).:

scheme@(guile-user)> (foo 1)
$1 = #<some-object>
scheme@(guile-user)> (get-bar $$0)
$2 = ...
scheme@(guile-user)> (get-baz $$1)
$3 = ...
scheme@(guile-user)> (foo 2)
$4 = #<some-object>
scheme@(guile-user)> (get-bar $$0)
$5 = ...
scheme@(guile-user)> (get-baz $$1)
$6 = ...

The point is that we can use readline's value history to pick earlier lines with minor or no editing, i.e. we don't need to say $4 in the last two selectors. Maybe even more importantly, using relative value history references is conceptually easier, since we don't have to pay attention to the index of the value in value history.

Mark also mentioned a use case where procedures are successively built up using values from value history.


We have now discussed three different problems associated with such an extension:

1. Naming and name collisions

We currently have a GDB-compatible naming scheme where values are named $1, $2 etc. It is then natural to extend this to the full GDB value history naming scheme, introducing $, $$ and $$<N>.

$ collides with the auxilliary syntactic keyword $ in (ice-9 match). Unfortunately, the interpretation of literals in syntax-rules and syntax-case macros are influenced by bindings in the environment where macros are used (according to R5RS 4.3.2) such that value history $ will disrupt $ matching in (ice-9 match).

This can be solved by using different naming. Mark suggested that $ could be renamed to $$. I suggested a naming scheme where $-1, $-2 could be used for relative refrences. Chicken scheme uses #[N] and this could also be extended by letting negative numbers be relative references.

However, I'm thinking that it is a bit awkward to have to consider all possible uses of names when selecting this naming scheme. I would very much prefer to let a GDB user feel at home. Is there a way for names to coexist?

Well, we have the module system. Ideally, I think that value history lookups should be done in a special top level environment which is associated with the REPL. Top level bindings in the Scheme environment should have precedence. Now, instead, (ice-9 history) has a hack which patches (value-history) into every environment we visit.

Given this situation, is there still a way to use the module system to give (ice-9 match) $ precedence? There is. Göran Weinholt has pointed out that other Scheme implementations tend to export their auxilliary keywords. If we export $ like this:

--8<---------------cut here---------------start------------->8---
(define-module (ice-9 match)
  #:export (match
            match-lambda
            match-lambda*
            match-let
            match-let*
            match-letrec)
  #:replace ($))

[...]

(define misplaced
  (let ((m (lambda (x)
             (format #f "Misplaced aux keyword ~A" x))))
    (lambda (x)
      (syntax-case x ()
        (_ (identifier? x) (syntax-violation #f (m (syntax->datum x)) x))
        ((_ ...) (syntax-violation #f (m (car (syntax->datum x))) x))))))

(define-syntax $ misplaced)

[...]

(include-from-path "ice-9/match.upstream.scm")
--8<---------------cut here---------------start------------->8---

then (ice-9 match) will gracefully replace $ in (value-history) and match will work as expected. A good idea would be to define *all* auxilliary keywords to `misplaced' above, according to what Göran has said. That is independent of the issue of name collisions.

If $ is replaced this way, the user can still write $$0 for that value.

2. Threading and multiple REPL:s

I have suggested to store value history in a fluid and use a functional data type for the value history. That way we avoid race conditions.

3. Efficiency

If value history is stored in a vlist (a module which is used in the core of Guile), much of it, particularly the most recent values, will be accessed at O(1). If we care less about efficiency and want something lean and simple, then ordinary lists will do.


Comments?