To help myself learn Racket, I ported a simple JavaScript ircbot module I wrote for Node.js to Racket. The Racket version is built atop the Racket irc package, so the low-level code is handled by that. My module simply provides some utility functions to make it easier to implement IRC bots.My code is as follows. I know it's rather long and probably fairly unreadable, and for that I apologize. I'm not looking for anything extremely specific, but I would like to know if I'm violating any major conventions of the language in any obvious ways.#lang racket(provide ircbot-connect         ircbot-say         ircbot-listen-action         ircbot-listen-chat         ircbot-listen-trigger         ircbot-listen-message         ircbot-listen-self         ircbot-listen-self-action         ircbot-listen-join         ircbot-listen-part         ircbot-listen-quit         ircbot-listen-nick         ircbot-listen);; ---------------------------------------------------------------------------------------------------(require (for-syntax racket/syntax))(require racket/async-channel)(require irc)(require srfi/13)(struct ircbot-connection (server nick username realname channels triggers pmtrigger                                  action-handlers chat-handlers trigger-handlers message-handlers                                  self-handlers self-action-handlers join-handlers part-handlers                                  quit-handlers nick-handlers                                  connection))(define (ircbot-connect #:server [server localhost]                        #:port [port 6667]                        #:nick [nick racketircbot]                        #:username [username racketircbot]                        #:realname [realname racketircbot]                        #:channels [channels (list)]                        #:triggers [triggers (list)]                        #:pmtrigger [pmtrigger #f])  (define-values (connection ready-event)    (irc-connect server port nick username realname))  (sync ready-event)  (for ([channel channels])    (irc-join-channel connection channel))  (ircbot-connection server nick username realname channels triggers pmtrigger                     (box '()) (box '()) (box '()) (box '()) (box '())                     (box '()) (box '()) (box '()) (box '()) (box '())                     connection))(define (ircbot-say connection message [channels (ircbot-connection-channels connection)])  (when (not (list? channels))    (set! channels (list channels)))  (define action #f)  (when (equal? (string-contains message /me ) 0)    (set! action #t)    (set! message (substring message 4)))  (for ([channel channels])    (if action        (irc-send-command (ircbot-connection-connection connection)                          PRIVMSG                          channel (format :\u0001ACTION ~a\u0001 message))        (irc-send-message (ircbot-connection-connection connection) channel message))));; ---------------------------------------------------------------------------------------------------(define-syntax (define-ircbot-listener stx)  (syntax-case stx ()    [(define-ircbot-listener name)     #`(define (#,(format-id #'name #:source #'name                             ircbot-listen-~a                             (syntax-e #'name)) connection callback)         (set-box! (#,(format-id #'name #:source #'name                                 ircbot-connection-~a-handlers                                 (syntax-e #'name)) connection)                   (append (unbox (#,(format-id #'name #:source #'name                                                ircbot-connection-~a-handlers                                                (syntax-e #'name)) connection))                           (list callback))))]))(define-ircbot-listener action)(define-ircbot-listener chat)(define-ircbot-listener trigger)(define-ircbot-listener message)(define-ircbot-listener self)(define-ircbot-listener self-action)(define-ircbot-listener join)(define-ircbot-listener part)(define-ircbot-listener quit)(define-ircbot-listener nick);; ---------------------------------------------------------------------------------------------------(define (channel? str)  (equal? (string-ref str 0) #\#))(define (respond connection sender recipient response)  (irc-send-message (ircbot-connection-connection connection)                    (if (channel? recipient) recipient sender)                    response)  (when (channel? recipient)    (map ( (el) (el (hash text response))) (unbox (ircbot-connection-self-handlers connection)))))(define (ircbot-listen connection)  (let loop ()    (define message (async-channel-get                     (irc-connection-incoming (ircbot-connection-connection connection))))    (match message      [(irc-message prefix PRIVMSG params _)       (define prefix-match (regexp-match #rx^[^!]+ prefix))       (when prefix-match         (define sender (first prefix-match))         (define recipient (first params))         (define message (second params))         (define action-match (regexp-match #rx^\u0001ACTION ([^\u0001]*)\u0001 message))         (define (callback text cb)           (cb (hash sender sender                     recipient recipient                     message  text                     type (if action-match action chat)                     private  (not (channel? recipient)))               ( (response) (respond connection sender recipient response))))         (cond           [action-match            (set! message (second action-match))            ; call all message handlers            (map (curry callback message)                 (unbox (ircbot-connection-message-handlers connection)))            ; call all action handlers            (map (curry callback message)                 (unbox (ircbot-connection-action-handlers connection)))            ]           [else            ; call all message handlers            (map (curry callback message)                 (unbox (ircbot-connection-message-handlers connection)))            ; call all action handlers            (map (curry callback message)                 (unbox (ircbot-connection-chat-handlers connection)))            ; call trigger handlers if necessary            (define triggered #f)            (define args (string-split message))            (define trigger (first args))            (define reconstructed (string-join (rest args)))            (when (member trigger (ircbot-connection-triggers connection))              (set! triggered #t)              (map (curry callback reconstructed)                   (unbox (ircbot-connection-trigger-handlers connection))))            ; call trigger handlers on pm if enabled            (when (and                   (not triggered)                   (ircbot-connection-pmtrigger connection)                   (not (channel? recipient)))              (set! triggered #t)              (map (curry callback message)                   (unbox (ircbot-connection-trigger-handlers connection))))            ]))]      [(irc-message prefix JOIN params _)       (define prefix-match (regexp-match #rx^[^!]+ prefix))       (when prefix-match         (for ([callback (unbox (ircbot-connection-join-handlers connection))])           (callback (hash channel (first params)                           nick (first prefix-match)))))]      [(irc-message prefix PART params _)       (define prefix-match (regexp-match #rx^[^!]+ prefix))       (when prefix-match         (for ([callback (unbox (ircbot-connection-part-handlers connection))])           (callback (hash channel (first params)                           nick (first prefix-match)                           reason (second params)))))]      [(irc-message prefix QUIT params _)       (define prefix-match (regexp-match #rx^[^!]+ prefix))       (when prefix-match         (for ([callback (unbox (ircbot-connection-quit-handlers connection))])           (callback (hash nick (first prefix-match)                           reason (first params)))))]      [(irc-message prefix NICK params _)       (define prefix-match (regexp-match #rx^[^!]+ prefix))       (when prefix-match         (for ([callback (unbox (ircbot-connection-nick-handlers connection))])           (callback (hash oldnick (first prefix-match)                           newnick (first params)))))]      [_ (void)])    (loop)))In case this makes it more clear, here is an extremely simple bot implemented using the above module.#lang racket(require irc-bot.rkt)(define connection (ircbot-connect #:nick racketbot                                   #:username racketbot                                   #:realname RacketBot 9000                                   #:channels (list #racket)                                   #:triggers (list !rb)                                   #:pmtrigger #t)); log chats(ircbot-listen-chat connection                    ( (data respond)                      (printf <~a> ~a~n                              (hash-ref data sender)                              (hash-ref data message)))); log actions(ircbot-listen-action connection                      ( (data respond)                        (printf * ~a ~a~n                                (hash-ref data sender)                                (hash-ref data message)))); respond to simple commands(ircbot-listen-trigger connection                       ( (data respond)                         (define args (string-split (hash-ref data message)))                         (match args                           [(list hello)                            (respond (format Hello, ~a! (hash-ref data sender)))]                           [(list random n)                            (respond (number->string (random (string->number n))))]                           [_ (void)]                           )))(ircbot-listen connection)The bot prints standard chat messages and CTCP actions to stdout, and it responds to the !rb hello and !rb random <n> commands.
Is this IRC bot utility library Racket-y enough?
lisp;scheme;racket
Here are my comments after a brief reading:First, if you intend to make this library fit for general use, add documentation in comments for the provided functions. See the Racket style guide for examplesAlso, if you intend to make this a more general library, don't include default arguments (e.g. for ircbot-connect) unless they make sense for all users. Most users will connect to port 6667, but most users will not want to use the nick racketircbotI agree with Greg Hendershott, I don't you don't need boxes in your struct - just use the #:mutable keywordBreak your code up into shorter functions where the logic is complex (e.g. the PRIVMSG handling should be a separate function). This is a good practice for all languages, not just Racket.The use of curry seems awkward, but I haven't read the code in-depth enough to figure out if it's needed or notAllowing the channels argument to ircbot-say be either a list or a single channel strikes me as strange - document this at the very least, but consider changing it to just take a list.Your code is quite imperative in places, while Racket favors a more functional style. When you find yourself using set!, try to find a way to use just define instead, and only define each variable once.I rewrote ircbot-say in a more functional form (although it's still not perfect):  (define (ircbot-say connection message [channels (ircbot-connection-channels connection)])  (define channel-list (if (list? channels) channels (list channels)))  (cond ([(string-contains message /me )          (for ([channel channels])            (irc-send-command (ircbot-connection-connection connection)                              PRIVMSG                              channel (format :\u0001ACTION ~a\u0001 (substring message 4))))]         [else          (for ([channel channels])            (irc-send-message (ircbot-connection-connection connection) channel message))])))Otherwise, though, I think your code looks Racket-y. The match branches in ircbot-listen are exactly the kind of thing I envisioned when I wrote the irc package.