2 Types
(require relation/type) | package: relation-lib |
Generic utilities for constructing data and transforming it from one type to another.
The type constructors and transformers provided by Racket out of the box are type-specific; for instance in order to construct a list, we use cons or list, and for a stream we’d use stream-cons or stream. Likewise, to convert data into a string, we would use symbol->string if the data is a symbol, and number->string if the data is a number. Similarly, converting a number to an integer from a more precise form, or vice versa, typically involves multiple steps and the method varies depending on the number’s type.
This module provides a generic type constructor that constructs a type by referring to a provided instance, and also provides convenient interfaces to perform many common type conversions, while keeping them agnostic to the source type. If mutable and immutable versions of a data type exist, these interfaces will return the immutable version.
See also: Sugar.
2.1 Constructors
procedure
(make form element ...) → collection?
form : collection? element : any/c
procedure
element : any/c form : any/c
procedure
(make! form element ...) → collection?
form : collection? element : any/c
procedure
element : any/c form : any/c
: is a convenience wrapper around make with a more familiar interface, mirroring the cons list constructor in terms of argument order and the constructed result, and handling additional common cases outside the purview of make. In particular, if form is not a generic collection, then the elements are simply cons’d together (if there are two of them) or collected into a list (if there are more).
make! and :! are similar except that they mutate the form rather than construct the new object "functionally."
These constructors are a generic alternative to type-specific constructors like cons, stream-cons, set-add, hash-set, and so on (along with their mutative counterparts), with the caveat that for streams, the construction will not happen lazily since these generic versions are functions rather than macros. For streams, unless you are just prototyping or running tests in a shell, you will want to use stream-cons directly.
> (: 1 2) '(1 . 2)
> (: 1 2 3 4 5) '(1 2 3 4 5)
> (: 1 null) '(1)
> (: 1 (list 2 3)) '(1 2 3)
> (: 1 2 3 4 (list 5 6)) '(1 2 3 4 5 6)
> (->list (: 1 empty-stream)) '(1)
> (: 1 #(2 3)) '#(2 3 1)
> (: '(a . 1) (hash 'b 2 'c 3)) '#hash((a . 1) (b . 2) (c . 3))
> (define h (hash 'a 1 'b 2 'c 3)) > (:! '(a . 5) h) > h '#hash((a . 5) (b . 2) (c . 3))
> (define s (set 1 2 3)) > (:! 2 3 4 s) > (->list s) '(4 3 2 1)
2.2 Transformers
> (->integer "42") 42
> (->integer 3/2) 1
> (->integer 3/2 #:round 'up) 2
> (->integer 3.6 #:round 'nearest) 4
> (->list "apple") '(#\a #\p #\p #\l #\e)
> (->list #(1 2 3)) '(1 2 3)
> (->list (stream 1 2 3)) '(1 2 3)
> (->list (hash 'a 1 'b 2 'c 3)) '((c . 3) (b . 2) (a . 1))
> (->vector "apple") '#(#\a #\p #\p #\l #\e)
> (->vector '(1 2 3)) '#(1 2 3)
> (->vector (stream 1 2 3)) '#(1 2 3)
> (->vector (hash 'a 1 'b 2 'c 3)) '#((c . 3) (b . 2) (a . 1))
procedure
(->generator v [return]) → generator?
v : any/c return : any/c = (void)
Note that, owing to the stateful nature of the underlying generator, it’s possible that a stream constructed from a generator would continue to provide lazy evaluation but not take up constant memory. On the other hand, a stream to generator conversion should not incur any additional memory overhead.
Another thing to be wary of with a generator to stream conversion is that since the underlying generator is mutable, independent invocations of the generator after the stream has been constructed would affect the sequence represented by the stream, which is likely to result in unexpected behavior. In general it is advisable to manipulate stateful entities such as generators via a single common interface, whether that is, in the present case, the generator itself directly, or the stream representation of it – but not both.
> (->generator "apple") #<procedure:sequence->generator>
> (->generator '(97 112 112 108 101)) #<procedure:sequence->generator>
> (->list (->generator (conj (->stream (->generator '(1 2 3))) 4))) '(4 1 2 3)
> (->syntax "apple") #<syntax "apple">
> (->syntax 42) #<syntax 42>
> (->syntax '(+ 1 2)) #<syntax (+ 1 2)>
string->symex maps a string to a symex by treating the string as a representation of a symex rather than as a symex itself (which it is, since a string by itself is a valid expression).
> (->symex "apple") "apple"
> (->symex #'42) 42
> (->symex #'(+ 1 2)) '(+ 1 2)
> (->symex #'(define (square x) (* x x))) '(define (square x) (* x x))
> (eval (->symex #'((λ (x) (* x x)) 4))) 16
> (->symex "(+ 1 2)") "(+ 1 2)"
> (string->symex "(+ 1 2)") '(+ 1 2)
> (->hash (hash 'a 1 'b 2)) '#hash((a . 1) (b . 2))
> (->hash (list '(a . 1) '(b . 2))) '#hash((a . 1) (b . 2))
procedure
(->procedure v) → procedure?
v : any/c
> (->procedure add1) #<procedure:add1>
> (->procedure 5) #<procedure:...elation-lib/type.rkt:286:13>
> ((->procedure 5) 'a 'b) 5