Trying to treat flvector as a collection: violates contract

I thought that data/collection was meant to allow a set of functions to be collection type agnostic. There must be some syntactic trick that is not documented for doing so.

#lang racket/base

(require racket/flonum)
(require data/collection)

(define (vector-apply vec)
  (data/collection:apply fl+ vec))

(define-syntax-rule (bench vector-sum)
  (begin
    (define (vector-bench len fval)
      (let ((vec (make-flvector len fval)))
        (vector-sum vec)))

    (define (repeat n len fval)
      (for ([_ (in-range n)])
        (vector-bench len fval)))

    (collect-garbage)
    ; use as follows:
    'vector-sum
    (time (repeat 1000 100000 0.5)) ; create vector 1000 times and sum
    ))
(#%declare #:unsafe)

(bench vector-apply)

Results in this error:

(bench vector-apply)
apply: contract violation
  expected: sequence?
  given: (flvector 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5...
  in: the last argument of
      (-> procedure? any/c ... sequence? any)
  contract from: 
      <pkgs>/collections-lib/data/collection/collection.rkt
  blaming: /Users/lewis/Library/CloudStorage/Dropbox/lisp-play/gambit-play/bench-new-racket.rkt
   (assuming the contract is correct)
  at: <pkgs>/collections-lib/data/collection/collection.rkt:52:3
 [,bt for context]

Don't understand why. The documentation for sequences in 4.16.1 clearly says that flvectors, which I assume are mutable, can be sequences.

The Generic Collections library (actually called collections-lib and incorporated in code as data/collections[1] is less clear on the point. There is a reference to an any/c thing, which would appear to be an "object" that satisfies a flat-contract. So much conceptual overhead here... So, does an flvector satisfy a flat contract?

Lots of documentation on how to make specific kinds of contracts. Not a lot on how to test if an object satisfies a contract--it must be there, but it lost in the deep topic.

Without sorting out the docs, these simple examples confirm that an flvector fails to satisfy the contract:

(apply fl+ (flvector 1.0 2.0 3.0)) ; -> contract failure

(apply fl+ #(1.0 2.0 3.0)) ; -> 6.0 hallelujah!

So, should I conclude there is no efficiently implemented "apply" for flvectors? For/fold works great but apply would be more concise (uh-oh: I too despite typing!). I am about to try a named let.

As a general comment, Scheme not embracing method dispatch (a poor choice of concept names from Julia--it should just be called function/operator overloading...) and including the requisite functions in its libraries is a pretty big gap. Function and method overloading (with suitable contracts) would be far more useful than any CLOS-like thing: and much easier to use and likely more performant. This is seemingly too modern a feature though sbcl goes a bit further in providing it.

[1] This use of 3 names for what is surely e the same thing is unnecessarily confusing. Experienced programmers seem to viciously despise typing, so either use a short string everywhere or just bite the bullet and use the long string for clarity: typing is not that hard, especially as these strings will be entered only once in a module or source file...

1 Like

I was pretty confused by the example:

(apply fl+ '(1.0 2.0 3.0)) ; -> 6.0 hallelujah!

The confusion didn't go away until I found out that data/collection exports
generic versions for apply, apppend, map, etc.

[If you are benchmarking, you might want to use prefix-in either with
data/collection or with racket so you still have access the original, fast versions
of these functions.]

The package data/collection is not part of the main distribution, so to make it work with flvector you'll need to convince Alexis to add them. Currently only vectors not flvectors can be used as generic collections.

The documentation for sequences in 4.16.1 clearly says that flvectors, which I assume are mutable, can be sequences.

The concept of sequences in the Reference and the concept of generic sequence in Alexis' library differ.

The generic sequences in the library are:

It's amazing that it's possible to add the functionality as a "third party" library.
The power of the macro and module system really shines here.

But it's the same power that blurs the line between language and library
(if the line still exists) and thus can be confusing at times even for old-timers.

3 Likes

If using flvectors as collections were part of the library it would have been fair to included it in the benchmark. As it’s not, then extending the library would be a good exercise, but perhaps not fair in the benchmark.

Thanks!

(collect-garbage)
;; benchmark...

OT but I wanted to share some Racket lore I learned years ago, which is to collect-garbage three times before a benchmark.

You could write that out 3X or do (for ([_ 3]) (collect-garbage)).

The idea is to avoid a slow major GC during your benchmark.

Caveat: I seem to learn a lot by giving bad advice then getting corrected. Maybe that lore has expired, and the advice now is (collect-garbage 'major), or something else; we shall both see... :wink:

1 Like