# record-vector ## Description **Record-vector** is a module for CHICKEN5|6 Scheme, and R7RS library for #:Guile :Gauche Gambit: Schemes, designed to work with key-value data structures, aka `records`. Record-vector does not declare a new record type, it is not a hash-table, nor a tree. Record-vector itself is just a pair of **alist** of attribute indexes in the second part of pair - value's **vector**. '(((#:KW1 . INDEX1) ... (#:KWN . INDEXN)) . #(VALUE1 ... VALUEN)) KW - name of attribute, also Scheme keyword #:NAME or NAME: depend of Scheme variant. VALUE - any Scheme value. Cons * Access to the record attribute barely dumb sequence of *number? + car + assq + vector-ref* * Record-vectors are not tagtyped but duck typed * It's not bit-compact * Nor cache frendly it is user frendly records. Pros * Transparent good old way of the Lisp data representation (sic!) * automatic serialization by *read/write* * easy handy creation * automatic data import (in memory of SRFI-9 import-export access proc.s hell) * Access to the attribute value by the #:keyword or by index * Access to the attribute of record-vector inside record-vector, recursive, by path of keywords or indexes. ## Example. Basic usage ``` Scheme > (record-vector #:x 0.0 #:speed 0.1) => (((#:x . 0) (#:speed . 1)) . #(0.0 0.1)) ;; make-rv an alias of [procedure]record-vector > (define boat (make-rv #:posn (make-rv #:x 1.0 #:y 2.0) #:id 10001 #:passengers 4)) ;; record-vector-ref/rv-ref is accessor procedure/macro (last one supports inner rv chain access) > (rv-ref boat #:posn #:y) => 2.0 ;; same reference by attribute indexes > (rv-ref boat 0 1) => 2.0 ;; Zero - #:posn, 1 - #:y in position. ;; the record-vector extends in this way > (rv-attr? boat #:model) => #f > (rv-update! boat #:model "Azimut 50") > (rv-attr? boat #:model) => (#:model . 3) ;; get attribute by index > (rv-ref boat 3) => "Azimut 50" ``` ## Example. Compound record-vector ```Scheme (define objects (record-vector #:table (make-rv #:posn (make-rv #:x 100 #:y 20) #:height 80 #:items (make-rv #:cup (make-rv #:posn (make-rv #:x 10 #:y 0)) #:pot (make-rv #:posn (make-rv #:x 20 #:y 10)))))) > (cdr (rv-ref objects #:table #:items #:pot #:posn)) => #(20 10) ``` ## Requirements Non standard predicate *keyword?* and utility (R7RS) procedure *vector-append* are used. Also in CHICKEN SRFI-17 *setter* adapter used for *record-vector-ref*. ## API All record-vector-* forms has rv-* shortcomings, some in form of macros with extended abilities. ### record-vector [procedure] (record-vector [#:NAME-1 VALUE-1 ... #:NAME-N VALUE-N]) [procedure] (make-rv [#:NAME-1 VALUE-1 ... #:NAME-N VALUE-N]) Record-vector construction procedures. *make-rv* is an alias of *record-vector* + __NAME-1__ - first attribute name - scheme keyword _#:NAME_ (or _NAME:_ in CHICKEN) + __VALUE-1__ - first attribute value *record-vector* may be empty. ```Scheme > (record-vector #:code "defs.scm" #:lang 'Scheme #:loc 117)) => (((#:code . 0) (#:lang . 1) (#:loc . 2)) . #("defs.scm" Scheme 117)) > (define empty-obj (make-rv)) > empty-obj => (() . #()) > (record-vector? empty-obj) => #t ``` ### record-vector? [procedure] (record-vector? VAR) Unstrict predicate which test structure of argument **VAR** is like Scheme pair of list and vector. ### record-vector-clone [procedure] (record-vector-clone R-V-SRC [#:NAME-k #:NEW-VALUE-k ...]) [procedure] (rv-clone R-V-SRC [#:NAME-k #:NEW-VALUE-k ...]) Create the new record-vector by consing attributes alist of R-V-SRC and copy of R-V-SRC value vector. If every n-th value also *record-vector?* than recursive clone it inplace of new n-th value. Also apply new values to existing attributes with name **#:NAME-k** or extends new *record-vector* with them. ```Scheme > (define r (record-vector #:x 1.0 #:y 3.0)) > (define r2 (record-vector-clone r #:y 2.0 #:z 3.0)) > r2 => (((#:x . 0) (#:y . 1) (#:z . 2)) . #(1.0 2.0 3.0)) ``` ### record-vector-like? [procedure] (record-vector-like? r1 r2) [procedure] (rv-like? r1 r2) Procedure *record-vector-like?* returns true if all of attribute names in r1.alist also in r2.alist or vice versa. ```Scheme > (define r1 (make-rv x: 1 y: 2 z: 3)) > (define r2 (make-rv x: 4 y: 2)) > (rv-like? r1 r2) => #t ;; bc all attributes from r2 are present in r1. > (rv-like? r2 r1) => #t ;; still True, record-vector-like? symmetrical ``` ### record-vector-attr? [procedure] (record-vector-attr? R-V #:NAME) [procedure] (rv-attr? R-V #:NAME) This proc's for check presence of the attribute #:NAME in alist dictionary of **R-V**. Returns _(assq #:NAME r-v.alist)_. ### record-vector-ref [procedure] (record-vector-ref R-V #:NAME-k #!optional DEFAULT) [macro] (rv-ref R-V #:NAME-k [#:SUB-NAME-j ...]) This is accesor of the attribute of the record-vector **R-V**. In CHICKEN *record-vector-ref* (not the *rv-ref*) SRFI-17 compatible. Optional **DEFAULT** value is used if there is no attribute named **#:NAME-k**. The **rv-ref** macro works like *record-vector-ref*, but if the value with **#:NAME-k** is a *record-vector?*, it looks for the value of the **#:SUB-NAME-j** attribute in it, recursively. The macro does not have a default return value, unlike *record-vector-ref*. ```Scheme > (record-vector-ref (record-vector #:x 124 #:y 0) #:x) => 124 > (record-vector-ref (make-rv #:x 1) #:t 1900) => 1900 > ;; but no default value in rv-ref macro > (rv-ref (make-rv #:x 1) #:t 1990) => Error: (cdr) bad argument type: #f > (rv-ref (make-rv #:person (make-rv #:name "Joe" #:age 50)) #:person #:name ) => "Joe" ;; if attribute is not present it returns False (use rv-attr? predicate before) > (rv-ref (make-rv #:posn (make-rv #:x 0.0 #:y 0.0)) #:posn #:z) => #f .... ``` ### record-vector-set! [procedure] (record-vector-set! R-V #:NAME-k NEW-VALUE) [macro] (rv-set! R-V #:NAME-k [#:SUB-R-V-NAME-j ...] NEW-VALUE) *record-vector-set!* is a mutator of the record-vector attribute **NAME** *rv-set!* - recursive macro which able set value not only **R-V** but attributes compatible with *record-vector* structure ```Scheme > (define r (record-vector #:pos 0.0 #:line (make-rv #:color 'red #:a 1.0 #:b 2))) > r => (((#:pos . 0) (#:line . 1)) . #(0.0 (((#:color . 0) (#:a . 1) (#:b . 2)) . #(red 1.0 2)))) > (set! (record-vector-ref r #:pos) 1000.0) ;; SRFI-17 > r => (((#:pos . 0) (#:line . 1)) . #(1000.0 (((#:color . 0) (#:a . 1) (#:b . 2)) . #(red 1.0 2)))) > (rv-set! r #:line #:color 'blue) > r => (((#:pos . 0) (#:line . 1)) . #(1000.0 (((#:color . 0) (#:a . 1) (#:b . 2)) . #(blue 1.0 2)))) ``` ### record-vector-update! [procedure] (record-vector-update! R-V #:NAME VALUE) [macro] (rv-update! R-V #:NAME [#:SUB-NAME ...] VALUE) These procedure and macro (which recursive variant) for update existing attribute values ​​and add new attribute in record-vector **R-V**. ```Scheme > (define p (make-rv posn: (make-rv x: 1 y: 2))) > (rv-update! p posn: x: 10) > p => (((#:posn . 0)) . #((((#:x . 0) (#:y . 1)) . #(10 2)))) > (rv-update! p posn: z: 30) > p => (((#:posn . 0)) . #((((#:x . 0) (#:y . 1) (#:z . 2)) . #(10 2 30)))) ;; rv-update! is stable > (rv-update! p posn: z: 30) > p => (((#:posn . 0)) . #((((#:x . 0) (#:y . 1) (#:z . 2)) . #(10 2 30)))) ``` ## Example ``` Scheme (import (rename (record-vector) (record-vector rv))) (let ((r (rv x: 1 y: 2)) (nr (rv x: 2 y: 3))) (define (distance p t) (let ((dx (- (rv-ref t #:x) (rv-ref p #:x))) (dy (- (rv-ref t #:y) (rv-ref p #:y)))) (sqrt (+ (* dx dx) (* dy dy))))) (= (distance r nr) (distance nr r))) ``` ## Source code [record-vector at Codeberg] (https://codeberg.org/Corbas/record-vector) ## Authors [Anton Idukov] (e-mail-to:corbas.ai@gmail.com) ## Licence BSD-2-Clause licence. ## Version History 1.0.0 Initial release