Email list hosting service & mailing list manager

Review of SRFI 122 draft #8 John Cowan (23 Aug 2016 20:50 UTC)
Re: Review of SRFI 122 draft #8 Bradley Lucier (24 Aug 2016 21:40 UTC)
Re: Review of SRFI 122 draft #8 John Cowan (24 Aug 2016 22:43 UTC)
Re: Review of SRFI 122 draft #8 Bradley Lucier (25 Aug 2016 23:07 UTC)
Re: Review of SRFI 122 draft #8 John Cowan (26 Aug 2016 01:51 UTC)
Re: Review of SRFI 122 draft #8 Bradley Lucier (26 Aug 2016 14:49 UTC)

Re: Review of SRFI 122 draft #8 Bradley Lucier 25 Aug 2016 23:07 UTC

On 08/24/2016 06:43 PM, John Cowan wrote:
>
>
> On Wed, Aug 24, 2016 at 5:40 PM, Bradley Lucier <xxxxxx@math.purdue.edu
> <mailto:xxxxxx@math.purdue.edu>> wrote:
>
>
>         5) It should be explicitly stated that arrays, specialized
>         arrays, and
>         intervals are disjoint types.
>
>
>     I'm not going to argue against this, but let me ask "why?"
>     Translations and permutations are not disjoint types.  And a
>     specialized-array is an array, so in what way are these types disjoint?
>
>
> Yes, I meant that arrays and intervals are disjoint with each other and
> with the standard Scheme types.

I'm asking why should this be mandated?

  Mentioning specialized-arrays was
> pointless.
>
>
>     I introduced two procedures, specialized-array-default-safe? and
>     specialized-array-default-safe?-set!
>
>
> If specialized-array-default-safe? accepts an optional argument and sets
> the internal variable based on it, that is the SRFI 39 pattern, which
> should be familiar.

OK, fixed in internal draft.

> I still think you should say it is an error, but if you don't want to, okay.
>
>
>     I don't know what you mean.  The (heavily-macrofied) C code
>     generated by Gambit [...]
>
>     The three arguments are stored in registers R1, R2, and R3, the
>     return address is stored in R0, and there's a direct jump to the
>     label for the code for getter, which has been put into ___STK(-5).
>     I don't see any lists.
>
>
> Gambit is an optimizing compiler, but most Schemes aren't: they take
> literally the statement in R5RS/R7RS section 4.1.4: "The value stored in
> the binding of the last variable will be a newly allocated list of the
> actual arguments left over after all the other actual arguments have
> been matched up against the other formal arguments."  Since the callee
> may mutate this list, it has to be reconstructed at every call.
>
> Furthermore, what happens in Gambit if there are more dimensions than
> registers?  Also, what happens in case of separate compilation, which
> will be usual when this SRFI's implementation is in use?

I now understand that you're worried about the code on the callee side,
not the caller side.

The reference implementation has a lot of repetitive code (some
macrofied) to special-case efficient getter and setter code for arrays
up to dimension four, and indeed for arrays of dimension five or higher
the code is much less efficient.  And safe code is slower still. So you
are correct, for arrays with larger dimensions.

Here is an example; the input code:

(include "generic-arrays.scm")

