Hiredis
A Redis client library for CHICKEN Scheme that provides a simple interface to interact with Redis servers using the hiredis C library.
Description
Hiredis is a lightweight Redis client wrapper that allows you to execute Redis commands from CHICKEN Scheme with automatic reply parsing. It provides a simple Scheme-friendly API for interacting with Redis servers, including support for pub/sub functionality. The library converts Redis replies to appropriate Scheme data types and handles connection management transparently.
Requirements
- CHICKEN Scheme 5.0 or later
- Dependencies: foreigners
- System dependency: hiredis C library
Installation
First, ensure you have the hiredis C library installed on your system:
# On macOS with Homebrew brew install hiredis # On Ubuntu/Debian sudo apt-get install libhiredis-dev # On CentOS/RHEL sudo yum install hiredis-devel
Then install the CHICKEN egg:
chicken-install hiredis
Basic Usage
(import hiredis) ;; Connect to Redis server & set context for current thread (redis-context (redis-connect "127.0.0.1" 6379)) ;; Execute Redis commands (redis-command "SET" "mykey" "myvalue") (redis-command "GET" "mykey") (redis-command "HSET" "myhash" "field1" "value1") (redis-command "HGET" "myhash" "field1") (redis-command "KEYS" "*")
API
[procedure] (redis-connect [hostname] [port])Connect to a Redis server.
Parameters:
- hostname: Redis server hostname or IP address (string, optional, defaults to "localhost")
- port: Redis server port number (integer, optional, defaults to 6379)
Returns: Redis connection context pointer that can be used to set the redis-context parameter.
(redis-connect "127.0.0.1" 6379) (redis-connect "localhost" 6379) (redis-connect) ; Uses defaults: localhost:6379 (redis-connect "redis.example.com") ; Uses default port 6379
After connecting you will need to set the context for all the redis functions. This ensures that your local thread has a context available.
(redis-context (redis-connect ...))
[parameter] (redis-context)
Parameter that holds the current Redis connection context. Must be set after connecting before using other Redis functions.
[procedure] (redis-command command . args)Execute a Redis command with optional arguments.
Parameters:
- command: Redis command string (e.g., "GET", "SET", "HGET")
- args: Optional command arguments
Returns: Scheme object representation of Redis reply
;; String operations (redis-command "GET" "mykey") (redis-command "SET" "mykey" "myvalue") ;; Hash operations (redis-command "HGET" "myhash" "field") (redis-command "HSET" "myhash" "field" "value") ;; List operations (redis-command "LPUSH" "mylist" "item1" "item2") (redis-command "LRANGE" "mylist" "0" "-1") ;; Key operations (redis-command "KEYS" "*") (redis-command "EXISTS" "mykey") (redis-command "DEL" "key1" "key2")[procedure] (redis-subscribe channel callback)
Subscribe to a Redis channel using pattern matching (internally uses the `PSUBSCRIBE` command).
Parameters:
- channel: Channel pattern to subscribe to (string, supports wildcards like `*` and `?`)
- callback: Callback function that receives each message
Callback Function: The callback receives a list with 4 elements for pattern messages: `("pmessage" "pattern" "channel" "message")`
The callback should return non-false to continue listening, or #f to unsubscribe.
;; Subscribe to all channels starting with "test" (redis-subscribe "test*" (lambda (reply) (let ((msg (list-ref reply 3))) (format #t "Received: ~A\n" msg) (not (string=? msg "quit"))))) ;; Subscribe to a specific channel (redis-subscribe "notifications" (lambda (reply) (let ((channel (list-ref reply 2)) (message (list-ref reply 3))) (format #t "Channel ~A: ~A\n" channel message) #t))) ; Continue listening indefinitely
Reply Types
The library automatically converts Redis replies to appropriate Scheme objects:
- String replies → Scheme strings
- Integer replies → Scheme integers
- Array replies → Scheme lists
- Nil replies → `#f`
- Status replies → Scheme strings
- Error replies → `(error . "error message")`
- Double replies → Scheme floats
;; String reply (redis-command "GET" "mykey") ; => "myvalue" ;; Integer reply (redis-command "INCR" "counter") ; => 1 ;; Array reply (redis-command "LRANGE" "mylist" "0" "-1") ; => ("item1" "item2" "item3") ;; Nil reply (redis-command "GET" "nonexistent") ; => #f ;; Status reply (redis-command "SET" "key" "value") ; => "OK" ;; Error reply (redis-command "INVALID" "command") ; => (error . "ERR unknown command") ;; Double reply (Redis 6.2+) (redis-command "HINCRBYFLOAT" "hash" "field" "1.5") ; => 1.5
Complete Examples
Basic Redis Operations
(import hiredis) ;; Connect and set context (define ctx (redis-connect "localhost" 6379)) (redis-context ctx) ;; String operations (redis-command "SET" "user:1000:name" "John Doe") (redis-command "GET" "user:1000:name") ; => "John Doe" ;; Increment operations (redis-command "SET" "page:views" "10") (redis-command "INCR" "page:views") ; => 11 (redis-command "INCRBY" "page:views" "5") ; => 16 ;; Hash operations (redis-command "HSET" "user:1000" "name" "John" "email" "john@example.com") (redis-command "HGET" "user:1000" "name") ; => "John" (redis-command "HGETALL" "user:1000") ; => ("name" "John" "email" "john@example.com") ;; List operations (redis-command "LPUSH" "tasks" "task1" "task2" "task3") (redis-command "LRANGE" "tasks" "0" "-1") ; => ("task3" "task2" "task1") (redis-command "RPOP" "tasks") ; => "task1"
Pub/Sub Example
;; Publisher (in one process/thread) (define pub-ctx (redis-connect)) (redis-context pub-ctx) (redis-command "PUBLISH" "notifications" "Hello World!") (redis-command "PUBLISH" "test-channel" "Test message") ;; Subscriber (in another process/thread) (define sub-ctx (redis-connect)) (redis-context sub-ctx) ;; Subscribe to all channels matching pattern (redis-subscribe "test*" (lambda (reply) (let ((type (list-ref reply 0)) (pattern (list-ref reply 1)) (channel (list-ref reply 2)) (message (list-ref reply 3))) (format #t "Type: ~A, Pattern: ~A, Channel: ~A, Message: ~A\n" type pattern channel message) ;; Continue listening unless message is "quit" (not (string=? message "quit")))))
Error Handling
(define (safe-redis-command command . args) (let ((result (apply redis-command command args))) (if (and (pair? result) (eq? (car result) 'error)) (begin (format #t "Redis error: ~A\n" (cdr result)) #f) result))) ;; Usage (safe-redis-command "GET" "mykey") (safe-redis-command "INVALID" "command") ; Prints error, returns #f
Files
- hiredis.scm - Main Redis client module
- redis_helpers.c - C helper functions for hiredis integration
- test.scm - Example usage and tests
License
Copyright © 2025 Rolando Abarca. Licensed under the BSD 3-Clause License.