Re: relationship to SRFI-25 Jamison Hope 29 Sep 2015 16:12 UTC

On Sep 29, 2015, at 2:03 AM, Bradley Lucier <> wrote:

> On 09/28/2015 07:20 PM, Jamison Hope wrote:
> 't really understand this part.
>> I'm not suggesting that array-map be implemented on top of array-ref,
>> or that whatever other mutating functions there are be implemented
>> on top of array-set! -- just that as an end user, having to extract
>> and then invoke a closure rather than just calling a setter or getter
>> *function* seems strange.
> OK.
> Often, there is only the getter function.  (I'm not sure what
> distinction you're trying to make when you call it a "closure".)

The getter and setter functions do not take the array object or any
related context as an argument, only the indices.  If a cell's value is
purely a function of its location (as in an identity matrix), then the
array must be immutable and the getter will be a function.  But in every
other case, the getter function and (optional) setter must close over
some direct or indirect storage in order to do the appropriate
lookup/assignment.  They are closures.

There's nothing wrong with that per se, but it's a lot more..
object-oriented (a la Smalltalk, not CLOS) than most Scheme/Lisp APIs.
(This reminds me of Anton's koan about Master Qc Na [1].)

Suppose I want to write my own mutable array implementation backed by
vectors (I know there are API-supplied functions for this, but humor
me).  In my case, I only care about 2D 3x3 matrices because I intend to
use them to represent spatial rotations or something like that.

At the lowest level, I need these two functions:

(define (matrix3-ref v i j)
  (vector-ref v (+ (* i 3) j)))

(define (matrix3-set! v newval i j)
  (vector-set! v (+ (* i 3) j) newval))

But they aren't directly usable as SRFI-122 getter and setter functions,
because the latter must close over the storage v.  For each matrix
instance I will need to allocate three objects (not including the
SRFI-122 array object and its interval object): the vector where the
data will live, and two lambdas that wrap calls to matrix3-ref and

(let ((v (make-vector (* 3 3) 0)))  ;; first object
  (mutable-array (interval '#(0 0) '#(3 3))
    (lambda (i j) (matrix3-ref v i j)) ;; second object
    (lambda (val i j) (matrix3-set! v val i j)))) ;; third object

As an alternative, suppose the API for mutable-array looked like this:
(mutable-array domain getter setter context) where context is of
arbitrary type but the getter and setter functions are specified to
take the context as an argument (they are free to ignore it if they
wish): (getter context i_1 ... i_n) and
(setter context val i_1 ... i_n).

With this hypothetical API, I could construct my 3x3 mutable array with

(mutable-array (interval '#(0 0) '#(3 3))
  matrix3-ref matrix3-set! (make-vector (* 3 3) 0))

The only allocations now are the interval, the vector, and the

Then we would need an additional function (array-context array), and a
"get" call would look like
((array-getter a) (array-context a) i j)

But I digress.  My original point was just that with most Scheme APIs,
the user-level single-element accessors are functions TYPE-ref and
TYPE-set! which take as arguments an instance of the TYPE as well as the
location within TYPE (index or key).

If I want to read out single values from several vectors, I repeatedly
call the same function (vector-ref) passing each vector in turn.  If I
want to read out single values from several strings, I repeatedly call
the same function (string-ref) passing each string in turn.  If I want
to read out single values from several SRFI-25 arrays, I repeatedly call
the same function (array-ref) passing each.  If I want to read out
single values from several SRFI-122 arrays, I must explicitly retrieve
a separate getter procedure for each array.

Again, there's nothing wrong with that in principle, but within the
ecosystem of existing Scheme libraries, it violates the principle of
least astonishment.



Jamison Hope