(define A
   (array->specialized-array
    (make-array (make-interval '#(0 0 0) '#(10 10 10))
	       (lambda args
		 (random-integer 256)))
    u8-storage-class))
(pp "Unsafe code for three-dimensional array")
(pp (array-getter A))
(pp (array-setter A))
(pp (array-indexer A))

(define B
   (array->specialized-array
    (make-array (make-interval '#(0 0 0 0 0) '#(10 10 10 10 10))
	       (lambda args
		 (random-integer 256)))
    u8-storage-class))
(pp "Unsafe code for five-dimensional array")
(pp (array-getter B))
(pp (array-setter B))
(pp (array-indexer B))

(define A
   (array->specialized-array
    (make-array (make-interval '#(0 0 0) '#(10 10 10))
	       (lambda args
		 (random-integer 256)))
    u8-storage-class
    #t))

(pp "Safe code for three-dimensional array")
(pp (array-getter A))
(pp (array-setter A))
(pp (array-indexer A))

(define B
   (array->specialized-array
    (make-array (make-interval '#(0 0 0 0 0) '#(10 10 10 10 10))
	       (lambda args
		 (random-integer 256)))
    u8-storage-class
    #t))

(pp "Safe code for five-dimensional array")
(pp (array-getter B))
(pp (array-setter B))
(pp (array-indexer B))

The result (undefined values are loaded from closures):

heine:~/lang/scheme/srfi-122/srfi-122-temp> gsi test.scm
"Unsafe code for three-dimensional array"
(lambda (i j k) (u8vector-ref body (indexer i j k)))
(lambda (value i j k) (u8vector-set! body (indexer i j k) value))
(lambda (i j k) (+ (* increment-0 i) (* increment-1 j) k))
"Unsafe code for five-dimensional array"
(lambda multi-index (u8vector-ref body (apply indexer multi-index)))
(lambda (value . multi-index)
   (u8vector-set! body (apply indexer multi-index) value))
(lambda multi-index
   (letrec ((##do-loop
             (lambda (multi-index lower-bounds increments result)
               (if (null? multi-index)
                   result
                   (##do-loop
                    (cdr multi-index)
                    (cdr lower-bounds)
                    (cdr increments)
                    (+ result
                       (* (car increments)
                          (- (car multi-index)
                             (car lower-bounds)))))))))
     (##do-loop multi-index lower-bounds increments base)))
"Safe code for three-dimensional array"
(lambda (i j k)
   (cond ((not (and (##exact-integer? i)
                    (##exact-integer? j)
                    (##exact-integer? k)))
          (error "array-getter: multi-index component is not an exact
integer: "
                 i
                 j
                 k))
         ((not (##interval-contains-multi-index?-3 domain i j k))
          (error "array-getter: domain does not contain multi-index: "
                 domain
                 i
                 j
                 k))
         (else (storage-class-getter body (indexer i j k)))))
(lambda (value i j k)
   (cond ((not (and (##exact-integer? i)
                    (##exact-integer? j)
                    (##exact-integer? k)))
          (error "array-setter: multi-index component is not an exact
integer: "
                 i
                 j
                 k))
         ((not (##interval-contains-multi-index?-3 domain i j k))
          (error "array-setter: domain does not contain multi-index: "
                 domain
                 i
                 j
                 k))
         ((not (checker value))
          (error "array-setter: value cannot be stored in body: "
                 value))
         (else (storage-class-setter body (indexer i j k) value))))
(lambda (i j k) (+ (* increment-0 i) (* increment-1 j) k))
"Safe code for five-dimensional array"
(lambda multi-index
   (cond ((not (##every ##exact-integer? multi-index))
          (apply error
                 "array-getter: multi-index component is not an exact
integer: "
                 multi-index))
         ((not (= (##interval-dimension domain) (length multi-index)))
          (apply error
                 "array-getter: multi-index is not the correct dimension: "
                 domain
                 multi-index))
         ((not (##interval-contains-multi-index?-general
                domain
                multi-index))
          (apply error
                 "array-getter: domain does not contain multi-index: "
                 domain
                 multi-index))
         (else
          (storage-class-getter body (apply indexer multi-index)))))
(lambda (value . multi-index)
   (cond ((not (##every ##exact-integer? multi-index))
          (apply error
                 "array-setter: multi-index component is not an exact
integer: "
                 multi-index))
         ((not (= (##interval-dimension domain) (length multi-index)))
          (apply error
                 "array-setter: multi-index is not the correct dimension: "
                 domain
                 multi-index))
         ((not (##interval-contains-multi-index?-general
                domain
                multi-index))
          (apply error
                 "array-setter: domain does not contain multi-index: "
                 domain
                 multi-index))
         ((not (checker value))
          (error "array-setter: value cannot be stored in body: "
                 value))
         (else
          (storage-class-setter
           body
           (apply indexer multi-index)
           value))))
(lambda multi-index
   (letrec ((##do-loop
             (lambda (multi-index lower-bounds increments result)
               (if (null? multi-index)
                   result
                   (##do-loop
                    (cdr multi-index)
                    (cdr lower-bounds)
                    (cdr increments)
                    (+ result
                       (* (car increments)
                          (- (car multi-index)
                             (car lower-bounds)))))))))
     (##do-loop multi-index lower-bounds increments base)))

>
>         Array-ref and array-set!  convenience methods for getting and
>         setting
>         without explicitly retrieving the getter/setter.
>
>
>     I'm trying to offer alternatives to word-at-a-time array processing
>     in this SRFI.
>
>
> Absolutely, but they should be there as a fallback.  A specific case for
> such
> processing is a system that receives tuples of integers and counts the
> number
> of times each tuple appears using array-ref (to get the previous count) and
> array-set! (to increment it).

I'll think about it.  The example you give works just as well with
extracting the array getter and setter once and then using them.  I'm
concerned that array-ref and array-set! require more error checking at
each call.

>
>     I have array-reduce; how does it differ from array-fold?
>
>
> Given a sequence of length N, reduce calls its procedure N - 1 times and
> returns the identity if N = 1.  Fold starts with a seed and calls its
> procedure N times, passing the seed and the element and getting a new
> seed.  Each is useful in different contexts.

Interesting.  The MIT Scheme manual gives essentially the same
definitions, yet other sources basically equate reduce and fold; I
understood that reduce could rely on the operation being associative.

I'll have to think some more.

Brad