#lang racket/base (require "define.rkt" "coerce.rkt" "len.rkt" racket/list racket/set racket/sequence racket/stream racket/dict) (define (sliceable-container? x) (ormap (λ(proc) (proc x)) (list list? string? symbol? vector? path? (λ(i) (and (not (dict? i)) (sequence? i)))))) (define (gettable-container? x) (ormap (λ(proc) (proc x)) (list sliceable-container? dict?))) (define+provide+safe (get container start [end #f]) ((gettable-container? any/c) ((or/c (and/c integer? positive?) #f)) . ->* . any) (define result ;; use handler to capture error & print localized error message (with-handlers ([exn:fail? (λ(exn) (error (format "get: couldn't retrieve ~a from ~a" (if end (format "items ~a through ~a" start end) (format "item ~a" start)) container)))]) (let ([end (if (and (equal? end #f) (sliceable-container? container)) (add1 start) end)]) (cond [(list? container) (for/list ([i (in-range start end)]) (list-ref container i))] [(vector? container) (for/vector ([i (in-range start end)]) (vector-ref container i))] [(string? container) (substring container start end)] [(symbol? container) (->symbol (get (->string container) start end))] [(path? container) (get (explode-path container) start end)] [(dict? container) (dict-ref container start)] [(sequence? container) (get (->list container) start end)] [else (error)])))) ;; don't return single-item results inside a list ;; check for integer because integers don't have length (if (and (not (integer? result)) (= (len result) 1) (sliceable-container? container)) (car (->list result)) result)) (define (listlike-container? container) (ormap (λ(pred) (pred container)) (list vector? set? sequence?))) (define+provide+safe (in? item container) (any/c any/c . -> . boolean?) (->boolean (cond [(list? container) (member item container)] [(dict? container) (dict-has-key? container item)] [(path? container) (in? (->path item) (explode-path container))] [(stringish? container) (regexp-match (->string item) (->string container))] ;; location relevant because dicts and strings are also listlike (= sequences) [(listlike-container? container) (in? item (->list container))] [else #f])))