diff --git a/coalton.asd b/coalton.asd index cafb1954f..f9e41fe6e 100644 --- a/coalton.asd +++ b/coalton.asd @@ -161,7 +161,8 @@ (:file "complex") (:file "elementary") (:file "dyadic") - (:file "dual"))) + (:file "dual"))) + (:file "randomaccess") (:file "cell") (:file "iterator") (:file "optional") diff --git a/library/randomaccess.lisp b/library/randomaccess.lisp new file mode 100644 index 000000000..27d15cfed --- /dev/null +++ b/library/randomaccess.lisp @@ -0,0 +1,68 @@ +(coalton-library/utils:defstdlib-package #:coalton-library/randomaccess + (:use + #:coalton + #:coalton-library/classes) + (:export + #:RandomAccess + #:make + #:length + #:readable? + #:writable? + #:unsafe-aref + #:unsafe-set! + + #:aref + #:set!)) + +(in-package #:coalton-library/randomaccess) + +(named-readtables:in-readtable coalton:coalton) + +#+coalton-release +(cl:declaim #.coalton-impl/settings:*coalton-optimize-library*) + +;;; Class Implementation +(coalton-toplevel + ;; The decision to use a functional dependency was an ergonomic one, + ;; not a technical one. If we did not establish this dependency, we + ;; would win flexibility (a single storage type can store multiple + ;; data types), but we would lose a lot of practical ergonomics + ;; (e.g., we would need Proxy types, calls to length would need to be + ;; disambiguated, etc.). + ;; + ;; While we lose some flexibility, we still retain some. For + ;; instance, we can have multiple storage types for double floats + ;; (think: Lisp arrays, C arrays, GPU arrays, etc.). + (define-class (RandomAccess :f :t (:f -> :t)) + "Establishes that `:f` is a random-access store of elements of type `:t`. The **storage type** `:f` implies the **stored type** `:t`. The storage is expected to be sequential and O(1) in random access reads and writes. + +It is permitted for any of `make`, `unsafe-aref`, or `unsafe-set!` to error." + (make (UFix -> :t -> :f)) + (length (:f -> UFix)) + (readable? (:f -> Boolean)) + (writable? (:f -> Boolean)) + (unsafe-aref (:f -> UFix -> :t)) + (unsafe-set! (:f -> UFix -> :t -> Unit)))) + +;;; Derived Functions +(coalton-toplevel + (declare aref (RandomAccess :f :t => :f -> UFix -> (Optional :t))) + (define (aref storage index) + "Read the element at `index` of the random-access storage `storage`. Return `None` if the read is out-of-bounds or not permitted." + (if (and (readable? storage) + (<= 0 index) + (< index (length storage))) + (Some (unsafe-aref storage index)) + None)) + + (declare set! (RandomAccess :f :t => :f -> UFix -> :t -> (Optional Unit))) + (define (set! storage index value) + "Write the element `value` at `index` of the random-access storage `storage`. Return `None` if the write is out-of-bounds or not permitted." + (if (and (writable? storage) + (<= 0 index) + (< index (length storage))) + (Some (unsafe-set! storage index value)) + None))) + +#+sb-package-locks +(sb-ext:lock-package "COALTON-LIBRARY/RANDOMACCESS") diff --git a/library/vector.lisp b/library/vector.lisp index be5ebd7c6..b39dabff6 100644 --- a/library/vector.lisp +++ b/library/vector.lisp @@ -8,7 +8,8 @@ (#:types #:coalton-library/types) (#:list #:coalton-library/list) (#:cell #:coalton-library/cell) - (#:iter #:coalton-library/iterator)) + (#:iter #:coalton-library/iterator) + (#:ram #:coalton-library/randomaccess)) (:export #:Vector #:new @@ -66,7 +67,7 @@ (lisp (Vector :a) (n) (cl:make-array n :fill-pointer 0 :adjustable cl:t :element-type cl:t))) - (declare with-initial-element (types:RuntimeRepr :a => UFix -> :a -> Vector :a)) + (declare with-initial-element (UFix -> :a -> Vector :a)) (define (with-initial-element n x) "Create a new vector with `n` elements equal to `x`." (let v = (with-capacity n)) @@ -286,6 +287,20 @@ :initial-value init :from-end cl:t)))) + (define-instance (ram:RandomAccess (Vector :t) :t) + (define (ram:make n x) + (with-initial-element n x)) + (define (ram:length a) + (length a)) + (define (ram:readable? _) + True) + (define (ram:writable? _) + True) + (define (ram:unsafe-aref a n) + (index-unsafe n a)) + (define (ram:unsafe-set! a n x) + (set! n x a))) + (define-instance (Into (List :a) (Vector :a)) (define (into lst) (let ((out (with-capacity (list:length lst))) diff --git a/src/doc/generate-documentation.lisp b/src/doc/generate-documentation.lisp index 2c587a544..00d402dab 100644 --- a/src/doc/generate-documentation.lisp +++ b/src/doc/generate-documentation.lisp @@ -125,6 +125,7 @@ coalton-library/list coalton-library/result coalton-library/cell + coalton-library/randomaccess coalton-library/vector coalton-library/slice coalton-library/hashtable