<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title>defn.io</title><link>https://defn.io</link><description>Bogdan Popa's website.</description><language>en-US</language><lastBuildDate>Wed, 14 Feb 2024 10:18:18 +0200</lastBuildDate><atom:link rel="self" href="https://defn.io/index.xml" type="application/rss+xml"></atom:link><item><title>One Billion Row Challenge in Racket</title><link>https://defn.io/2024/01/10/one-billion-row-challenge-in-racket</link><guid>https://defn.io/2024/01/10/one-billion-row-challenge-in-racket</guid><pubDate>Wed, 10 Jan 2024 21:51:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;I decided to have some fun tonight and work on a Racket solution to
&lt;a href="https://github.com/gunnarmorling/1brc?tab=readme-ov-file"&gt;the One Billion Row Challenge&lt;/a&gt;. I came up with a (pretty gnarly)
solution that completes in about 45 seconds on my machine, a 2023
12-core Apple M2 Max with 96GB of RAM. This is about on par with the
lower end of the optimized Java solutions shown in the challenge repo's
&lt;code&gt;README&lt;/code&gt;, when I run them on the same machine.&lt;/p&gt;&lt;p&gt;You can find the solution in this &lt;a href="https://gist.github.com/Bogdanp/b1edba407b5f7ab8794e0cb5ac197d34"&gt;GitHub Gist&lt;/a&gt;. It works by
splitting the work across several &lt;a href="https://docs.racket-lang.org/guide/parallelism.html#%28tech._place%29"&gt;places&lt;/a&gt;&lt;sup&gt;&lt;a href="#fn_1" id="fnref_1_1"&gt;1&lt;/a&gt;&lt;/sup&gt;, where each place
iterates over the input file in full. I initially attempted an approach
where the main place read the whole file into a &lt;code&gt;shared-bytes&lt;/code&gt; value and
then dispatched work to separate places, but that turned out to have too
much overhead from the places accessing the shared bytes. I had also
tried an approach where the main place sent individual work units (i.e.
lines) to the worker places, but that was killed by the overhead of
&lt;code&gt;place-channel-put&lt;/code&gt;&lt;sup&gt;&lt;a href="#fn_2" id="fnref_2_1"&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;&lt;p&gt;On boot, each place is given the path to the input file, the total
number of places (shards), its shard number and a place channel where it
can publish the aggregated data once it's done reading the file. Even
though each place iterates over the full input file, it only processes
those entries that match its shard number, skipping the rest and thereby
reducing the total number of work per place by the total number of
places.&lt;/p&gt;&lt;p&gt;The data gets read in in 10MB chunks, but the difference between buffer
sizes 1MB and over is negligible. The place-local data is stored in a
hash from location names to &lt;code&gt;state&lt;/code&gt; structs that contain the number of
entries, the lowest temperature, the highest temperature and the sum of
all temperatures at that location, as seen by that particular place. Any
one location's data may be spread across different places, and there is
a final step to combine the data from every place by location and print
out the results.&lt;/p&gt;&lt;p&gt;Originally, I had used a regular Racket hash to store the place-local
data, but I eventually rolled my own &lt;a href="https://en.wikipedia.org/wiki/Open_addressing"&gt;open-addressed&lt;/a&gt; hash table to
avoid the cost of copying location names from the input buffer to
individual bytes values for use with &lt;code&gt;hash-ref!&lt;/code&gt;. I don't remember
exactly how much time this saved, but I believe it was on the order
of about 10-15 seconds. This is probably less because my hash is any
good (it's not), and more because copying so many little strings gets
expensive fast. The code goes to great lengths to avoid copying as much
as possible and, thankfully, the built-in bytes procedures are set up to
help as much as they can.&lt;/p&gt;&lt;p&gt;A couple more minor, but potentially-interesting, things about the code
are its use of &lt;code&gt;#%declare&lt;/code&gt; to disable some contract checks, and the
&lt;code&gt;filtered-in&lt;/code&gt; require to rename imports from &lt;code&gt;racket/unsafe/ops&lt;/code&gt; to drop
the &lt;code&gt;unsafe-&lt;/code&gt; prefix.&lt;/p&gt;&lt;p&gt;I'm not completely satisfied with the result, so I may spend some more
time in the coming days trying to come up with ways to make this code
faster. Let me know if you have any ideas!&lt;/p&gt;&lt;section class="footnotes"&gt;&lt;ol&gt;&lt;li id="fn_1"&gt;&lt;p&gt;You might have noticed I spawned one place for every two CPU
cores on my machine. This turned out to be more performant than
trying to use one place per core, presumably because there was
less synchronization overhead between places. &lt;a class="footnote-backref" href="#fnref_1_1" title="Jump to reference" aria-label="Jump to reference"&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li id="fn_2"&gt;&lt;p&gt;My guess would be the issue here was partly contract checks and
partly the overhead of copying the bytes between places. It would
be nice if we had support for some kind of shared immutable bytes
with less overhead than regular shared bytes. That way one could
send the shared immutable bytes over to the worker places once and
subsequently send pairs of start and end indexes representing the
work units. &lt;a class="footnote-backref" href="#fnref_2_1" title="Jump to reference" aria-label="Jump to reference"&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/section&gt;&lt;/article&gt;</description></item><item><title>Extensible Linting for Racket</title><link>https://defn.io/2024/01/07/extensible-racket-linting</link><guid>https://defn.io/2024/01/07/extensible-racket-linting</guid><pubDate>Sun, 7 Jan 2024 18:37:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;Several years ago, I released &lt;a href="https://github.com/Bogdanp/racket-review"&gt;review&lt;/a&gt;, a little linter for Racket.
Recently, I added support for extending the linter from arbitrary Racket
packages.&lt;/p&gt;&lt;p&gt;Review analyzes code by pattern-matching on input programs'
surface-level syntax, rather than by analyzing their fully-expanded
forms. This is a nice property because it means that it can provide
advice about &lt;i&gt;how&lt;/i&gt; to use certain syntactic forms before those forms
are expanded to core Racket forms. It is, however, a double-edged sword
because it means that the linter has to have a rule for every syntactic
form it wants to lint, including ones that it can't possibly have any
knowledge of (eg. because they're user-defined). By default, review
simply ignores such unknown forms and does its best to analyze the rest
of the program. This works reasonably well, but may cause it to return
false-positive results or miss certain issues altogether.&lt;/p&gt;&lt;p&gt;I had had it in the back of my mind to make review user-extensible for
a while but never got around to it until this winter break. It turned
out to be much easier than I expected!&lt;/p&gt;&lt;h2&gt;How it Works&lt;/h2&gt;&lt;p&gt;When review is run, it looks for installed Racket &lt;a href="https://docs.racket-lang.org/guide/module-basics.html#%28tech._package%29"&gt;packages&lt;/a&gt; that have a
&lt;code&gt;review-exts&lt;/code&gt; entry in any of their &lt;code&gt;info.rkt&lt;/code&gt; files. Every entry must
be a list of 3-element lists of this form:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;[module-path should-review?-proc-id review-ext-proc-id]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;On boot, it dynamically requires the &lt;code&gt;should-review?-proc-id&lt;/code&gt; and
the &lt;code&gt;review-ext-proc-id&lt;/code&gt; from each &lt;code&gt;module-path&lt;/code&gt; of each entry it
discovers. For every expression in the input program, it applies each
&lt;code&gt;should-review?-proc&lt;/code&gt; against the expression's syntax object. If any
of the &lt;code&gt;should-review?&lt;/code&gt; procedures returns &lt;code&gt;#t&lt;/code&gt;, that procedure's
associated &lt;code&gt;review-ext-proc&lt;/code&gt; is then called on that syntax object. The
first match wins and, if there are no matches, then review falls back to
its own analysis of the expression.&lt;/p&gt;&lt;p&gt;Review extension procedures can use the bindings provided by the
&lt;code&gt;review/ext&lt;/code&gt; module to track any errors or warnings they find or to
manage scopes and bindings.&lt;/p&gt;&lt;h2&gt;A Concrete Example&lt;/h2&gt;&lt;p&gt;The above description probably makes things sound more complicated than
they really are. So, let's try to write a custom linter. Say we have a
custom form for defining records:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define-record RECORD-ID
  (FIELD ...))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Where:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;FIELD
  = [FIELD-ID : TYPE-ID]

TYPE-ID
  = Int
  | Str
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The form can be used as follows:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define-record Person
 ([name : Str]
  [age : Int]))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For every record, the above definition generates a constructor
procedure (&lt;code&gt;make-Person&lt;/code&gt;), a predicate (&lt;code&gt;Person?&lt;/code&gt;), accessors for every
field (&lt;code&gt;Person-name&lt;/code&gt; and &lt;code&gt;Person-age&lt;/code&gt;), and setters for every field
(&lt;code&gt;set-Person-name&lt;/code&gt; and &lt;code&gt;set-Person-age&lt;/code&gt;).&lt;/p&gt;&lt;p&gt;We can start by adding a module called &lt;code&gt;review.rkt&lt;/code&gt; to the root of
our collection. In it, we'll have to define our &lt;code&gt;should-review?&lt;/code&gt; and
&lt;code&gt;review-syntax&lt;/code&gt; procedures:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;#lang racket/base

(require review/ext
         syntax/parse)

(provide
 should-review?
 review-syntax)

(define (should-review? stx)
  (syntax-parse stx
    #:datum-literals (define-record)
    [(define-record . _rest) #t]
    [_ #f]))

(define (review-syntax stx)
  stx)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;should-review?&lt;/code&gt; procedure returns &lt;code&gt;#t&lt;/code&gt; whenever it is given a list
whose first item is the literal symbol &lt;code&gt;define-record&lt;/code&gt; and &lt;code&gt;#f&lt;/code&gt; in every
other case. For now, the &lt;code&gt;review-syntax&lt;/code&gt; procedure is just a stub that
does nothing. With this module in hand, we can now update our &lt;code&gt;info.rkt&lt;/code&gt;
file to add a &lt;code&gt;review-exts&lt;/code&gt; entry:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;#lang info

...

;; Assuming our collection is named "example".
(define review-exts
  '([example/review should-review? review-syntax]))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As soon as we do this, running &lt;code&gt;raco review&lt;/code&gt; against a program should
defer to the &lt;code&gt;review-syntax&lt;/code&gt; procedure above any time it encounters a
&lt;code&gt;(define-record ...)&lt;/code&gt; form. So, let's extend it to perform some checks:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;#lang racket/base

#|review: ignore|#

(require review/ext
         syntax/parse/pre)

(provide
 should-review?
 review-syntax)

(define (should-review? stx)
  (syntax-parse stx
    #:datum-literals (define-record)
    [(define-record . _rest) #t]
    [_ #f]))

(define-syntax-class field-definition
  #:datum-literals (: Int Str)
  (pattern [id:id : {~or Int Str}])
  (pattern [id:id : type] #:do [(track-error #'type "invalid type")])
  (pattern e
           #:attr id #'stub
           #:do [(track-error this-syntax "invalid field definition")]))

(define-syntax-class record-definition
  (pattern (define-record record-id:id
             (record-field:field-definition ...))))

(define (review-syntax stx)
  (syntax-parse stx
    [d:record-definition #'d]
    [_ (begin0 stx
         (track-error stx "invalid record definition"))]))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Above, we define syntax classes to pattern match on record and field
definitions. Match failures, including using invalid types for field
definitions, are reported as errors. This already gets us a useful
little linter that checks that our uses of &lt;code&gt;define-record&lt;/code&gt; are
syntactically valid. With a little more work, we can extend our
&lt;code&gt;record-definition&lt;/code&gt; class to track the bindings generated by
&lt;code&gt;define-record&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define-syntax-class record-definition
  (pattern (define-record record-id:id
             (record-field:field-definition ...))
           #:do [(track-binding #'record-id "make-~a")
                 (track-binding #'record-id "~a?")
                 (define record-id-sym (syntax-&amp;gt;datum #'record-id))
                 (for ([field-id-stx (in-list (syntax-e #'(record-field.id ...)))])
                   (track-binding field-id-stx (format "~a-~~a" record-id-sym))
                   (track-binding field-id-stx (format "set-~a-~~a" record-id-sym)))]))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now, our linter will complain if we have any unused fields. You can
find the full example on &lt;a href="https://github.com/Bogdanp/racket-review-ext-example"&gt;GitHub&lt;/a&gt;. For a slightly more complex
example, check out the custom linter for deta on &lt;a href="https://github.com/Bogdanp/deta/tree/3b2d819f52e79e253b6b97e668c3f65a0cab032d/deta-lint-lib"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;&lt;h2&gt;Limitations&lt;/h2&gt;&lt;p&gt;This approach has some obvious limitations. The binding tracking is
approximate and done as a side-channel to the way Racket actually does
things. Syntactic forms are recognized symbolically rather than by
binding, so linters have no way of recognizing renamed imports, nor
do they have a way of distinguishing the same names across different
libraries.&lt;/p&gt;&lt;p&gt;Despite all that, I get valuable support from review in the 80% &lt;sup&gt;&lt;a href="#fn_1" id="fnref_1_1"&gt;1&lt;/a&gt;&lt;/sup&gt; of
cases where it does work well, and I expect to get more out of it as I
wrote more extensions for bits of custom syntax that I have here and
there. Maybe you will too!&lt;/p&gt;&lt;section class="footnotes"&gt;&lt;ol&gt;&lt;li id="fn_1"&gt;&lt;p&gt;A made-up number. Probably, it fails less than 20% of the time,
but I haven't actually kept track. &lt;a class="footnote-backref" href="#fnref_1_1" title="Jump to reference" aria-label="Jump to reference"&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/section&gt;&lt;/article&gt;</description></item><item><title>Advent of Racket 2023/07 - Camel Cards</title><link>https://defn.io/2023/12/07/advent-of-racket-2023-day-07</link><guid>https://defn.io/2023/12/07/advent-of-racket-2023-day-07</guid><pubDate>Thu, 7 Dec 2023 10:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;Quite a fun one &lt;a href="https://adventofcode.com/2023/day/7"&gt;today&lt;/a&gt;. The example input looks like this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;32T3K 765
T55J5 684
KK677 28
KTJJT 220
QQQJA 483
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The first column represents a poker hand and the second a bid. We're
to sort the hands, multiply the bid and the hand's rank (its position
after sorting), then sum up the results.&lt;/p&gt;&lt;p&gt;We can read the data as a vector of &lt;code&gt;(hand . bid)&lt;/code&gt; pairs:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define hands
  (call-with-input-file "day07.txt"
    (lambda (in)
      (for/vector ([line (in-lines in)])
        (match-define (regexp #rx"([^ ]+) (.+)"
                              (list _ hand (app string-&amp;gt;number bid)))
          line)
        (cons hand bid)))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To compute the type of hand we have, we can count the kinds of cards in
a hand, sort the results and pattern match on them, since each hand is
guaranteed to be of exactly one type:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (hand-counts h)
  (for/fold ([counts (hasheqv)])
            ([c (in-string h)])
    (hash-update counts c add1 0)))

(define (hand-type h)
  (define counts
    (sort
     (hash-&amp;gt;list
      (hand-counts h))
     #:key cdr &amp;gt;))
  (match counts
    [`(,_) 'five-of-a-kind]
    [`((,_ . 4) ,_) 'four-of-a-kind]
    [`((,_ . 3) ,_) 'full-house]
    [`((,_ . 3) ,_ ,_) 'three-of-a-kind]
    [`((,_ . 2) (,_ . 2) ,_) 'two-pair]
    [`((,_ . 2) ,_ ,_ ,_) 'one-pair]
    [`(,_ ,_ ,_ ,_ ,_) 'high-card]))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next, let's define a sort order for each type of hand:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (hand-type-numeric h)
  (case (hand-type h)
    [(five-of-a-kind) 6]
    [(four-of-a-kind) 5]
    [(full-house) 4]
    [(three-of-a-kind) 3]
    [(two-pair) 2]
    [(one-pair) 1]
    [(high-card) 0]))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In order to break ties, we're going to have to compare individual
cards in a hand, left-to-right. Higher cards beat lower cards. So, we
can define a procedure to get a card's score:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (card-score c)
  (match c
    [#\2 2]
    [#\3 3]
    [#\4 4]
    [#\5 5]
    [#\6 6]
    [#\7 7]
    [#\8 8]
    [#\9 9]
    [#\T 10]
    [#\J 11]
    [#\Q 12]
    [#\K 13]
    [#\A 14]))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally, we can define a procedure for sorting hands:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (hand&amp;gt; a b)
  (define an (hand-type-numeric a))
  (define bn (hand-type-numeric b))
  (if (= an bn)
      (for/fold ([ok? #t])
                ([ca (in-string a)]
                 [cb (in-string b)])
        (define cas (card-score ca))
        (define cbs (card-score cb))
        #:break (or (&amp;gt; cas cbs)
                    (not ok?))
        (and ok? (= cas cbs)))
      (&amp;gt; an bn)))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When there's a tie, we fall back to comparing the cards in both hands
positionally using &lt;code&gt;card-score&lt;/code&gt;. Otherwise, the better hand wins.&lt;/p&gt;&lt;p&gt;To compute the solution for part one, we can sort the hands then sum
up the winnings for each hand:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (compute-winnings hands [hand&amp;gt; hand&amp;gt;])
  (let ([hands (vector-copy hands)])
    (vector-sort! hands hand&amp;gt; #:key car)
    (for/sum ([(h idx) (in-indexed (in-vector hands))])
      (define rank (- (vector-length hands) idx))
      (* (cdr h) rank))))

(compute-winnings hands)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For part two, we need to reinterpret joker cards such that whenever we
compute the hand type for a hand that contains jokers, we have to
replace the jokers in that hand with whatever cards would make it the
best hand it can be. When comparing individual cards, jokers are now
the weakest card type.&lt;/p&gt;&lt;p&gt;Given any hand that contains jokers, we can find the best hand it can
be by iterating through all the possible replacements of non-joker
cards:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (find-strongest h)
  (define counts
    (hand-counts h))
  (cond
    [(hash-has-key? counts #\J)
     (define non-jokers
       (remv #\J (hash-keys counts)))
     ;; When
     ;;  non-jokers = '(#\Q #\2)
     ;; Then
     ;;  replacementss = '((#\Q #\Q) (#\Q #\2) (#\2 #\2))
     (define replacementss
       (remove-duplicates
        (map (λ (cards) (sort cards char&amp;gt;?))
             (apply cartesian-product (make-list (hash-ref counts #\J) non-jokers)))))
     (for/fold ([res #f] #:result (or res h))
               ([replacements (in-list replacementss)])
       (define replacement-hand
         (for/fold ([chars null]
                    [replacements replacements]
                    #:result (apply string (reverse chars)))
                   ([c (in-string h)])
           (if (char=? c #\J)
               (values (cons (car replacements) chars) (cdr replacements))
               (values (cons c chars) replacements))))
       (if (or (not res)
               (&amp;gt; (hand-type-numeric replacement-hand)
                  (hand-type-numeric res)))
           replacement-hand
           res))]
    [else h]))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next, we can update &lt;code&gt;card-score&lt;/code&gt; to take an optional argument
representing the score for joker cards:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (card-score c [j-score 11])
  (match c
    [#\2 2]
    [#\3 3]
    [#\4 4]
    [#\5 5]
    [#\6 6]
    [#\7 7]
    [#\8 8]
    [#\9 9]
    [#\T 10]
    [#\J j-score]
    [#\Q 12]
    [#\K 13]
    [#\A 14]))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And we can update the signature for &lt;code&gt;hand&amp;gt;&lt;/code&gt; to make it possible for
the caller to pass in custom procedures for computing hand types and
card scores:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (hand&amp;gt; a b
               #:hand-type-numeric-proc [hand-type-numeric hand-type-numeric]
               #:card-score-proc [card-score card-score])
  ...)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Its body remains unchanged. With these changes in place, we can solve
part two by passing in a custom sort procedure to &lt;code&gt;compute-winnings&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(compute-winnings
 hands
 (lambda (a b)
   (hand&amp;gt;
    #:hand-type-numeric-proc (compose1 hand-type-numeric find-strongest)
    #:card-score-proc (λ (c) (card-score c 1))
    a b)))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will be my last post in this series, but I'll probably keep solving
puzzles for another week or so. Check out &lt;a href="https://github.com/bogdanp/aoc2023"&gt;my solutions repo&lt;/a&gt; if you
want to follow along!&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Advent of Racket 2023/06 - Wait For It</title><link>https://defn.io/2023/12/06/advent-of-racket-2023-day-06</link><guid>https://defn.io/2023/12/06/advent-of-racket-2023-day-06</guid><pubDate>Wed, 6 Dec 2023 10:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;A really quick one &lt;a href="https://adventofcode.com/2023/day/6"&gt;today&lt;/a&gt;. The example input looks like this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Time:      7  15   30
Distance:  9  40  200
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Every pair of rows represents a race, where the distance is the record
distance so far. By pausing at the beginning of the race we gain one
distance unit per time unit paused, but lose that time unit in the
process. We're to determine the distinct durations we could have paused
for during every race in order to beat the record distance, then
multiply the results.&lt;/p&gt;&lt;p&gt;We can read the races as a list of pairs:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (read-integers in start)
  (map string-&amp;gt;number
       (string-split
        (substring (read-line in) start))))

(define races
  (call-with-input-file "day06.txt"
    (lambda (in)
      (define times (read-integers in (string-length "Time:")))
      (define distances (read-integers in (string-length "Distance:")))
      (map cons times distances))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And write a procedure to determine whether or not a given hold time
would beat the record distance:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (win? r hold-time)
  (match-define (cons race-time distance) r)
  (define travel-time
    (- race-time hold-time))
  (&amp;gt; (* hold-time travel-time) distance))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then all we have to do is count the number of times a race could be
won within its allotted time:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (winning-hold-times r)
  (for/sum ([i (in-range (add1 (car r)))]
            #:when (win? r i))
    1))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And multiply those counds for every race together:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(for/fold ([res 1])
          ([r (in-list races)])
  (* res (winning-hold-times r)))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For part two, we need to append all the race times and durations
together into one long race. So, instead of interpreting our example
input as three separate races, we need to interpret it as if it were
written without any spaces:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Time:      71530
Distance:  940200
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let's append the races together into our input for part two:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define one-race
  (let ([m (λ (n) (expt 10 (exact-ceiling (log n 10))))])
    (for/fold ([t 0] [d 0] #:result (cons t d))
              ([r (in-list races)])
      (match-define (cons r-t r-d) r)
      (values (+ (* t (m r-t)) r-t)
              (+ (* d (m r-d)) r-d)))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally, we can just call our &lt;code&gt;winning-hold-times&lt;/code&gt; procedure on the
&lt;code&gt;one-race&lt;/code&gt; value to find the solution for part two. The input is small
enough to brute force in a couple hundred milliseconds.&lt;/p&gt;&lt;p&gt;If the input for part two were larger, we could use a closed form
solution. We've already expressed a race's solution as:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;x(t - x) &amp;gt; d
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Where &lt;code&gt;x&lt;/code&gt; is the hold time required to beat the record &lt;code&gt;d&lt;/code&gt;. We can
expand that expression to:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;-x^2 + tx - d &amp;gt; 0
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And we can solve for &lt;code&gt;x&lt;/code&gt; using the &lt;a href="https://en.wikipedia.org/wiki/Quadratic_formula"&gt;quadratic formula&lt;/a&gt; and get all
possible values of &lt;code&gt;x&lt;/code&gt; for any given distance:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (winning-hold-times* r)
  (match-define (cons t d) r)
  (define discriminant (sqrt (- (* t t) (* 4 d))))
  (define hi (exact-ceiling (/ (+ t discriminant) 2)))
  (define lo (exact-floor (/ (- t discriminant) 2)))
  (- hi lo 1))
&lt;/code&gt;&lt;/pre&gt;&lt;/article&gt;</description></item><item><title>Advent of Racket 2023/05 - Fertilizer</title><link>https://defn.io/2023/12/05/advent-of-racket-2023-day-05</link><guid>https://defn.io/2023/12/05/advent-of-racket-2023-day-05</guid><pubDate>Tue, 5 Dec 2023 10:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;The example input for &lt;a href="https://adventofcode.com/2023/day/5"&gt;day five&lt;/a&gt; looks like this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;seeds: 79 14 55 13

seed-to-soil map:
50 98 2
52 50 48

soil-to-fertilizer map:
0 15 37
37 52 2
39 0 15

fertilizer-to-water map:
49 53 8
0 11 42
42 0 7
57 7 4

water-to-light map:
88 18 7
18 25 70

light-to-temperature map:
45 77 23
81 45 19
68 64 13

temperature-to-humidity map:
0 69 1
1 0 69

humidity-to-location map:
60 56 37
56 93 4
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;seeds&lt;/code&gt; line contains the set of inputs we're supposed to pass
through the given maps and each map feeds into the next. The entries in
a map represent the ranges of values that can be converted by the map.
Values outside the given ranges are passed through unchanged. After
feeding every seed value through all the maps, we need to report the
minimum value that comes out the other end.&lt;/p&gt;&lt;p&gt;I decided to make a struct to represent the individual ranges in a map:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(struct mapping (dst src len)
  #:transparent)

(define (parse-mapping str)
  (apply mapping (map string-&amp;gt;number (string-split str))))
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;&amp;gt; (parse-mapping "50 98 2")
(mapping 50 98 2)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To read a map, we ignore the empty line before its definition, ignore
the line that names the map and then read the ranges until we see an
empty line or reach the end of file:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (read-map in)
  (void (read-line in))
  (void (read-line in))
  (let loop ([mappings null])
    (define c (peek-char in))
    (cond
      [(or (eof-object? c)
           (eqv? c #\newline))
       (reverse mappings)]
      [else
       (loop
        (cons
         (parse-mapping (read-line in))
         mappings))])))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We read the initial seeds and then the seven maps. Since the maps feed
into each other, there's no need to keep track of which map is which,
so a list of maps is enough.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define-values (seeds maps)
  (call-with-input-file "day05.txt"
    (lambda (in)
      (define seeds
        (map
         string-&amp;gt;number
         (string-split
          (substring (read-line in) 6))))
      (values
       seeds
       (for/list ([_ (in-range 7)])
         (read-map in))))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Every map is just a list of &lt;code&gt;mapping&lt;/code&gt; struct instances and for any given
mapping, we can map a value by checking if it's within the specified
range and adding to it the delta between the &lt;code&gt;dst&lt;/code&gt; and &lt;code&gt;src&lt;/code&gt; values:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (mapping-map m v)
  (match-define (mapping dst src len) m)
  (and (&amp;gt;= v src)
       (&amp;lt;= v (+ src len))
       (+  v (- dst src))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Given a set of &lt;code&gt;mapping&lt;/code&gt;s (i.e. a map), we can write a lookup procedure
that returns the mapped value for any given value:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (look-up mappings v)
  (or
   (for*/first ([m (in-list mappings)]
                [mapped-v (in-value (mapping-map m v))]
                #:when mapped-v)
     mapped-v)
   v))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;With that, we can write a procedure to run a seed through all the maps:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (find-seed-location maps seed)
  (for/fold ([value seed])
            ([mappings (in-list maps)])
    (look-up mappings value)))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And, finally, go through all the seeds and find the minimum location:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(for/fold ([res +inf.0])
          ([s (in-list seeds)])
  (define loc
    (find-seed-location maps s))
  (if (&amp;lt; loc res) loc res))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For part two, we're asked to reinterpret the initial list of seeds as
pairwise ranges of seeds instead of individual seed numbers. So &lt;code&gt;79 14&lt;/code&gt;
in our initial example now represents the seeds from &lt;code&gt;79&lt;/code&gt; to &lt;code&gt;93&lt;/code&gt;. The
actual input is large enough that brute forcing a solution would take
a while.&lt;/p&gt;&lt;p&gt;Instead of iterating over all the seeds in every range, we can split
our ranges against all the mappings in a map, then map the values of
the split ranges and then feed the mapped ranges into the subsequent
maps.&lt;/p&gt;&lt;p&gt;First, let's collect the ranges into a list of pairs:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define ranges
  (let loop ([pairs null]
             [seeds seeds])
    (cond
      [(null? seeds)
       (reverse pairs)]
      [else
       (loop
        (cons
         (cons (car seeds)
               (+ (car seeds)
                  (cadr seeds)))
         pairs)
        (cddr seeds))])))
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;&amp;gt; (car ranges)
(79 . 93)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next, let's write a procedure to split a range for any given mapping:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (mapping-split-range m r)
  (match-define (mapping _dst src len) m)
  (match-define (cons lo hi) r)
  (define src-lo src)
  (define src-hi (+ src len))
  (cond
    [(and (&amp;lt; lo src-lo)
          (&amp;lt; hi src-lo))
     (list r)]
    [(and (&amp;gt; lo src-hi)
          (&amp;gt; hi src-hi))
     (list r)]
    [(and (&amp;lt; lo src-lo)
          (&amp;gt; hi src-hi))
     (list
      (cons lo (sub1 src-lo))
      (cons src-lo src-hi)
      (cons (add1 src-hi) hi))]
    [(&amp;lt; lo src-lo)
     (list
      (cons lo (sub1 src-lo))
      (cons src-lo hi))]
    [(&amp;gt; hi src-hi)
     (list
      (cons lo src-hi)
      (cons (add1 src-hi) hi))]
    [else
     (list
      (cons lo hi))]))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And a procedure to map a range for a mapping:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (mapping-map-range m r)
  (define m-lo (mapping-map m (car r)))
  (define m-hi (mapping-map m (cdr r)))
  (and m-lo m-hi (cons m-lo m-hi)))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let's write a procedure to map the ranges for a given set of mappings:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (map-ranges mappings ranges)
  ;; Split the ranges against all the mappings.
  (define split-ranges
    (let loop ([ranges ranges]
               [mappings mappings])
      (if (null? mappings)
          ranges
          (loop
           (apply append
                  (for/list ([r (in-list ranges)])
                    (mapping-split-range (car mappings) r)))
           (cdr mappings)))))
  ;; Then map the split ranges.
  (for/list ([r (in-list split-ranges)])
    (or
     (for*/first ([m (in-list mappings)]
                  [m-r (in-value (mapping-map-range m r))]
                  #:when m-r)
       m-r)
     r)))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And a procedure to feed the ranges through all the maps:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (find-location-ranges maps ranges)
  (for/fold ([ranges ranges])
            ([mappings (in-list maps)])
    (map-ranges mappings ranges)))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally, we can compute the result for part two by keeping track of the
smallest start value of every funneled range:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(for*/fold ([res +inf.0])
           ([r (in-list (find-location-ranges maps ranges))])
  (define loc (car r))
  (if (&amp;lt; loc res) loc res))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;An alternative, and probably less finicky, but less efficient, way to
solve part two would've been to iterate up from 0 and pass the numbers
backwards through the funnel, stopping on the first number that fit in
one of the seed ranges. In my case that approach would've found the
solution in a little under ten million iterations.&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Advent of Racket 2023/04 - Scratchcards</title><link>https://defn.io/2023/12/04/advent-of-racket-2023-day-04</link><guid>https://defn.io/2023/12/04/advent-of-racket-2023-day-04</guid><pubDate>Mon, 4 Dec 2023 10:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;&lt;a href="https://adventofcode.com/2023/day/4"&gt;Day four&lt;/a&gt; starts out very simple. We're given an input where we have
two lists of numbers per line. Per line (or "card"), we get one point
for the first number in the second list that is also found in the first,
and the score doubles for every subsequent match. The example input
looks like this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Card 1: 41 48 83 86 17 | 83 86  6 31 17  9 48 53
Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19
Card 3:  1 21 53 59 44 | 69 82 63 72 16 21 14  1
Card 4: 41 92 73 84 69 | 59 84 76 51 58  5 54 83
Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36
Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Since part one seemed a little too easy, I decided to parse the input
into a struct, in anticipation of a harder part two.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;;; id : int
;; winning : listof int
;; have : listof int
(struct card (id winning have)
  #:transparent)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Parsing the cards is straightforward enough, though I did spend a
minute scratching my head because I forgot to escape the &lt;code&gt;|&lt;/code&gt;
character:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;(define cards
  (call-with-input-file "day04.txt"
    (lambda (in)
      (for/vector ([line (in-lines in)])
        (match-define (regexp #rx"Card +([0-9]+): ([^|]+) \\| (.+)"
                              (list _ (app string-&amp;gt;number id) winning-str have-str))
          line)
        (card
         id
         (map string-&amp;gt;number (string-split winning-str))
         (map string-&amp;gt;number (string-split have-str)))))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I had originally stored the set of cards as a list, but changed it to a
vector for part two. We'll see why in a bit. In the mean time, computing
part one is just a matter of determining the number of matches in each
card:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (card-matches c)
  (for/sum ([n (in-list (card-have c))]
            #:when (memv n (card-winning c)))
    1))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And computing the score per card:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (card-score c)
  (define matches
    (card-matches c))
  (cond
    [(zero? matches) 0]
    [else (expt 2 (sub1 matches))]))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Putting those functions together, we get:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define part1
  (for/sum ([c (in-vector cards)])
    (card-score c)))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For part two, the problem goes exponential. For every card, the number
of matches that we find represents subsequent cards that we have to
check for matches. We have to recursively add up all the cards we see.&lt;/p&gt;&lt;p&gt;We need a function that returns the won cards for any given card:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (card-wins c)
  (match-define (card id _winning _have) c)
  (for/list ([i (in-range 0 (card-matches c))])
    (vector-ref cards (+ id i))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This function is the reason why I stored the cards as a vector earlier
on. With this function in hand, we can now write a function to compute
the number of cards seen when starting from any given card:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (add-counts cs)
  (apply hash-union cs #:combine +))

(define (card-counts c)
  (add-counts
   (list*
    (hasheqv (card-id c) 1)
    (map card-counts (card-wins c)))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Running &lt;code&gt;card-counts&lt;/code&gt; on the first card in the example input yields:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;&amp;gt; (card-counts (vector-ref cards 0))
'#hasheqv((1 . 1) (2 . 1) (3 . 2) (4 . 4) (5 . 7))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To compute the result for part two we just have to add up all the
counts for all the cards we have:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(apply + (hash-values
          (add-counts
           (for/list ([c (in-vector cards)])
             (card-counts c)))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This works fine for the example input, but the real input is much larger
and requires many more iterations. The trick to notice here is that
calling &lt;code&gt;card-counts&lt;/code&gt; on an individual card will always return the same
result, so we can simply memoize the result for every card and greatly
reduce the number of iterations required to compute the solution.&lt;/p&gt;&lt;p&gt;All we have to do is change &lt;code&gt;card-counts&lt;/code&gt; to:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define card-counts
  (let ([memo (make-hasheqv)])
    (lambda (c)
      (hash-ref!
       memo (card-id c)
       (lambda ()
         (add-counts
          (list*
           (hasheqv (card-id c) 1)
           (map card-counts (card-wins c)))))))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;memo&lt;/code&gt; hash keeps a mapping of card ids to the number of cards
seen when starting from that card. If a card's id is already in the
hash, we return its associated value, otherwise we compute the result
and store it in the hash for subsequent lookups.&lt;/p&gt;&lt;p&gt;That's it for day four!&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Advent of Racket 2023/03 - Gear Ratios</title><link>https://defn.io/2023/12/03/advent-of-racket-2023-day-03</link><guid>https://defn.io/2023/12/03/advent-of-racket-2023-day-03</guid><pubDate>Sun, 3 Dec 2023 10:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;Let's get right into &lt;a href="https://adventofcode.com/2023/day/3"&gt;day three&lt;/a&gt;. The first part of the puzzle today
is to find any numbers adjacent to a non-numeric, non-period symbol in
a table and add them all up. The example input looks like this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;467..114..
...*......
..35..633.
......#...
617*......
.....+.58.
..592.....
......755.
...$.*....
.664.598..
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The first thing I did was count the number of columns and rows. I
noticed the example input was a square, so I took a peek at my own input
and it, too, was square. So, I decided to read the whole input into a
unidimensional vector:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define table
  (call-with-input-file "day03.txt"
    (lambda (in)
      (for*/vector ([line (in-lines in)]
                    [char (in-string line)])
        char))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Since we're going to be looking for adjacent positions, we need to
know what the table's stride is. Since the input table is square,
that's easy:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define s (sqrt (vector-length table)))
(define -s (- s))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next, we can define a function to match special symbols:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (engine-symbol? c)
  (and (not (eqv? c #\.))
       (not (char-numeric? c))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And a function to determine if a position has any adjacent special
symbols:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (has-adjacent? t pos [ok? engine-symbol?])
  (for*/first ([d (in-list (list (- -s 1) -s (+ -s 1)
                                      -1           1
                                 (-  s 1)  s (+  s 1)))]
               [idx (in-value (+ pos d))]
               #:when (and (&amp;gt;= idx 0)
                           (&amp;lt;  idx (vector-length t))
                           (ok? (vector-ref t idx))))
    idx))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For any given index into the table, &lt;code&gt;has-adjacent?&lt;/code&gt; checks its 8
adjacent indexes for a position for which &lt;code&gt;ok?&lt;/code&gt; returns &lt;code&gt;#t&lt;/code&gt;,
returning the first match or &lt;code&gt;#f&lt;/code&gt; if no matches are found.&lt;/p&gt;&lt;p&gt;To compute part one, all we have to do is iterate over the table,
piece any run of digits into a number and add any number for which
we've found an adjacent symbol to the total:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(for/fold ([num 0]
           [ok? #f]
           [total 0]
           #:result (if ok? (+ num total) total))
          ([(c idx) (in-indexed (in-vector table))])
  (if (char-numeric? c)
      (values (+ (* num 10) (char-&amp;gt;decimal c))
              (or ok? (has-adjacent? table idx))
              total)
      (values 0 #f (if ok? (+ num total) total))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The one edge case to watch out for here is that a table may end in a
number, so we need to make sure the final result accounts for that and
add the last-collected number to the total if necessary. That case is
covered by the &lt;code&gt;#:result&lt;/code&gt; clause above.&lt;/p&gt;&lt;p&gt;As in day two, the second part of today's puzzle flips the problem on
its head and asks us to instead find any two numbers that are adjacent
to a &lt;code&gt;*&lt;/code&gt; symbol, multiply them together and sum them up.&lt;/p&gt;&lt;p&gt;To solve part two, we can just iterate over the table and whenever
we see a &lt;code&gt;*&lt;/code&gt; symbol, check if it has any adjacent numbers. If it has
exactly two of them, we can multiply them together and add them to the
total. It would be nice if we had a variant of &lt;code&gt;has-adjacent?&lt;/code&gt; that
returns the indexes of adjacent symbols, so let's go ahead and write
that function:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define-syntax-rule (define-finder id for*-id)
  (define (id t pos [ok? engine-symbol?])
    (for*-id ([d (in-list (list (- -s 1) -s (+ -s 1)
                                     -1           1
                                (-  s 1)  s (+  s 1)))]
              [idx (in-value (+ pos d))]
              #:when (and (&amp;gt;= idx 0)
                          (&amp;lt;  idx (vector-length t))
                          (ok? (vector-ref t idx))))
      idx)))

(define-finder find-adjacent for*/list)
(define-finder has-adjacent? for*/first)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I wanted to preserve the short-circuiting behavior of &lt;code&gt;has-adjacent?&lt;/code&gt;,
so I decided to abstract over its implementation using a macro. In this
case, the &lt;code&gt;define-finder&lt;/code&gt; macro lets us plug in different for loop
behaviors into the same implementation. So, &lt;code&gt;find-adjacent&lt;/code&gt; works the
same way as &lt;code&gt;has-adjacent?&lt;/code&gt;, but it returns all the adjacent positions
for which &lt;code&gt;ok?&lt;/code&gt; is true instead of just the first one.&lt;/p&gt;&lt;p&gt;One problem to consider, though, is that a symbol may be adjacent to
multiple digits belonging to the same number. For example:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;123
.*.
456
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Given the above example, the &lt;code&gt;find-adjacent&lt;/code&gt; procedure would return the
position of every digit surrounding the &lt;code&gt;*&lt;/code&gt; symbol. So, we need a way
get the numbers at a given set of positions that is aware of this issue
and skips redundant positions.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;; invariant: indexes always start out as valid digit positions in the table
(define (get-numbers t is)
  (let loop ([is is]
             [nums null])
    (cond
      [(null? is) nums]
      [else
       (define-values (num rem-is)
         (let get-number ([i (car is)])
           (if (or (&amp;lt; i 0)
                   (not (char-numeric? (vector-ref t i))))
               (for/fold ([n 0] [rem-is is])
                         ([(c idx) (in-indexed (in-vector t (add1 i)))])
                 #:break (not (char-numeric? c))
                 (values
                  (+ (* n 10) (char-&amp;gt;decimal c))
                  (remq (+ (add1 i) idx) rem-is)))
               (get-number (sub1 i)))))
       (loop rem-is (cons num nums))])))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;get-numbers&lt;/code&gt; procedure takes a table and a list of indexes into
that table. It then tries the indexes in order and walks backwards from
each index to find the start of a number, then it reads the number
forwards, removing any indexes that it sees along the way from the
pending list of indexes, and collecting the numbers into a list.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;&amp;gt; (define t
    (vector #\1 #\2 #\.
            #\3 #\4 #\.))
&amp;gt; (get-numbers t '())
'()
&amp;gt; (get-numbers t '(0))
'(12)
&amp;gt; (get-numbers t '(1))
'(12)
&amp;gt; (get-numbers t '(0 1))
'(12)
&amp;gt; (get-numbers t '(0 1 3))
'(34 12)
&amp;gt; (get-numbers t '(0 1 3 4))
'(34 12)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;With &lt;code&gt;find-adjacent&lt;/code&gt; and &lt;code&gt;get-numbers&lt;/code&gt; in hand, we can just iterate
over the table and compute the result:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(for/fold ([total 0])
          ([(c idx) (in-indexed (in-vector table))]
           #:when (eqv? c #\*))
  (define adjacent-numbers
    (get-numbers table (find-adjacent table idx char-numeric?)))
  (if (= (length adjacent-numbers) 2)
      (+ total (apply * adjacent-numbers))
      total))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Any time we see a &lt;code&gt;*&lt;/code&gt;, we find all the adjacent numeric positions and
get the numbers at those positions. If we have exactly two adjacent
numbers, then we multiply them together and add them to the sum. Easy
peasy.&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Advent of Racket 2023/02 - Cube Conondrum</title><link>https://defn.io/2023/12/02/advent-of-racket-2023-day-02</link><guid>https://defn.io/2023/12/02/advent-of-racket-2023-day-02</guid><pubDate>Sat, 2 Dec 2023 10:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;&lt;a href="https://adventofcode.com/2023/day/2"&gt;Today's puzzle&lt;/a&gt; was quick and easy. For the first part, we're to take
a list of "games" as input where each game has an id and a set of
semicolon-separated sets of plays and report the sum of the game ids
where the sets match a certain condition. The example input looks like:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green
Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue
Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red
Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red
Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I decided to make a struct to represent each game:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(struct game (id sets)
  #:transparent)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And to parse each set into a hash from colors to the number of blocks:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (parse-set set-str)
  (for/hasheq ([reveal-str (in-list (string-split set-str ","))])
    (match-define (regexp #rx"([0-9]+) ([a-z]+)"
                          (list _
                                (app string-&amp;gt;number blocks)
                                (app string-&amp;gt;symbol color)))
      reveal-str)
    (values color blocks)))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;parse-set&lt;/code&gt; procedure splits the input on commas and extracts the
block count and color of each reveal using a regular expression. The
&lt;a href="https://docs.racket-lang.org/search/index.html?q=for/hasheq"&gt;&lt;code&gt;for/hasheq&lt;/code&gt;&lt;/a&gt; form then collects the results of each iteration into a
hash. The &lt;a href="https://docs.racket-lang.org/search/index.html?q=match"&gt;&lt;code&gt;match&lt;/code&gt;&lt;/a&gt; form's &lt;code&gt;app&lt;/code&gt; syntax comes in handy when you want
to transform a matched value before binding it.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;&amp;gt; (parse-set "")
'#hasheq()
&amp;gt; (parse-set "10 red, 3 blue")
'#hasheq((blue . 3) (red . 10))
&amp;gt; (parse-set "10 red, 3 blue, 5 green")
'#hasheq((blue . 3) (green . 5) (red . 10))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To parse a game, we do a similar kind of pattern matching to extract
the game id and the set of reveals to pass to &lt;code&gt;parse-set&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (parse-game line)
  (match-define (regexp #rx"Game ([^:]+): (.+)"
                        (list _ (app string-&amp;gt;number id) sets-str))
    line)
  (define sets
    (map parse-set (string-split sets-str ";")))
  (game id sets))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;With the id and parsed sets in hand, we construct an instance of the
game struct.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;&amp;gt; (parse-game "Game 5: 10 red, 3 blue; 10 green, 5 red")
(game 5 '(#hasheq((blue . 3) (red . 10))
          #hasheq((green . 10) (red . 5))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The goal of part one is to sum up the game ids where the games would be
valid. A valid game is defined as any game where every set of reveals
had fewer than 12 red cubes, 13 green cubes and 14 blue cubes. So, I
defined a generic procedure for determining if a game was valid:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (game-ok? g proc)
  (for/and ([s (in-list (game-sets g))])
    (proc s)))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;a href="https://docs.racket-lang.org/search/index.html?q=for/and"&gt;&lt;code&gt;for/and&lt;/code&gt;&lt;/a&gt; form returns &lt;code&gt;#t&lt;/code&gt; when all of the iterations are
truthy and &lt;code&gt;#f&lt;/code&gt; otherwise. Next, we define a procedure representing the
valid condition for part 1:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (part1-ok? s)
  (and
   (&amp;lt;= (hash-ref s 'red 0) 12)
   (&amp;lt;= (hash-ref s 'green 0) 13)
   (&amp;lt;= (hash-ref s 'blue 0) 14)))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And, with that, we can put everything together:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(call-with-input-file "day02.txt"
  (lambda (in)
    (for*/sum ([line (in-lines in)]
               [game (in-value (parse-game line))]
               #:when (game-ok? game part1-ok?))
      (game-id game))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We use the &lt;code&gt;sum&lt;/code&gt; variant of &lt;a href="https://docs.racket-lang.org/search/index.html?q=for*"&gt;&lt;code&gt;for*&lt;/code&gt;&lt;/a&gt; to sum up the valid game ids. The
&lt;a href="https://docs.racket-lang.org/search/index.html?q=in-value"&gt;&lt;code&gt;in-value&lt;/code&gt;&lt;/a&gt; form generates a sequence of one element representing
each game for each line and the body of the loop is skipped whenever
&lt;code&gt;game-ok?&lt;/code&gt; is false.&lt;/p&gt;&lt;p&gt;In part two, the problem is flipped on its head and we're asked to
find the minimum constraint that would make each game valid. That's
just a matter of iterating over every set in each game and keeping
track of the &lt;i&gt;maximum&lt;/i&gt; number of blocks of each color:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (game-minimums g)
  (for*/fold ([minimums (hasheq 'red 0 'green 0 'blue 0)])
             ([s (in-list (game-sets g))]
              [c (in-list '(red green blue))])
    (hash-update minimums c (λ (blocks) (max blocks (hash-ref s c 0))))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For example:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;&amp;gt; (game-minimums (parse-game "Game 5: 10 red, 3 blue; 10 green, 5 red"))
'#hasheq((blue . 3) (green . 10) (red . 10))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To compute the puzzle solution, we have to sum up the result of
multiplying the number of colors in every game (its "power"). So, we
define a procedure to compute a game's power:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (game-power g)
  (apply * (hash-values (game-minimums g))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Put it all together:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(call-with-input-file "day02.txt"
  (lambda (in)
    (for*/sum ([line (in-lines in)]
               [game (in-value (parse-game line))])
      (game-power game))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And that's it for day two!&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Advent of Racket 2023/01 - Trebuchet?!</title><link>https://defn.io/2023/12/01/advent-of-racket-2023-day-01</link><guid>https://defn.io/2023/12/01/advent-of-racket-2023-day-01</guid><pubDate>Fri, 1 Dec 2023 10:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;The 2023 &lt;a href="https://adventofcode.com"&gt;Advent of Code&lt;/a&gt; advent calendar has started and I'm doing it
in &lt;a href="https://racket-lang.org"&gt;Racket&lt;/a&gt; again this year. I'll probably stick with it for a couple
of weeks or until the puzzles start taking me more than 10-15 minutes
to finish. I've also decided to write some short posts about &lt;a href="https://github.com/Bogdanp/aoc2023"&gt;my
solutions&lt;/a&gt;, so here's the first one.&lt;/p&gt;&lt;p&gt;The first part of &lt;a href="https://adventofcode.com/2023/day/1"&gt;today&lt;/a&gt;'s challenge is to take a list of
strings, combine the first and last decimal digit in each string into a
decimal number and sum them all up. The example input looks like:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;1abc2
pqr3stu8vwx
a1b2c3d4e5f
treb7uchet
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In Racket, we can open a file as an input port using
&lt;a href="https://docs.racket-lang.org/search/index.html?q=call-with-input-file"&gt;&lt;code&gt;call-with-input-file&lt;/code&gt;&lt;/a&gt; and iterate over its lines using
&lt;a href="https://docs.racket-lang.org/search/index.html?q=in-lines"&gt;&lt;code&gt;in-lines&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(call-with-input-file "day01.txt"
 (lambda (in)
   (for/sum ([line (in-lines in)])
     ...)))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;a href="https://docs.racket-lang.org/search/index.html?q=for/sum"&gt;&lt;code&gt;for/sum&lt;/code&gt;&lt;/a&gt; variant of &lt;a href="https://docs.racket-lang.org/search/index.html?q=for"&gt;&lt;code&gt;for&lt;/code&gt;&lt;/a&gt; returns the sum of all
intermediate iteration results. With the above skeleton in place, all
we have to do is extract the first and last digit in every line and
convert them to a number (the so-called "calibration value" in the
problem statement).&lt;/p&gt;&lt;p&gt;We're going to need to convert characters to digits, so let's first
write a helper procedure to do that.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (get-digit s i)
  (define c (string-ref s i))
  (and (char-numeric? c)
       (- (char-&amp;gt;integer c)
          (char-&amp;gt;integer #\0))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;get-digit&lt;/code&gt; procedure takes a string and an index into that string,
grabs the character at the given index and converts it into a decimal
digit if it is numeric. We're only interested in the ASCII character set
for this particular problem, so there's no need to worry about other
unicode numerals.&lt;/p&gt;&lt;p&gt;Next, we can define the procedure to extract the calibration value
from a line.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (calibration-value s)
  (define-values (d0 d1)
    (for/fold ([d0 #f]
               [d1 #f])
              ([i (in-range 0 (string-length s))])
      (define digit (get-decimal-digit s i))
      (values (or d0 digit)
              (or digit d1))))
  (+ (* d0 10) d1))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It uses the &lt;a href="https://docs.racket-lang.org/search/index.html?q=for/fold"&gt;&lt;code&gt;for/fold&lt;/code&gt;&lt;/a&gt; form to iterate over all the indices in a
given string and keep track of the first and last-seen digits. Finally,
it combines the two digits into a decimal number.&lt;/p&gt;&lt;p&gt;We can plug the two helpers into our skeleton to solve part one:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(call-with-input-file "day01.txt"
  (lambda (in)
    (for/sum ([line (in-lines in)])
      (calibration-value line))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For part two, we get a new example and the puzzle gets a little bit
harder. We now need to account for digits that are spelled out on each
line. The example input looks like:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;two1nine
eightwothree
abcone2threexyz
xtwone3four
4nineeightseven2
zoneight234
7pqrstsixteen
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As in the first part, let's first write a helper to extract digits out
of a string.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define digit-rxs
  (for/list ([s (in-list '(zero one two three four five six seven eight nine))])
    (regexp (format "^~a" s))))

(define (get-spelled-out-digit s i)
  (or
   (for/first ([(rx n) (in-indexed (in-list digit-rxs))]
               #:when (regexp-match? rx s i))
     n)
   (get-decimal-digit s i)))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Like &lt;code&gt;get-decimal-digit&lt;/code&gt;, the &lt;code&gt;get-spelled-out-digit&lt;/code&gt; procedure takes
a string and a starting index into that string. It then iterates over
a list of regular expressions to return the index of the first match.
The &lt;a href="https://docs.racket-lang.org/search/index.html?q=regexp-match?"&gt;&lt;code&gt;regexp-match?&lt;/code&gt;&lt;/a&gt; procedure takes an optional starting index that
tells it where in the given string it should start matching from&lt;sup&gt;&lt;a href="#fn_1" id="fnref_1_1"&gt;1&lt;/a&gt;&lt;/sup&gt;. If
none of the regular expressions match, the &lt;a href="https://docs.racket-lang.org/search/index.html?q=for/first"&gt;&lt;code&gt;for/first&lt;/code&gt;&lt;/a&gt; form produces
&lt;code&gt;#f&lt;/code&gt; and we fall back to the &lt;code&gt;get-decimal-digit&lt;/code&gt; procedure.&lt;/p&gt;&lt;p&gt;Now, we can update &lt;code&gt;calibration-value&lt;/code&gt; to take its digit-extracting
procedure as an argument instead of calling &lt;code&gt;get-decimal-digit&lt;/code&gt;
directly.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (calibration-value s [get-digit get-decimal-digit])
  (define-values (d0 d1)
    (for/fold ([d0 #f]
               [d1 #f])
              ([i (in-range 0 (string-length s))])
      (define digit (get-digit s i))
      (values (or d0 digit)
              (or digit d1))))
  (+ (* d0 10) d1))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Since the &lt;code&gt;get-digit&lt;/code&gt; argument to &lt;code&gt;calibration-value&lt;/code&gt; defaults to
&lt;code&gt;get-decimal-digit&lt;/code&gt;, we don't need to change our solution to part one
to account for this refactoring. The solution to part two is the same
as part one, but we pass the &lt;code&gt;get-spelled-out-digit&lt;/code&gt; procedure to
&lt;code&gt;calibration-value&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(call-with-input-file "day01.txt"
  (lambda (in)
    (for/sum ([line (in-lines in)])
      (calibration-value line get-spelled-out-digit))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That's it for day one!&lt;/p&gt;&lt;section class="footnotes"&gt;&lt;ol&gt;&lt;li id="fn_1"&gt;&lt;p&gt;Why not just pass in a substring? I prefer to avoid copying
where possible and many Racket built-ins that work on sequences
support this pattern of passing in beginning and end indices. &lt;a class="footnote-backref" href="#fnref_1_1" title="Jump to reference" aria-label="Jump to reference"&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/section&gt;&lt;/article&gt;</description></item><item><title>Distributing Apps to the Mac App Store With GitHub Actions</title><link>https://defn.io/2023/10/22/distributing-mac-app-store-apps-with-github-actions</link><guid>https://defn.io/2023/10/22/distributing-mac-app-store-apps-with-github-actions</guid><pubDate>Sun, 22 Oct 2023 23:00:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;This weekend, I decided to try and get &lt;a href="https://franz.defn.io"&gt;Franz&lt;/a&gt; published on the Mac
App Store. Since I'm already &lt;a href="/2023/09/22/distributing-mac-apps-with-github-actions"&gt;using GitHub Actions to
build distributions for my customers&lt;/a&gt;, I figured I'd extend that same
workflow to handle build submissions to the Mac App Store.&lt;/p&gt;&lt;p&gt;Most of the steps are the same as for manual distribution, but some
details are different and documentation is sparse, so I wanted to jot down some notes.&lt;/p&gt;&lt;p&gt;You can find the workflow definition on &lt;a href="https://github.com/Bogdanp/Franz/blob/e9f88613a62b4dcf9486cba59e7297cd732dad93/.github/workflows/build_macos.yml"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;&lt;h2&gt;Apple Developer Certificates&lt;/h2&gt;&lt;p&gt;Mac App Store apps are distributed as &lt;code&gt;.pkg&lt;/code&gt; installers, so I generated
a "Mac Installer Distribution" certificate in Xcode and exported it. I
also needed an "Apple Distribution" certificate and a "Mac Development"
certificate. As in my previous post, these are stored as base64-encoded
secrets that are then added to a keychain during the workflow run.&lt;/p&gt;&lt;h2&gt;Provisioning Profile&lt;/h2&gt;&lt;p&gt;This mode of distribution requires a provisioning profile. I
tried generating a profile from the &lt;a href="https://developer.apple.com/account/resources/profiles/list"&gt;Certificates, Identifiers &amp;amp;
Profiles&lt;/a&gt; section of the Apple Developer site, but I couldn't
get Xcode on the runner to recognize the profile no matter what I tried.
So, I gave up and I copied the provisioning profile Xcode generated
from &lt;code&gt;~/Library/MobileDevice/Provisioning Profiles&lt;/code&gt; and added it as a
base64-encoded secret under GHA. The workflow writes the secret to the
provisioning profiles folder on the build machine:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-bash"&gt;mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
echo -n "$MAC_PROVISIONING_PROFILE" | \
  base64 --decode -o ~/Library/MobileDevice/Provisioning\ Profiles/franz.provisionprofile
&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Building the App&lt;/h2&gt;&lt;p&gt;To build the app, I run the &lt;code&gt;xcodebuild archive&lt;/code&gt; command:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-bash"&gt;xcodebuild \
  archive \
  -project FranzCocoa.xcodeproj/ \
  -scheme 'Franz MAS' \
  -destination 'generic/platform=macOS' \
  -archivePath dist/Franz.xcarchive
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And to generate the &lt;code&gt;.pkg&lt;/code&gt;, I run &lt;code&gt;xcodebuild&lt;/code&gt; with the &lt;code&gt;-exportArchive&lt;/code&gt;
flag:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-bash"&gt;xcodebuild \
  -exportArchive \
  -archivePath dist/Franz.xcarchive \
  -exportOptionsPlist FranzCocoa/MASExportOptions.plist \
  -exportPath dist/
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;exportOptionsPlist&lt;/code&gt; file has to have a &lt;code&gt;method&lt;/code&gt; key whose value is
&lt;code&gt;app-store&lt;/code&gt;. When this &lt;code&gt;method&lt;/code&gt; is specified, &lt;code&gt;xcodebuild&lt;/code&gt; implicitly
exports a &lt;code&gt;.pkg&lt;/code&gt; instead of an &lt;code&gt;.app&lt;/code&gt; directory. All other options are
the same as in the developer ID method.&lt;/p&gt;&lt;p&gt;Finally, to upload the build to App Store Connect, I use &lt;code&gt;altool&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-bash"&gt;xcrun altool \
  --upload-package dist/Franz.pkg \
  --type macos \
  --asc-public-id '69a6de7a-5947-47e3-e053-5b8c7c11a4d1' \
  --apple-id '6470144907' \
  --bundle-id 'io.defn.Franz' \
  --bundle-short-version-string "$(/usr/libexec/PlistBuddy -c 'Print ApplicationProperties:CFBundleShortVersionString' dist/Franz.xcarchive/Info.plist)" \
  --bundle-version "$(/usr/libexec/PlistBuddy -c 'Print ApplicationProperties:CFBundleVersion' dist/Franz.xcarchive/Info.plist)" \
  --username 'bogdan@defn.io' \
  --password "$APPLE_ID_PASSWORD"
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To find my public ID, I had to run &lt;code&gt;xcrun altool --list-providers&lt;/code&gt;.
To get an Apple ID for my app, I had to first create it in App Store
Connect. The ID can be found under "General" -&amp;gt; "App Information".&lt;/p&gt;&lt;h2&gt;Release Process&lt;/h2&gt;&lt;p&gt;Since every submitted build must have a unique build number, I made this
part of the workflow optional. It only runs when the workflow is run
manually and the &lt;code&gt;masBuild&lt;/code&gt; flag is set. So, my release process for the
Mac App Store is:&lt;/p&gt;&lt;ol class="tight" start="1"&gt;&lt;li&gt;Bump the build number.&lt;/li&gt;&lt;li&gt;Run the workflow manually.&lt;/li&gt;&lt;li&gt;Go to App Store Connect and publish the new version.&lt;/li&gt;&lt;/ol&gt;&lt;/article&gt;</description></item><item><title>Franz for Windows</title><link>https://defn.io/2023/10/15/ann-franz-for-windows</link><guid>https://defn.io/2023/10/15/ann-franz-for-windows</guid><pubDate>Sun, 15 Oct 2023 10:00:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;Today marks the first beta release of &lt;a href="https://franz.defn.io"&gt;Franz&lt;/a&gt; for Windows!&lt;/p&gt;&lt;p&gt;&lt;figure&gt;&lt;img alt="A screenshot of a Franz for Windows workspace." src="/img/Franz_for_Windows.png"/&gt;&lt;figcaption&gt;Franz is a desktop client for Apache Kafka.&lt;/figcaption&gt;&lt;/figure&gt;&lt;/p&gt;&lt;p&gt;I had been planning to work on a Windows version of Franz since last
year. Originally, I was going to implement the UI in C# (or possibly F#
after a strong recommendation from Michael Sperber), but then &lt;a href="https://benknoble.github.io/"&gt;Ben&lt;/a&gt; and
I wrote &lt;a href="/papers/fungui-funarch23.pdf"&gt;a paper&lt;/a&gt; &lt;sup&gt;&lt;a href="#fn_1" id="fnref_1_1"&gt;1&lt;/a&gt;&lt;/sup&gt; about my declarative wrapper around &lt;code&gt;racket/gui&lt;/code&gt;
earlier this year and that inspired me to try using it to implement the
Windows (and, soon, Linux!) version.&lt;/p&gt;&lt;p&gt;On macOS, Franz is &lt;a href="/2022/11/20/ann-franz"&gt;implemented&lt;/a&gt; using a
combination of Racket for the core logic and Swift for the UI. The
Windows version reuses the core and implements all the app's views
afresh using &lt;a href="https://docs.racket-lang.org/gui-easy/index.html"&gt;gui-easy&lt;/a&gt;. The end result is about 5k lines of GUI code,
including many small component "tests" that let me easily exercise views
in isolation (think SwiftUI Previews, but, well, functional&lt;sup&gt;&lt;a href="#fn_2" id="fnref_2_1"&gt;2&lt;/a&gt;&lt;/sup&gt; and
interactive). For comparison, the Swift version has about 8.2k lines
of Swift code — a mix of AppKit and SwiftUI — and about 4.5k of XML
representing XIBs built using the Xcode Interface Builder. Being that
&lt;code&gt;racket/gui&lt;/code&gt; is a cross-platform library, and therefore it has to target
the least common denominator of the three platforms it supports in terms
of features, the end result isn't as polished as the Mac version or as a
Windows Forms&lt;sup&gt;&lt;a href="#fn_3" id="fnref_3_1"&gt;3&lt;/a&gt;&lt;/sup&gt; version could be, but it gets reasonably close.&lt;/p&gt;&lt;p&gt;Since the source is all Racket, distribution is trivial: just call &lt;code&gt;raco exe&lt;/code&gt; to generate an executable and then &lt;code&gt;raco dist&lt;/code&gt; to package it up.
The only snag has been Microsoft's SmartScreen. I'm not willing to pay
for an EV certificate&lt;sup&gt;&lt;a href="#fn_4" id="fnref_4_1"&gt;4&lt;/a&gt;&lt;/sup&gt; from one of the blessed vendors, so I need to
manually submit new Windows releases to Microsoft to check for malware,
but that hasn't been a huge hassle so far, apart from the processing
times.&lt;/p&gt;&lt;p&gt;In &lt;a href="/2022/11/20/ann-franz"&gt;my original announcement post&lt;/a&gt; for Franz I
had mentioned that I think this kind of separation between backend and
frontend is a very productive way of developing desktop apps and I think
that bears out here. I only had to make minimal changes to the core code
in order to support this new version and I was able to get everything
done in about two months' worth of working in my free time. I think a
small team using this model can move very fast and build great native
apps for every platform.&lt;/p&gt;&lt;p&gt;Franz is &lt;a href="/2023/08/10/ann-franz-source-available"&gt;source available&lt;/a&gt; so you can
take a peek at &lt;a href="https://github.com/Bogdanp/Franz/tree/master/FranzCross"&gt;the implementation&lt;/a&gt; yourself if you're interested.
If you use Franz for your work, I'd love to get your feedback!&lt;/p&gt;&lt;section class="footnotes"&gt;&lt;ol&gt;&lt;li id="fn_1"&gt;&lt;p&gt;See also &lt;a href="https://www.youtube.com/watch?v=2YAMKwQf3NA&amp;amp;t=9160s"&gt;Ben's talk at ICFP&lt;/a&gt;. &lt;a class="footnote-backref" href="#fnref_1_1" title="Jump to reference" aria-label="Jump to reference"&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li id="fn_2"&gt;&lt;p&gt;As in "working," not the programming paradigm. &lt;a class="footnote-backref" href="#fnref_2_1" title="Jump to reference" aria-label="Jump to reference"&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li id="fn_3"&gt;&lt;p&gt;Or whatever the current Windows GUI framework &lt;i&gt;du jour&lt;/i&gt; is. &lt;a class="footnote-backref" href="#fnref_3_1" title="Jump to reference" aria-label="Jump to reference"&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li id="fn_4"&gt;&lt;p&gt;What a &lt;i&gt;racket&lt;/i&gt;! &lt;a class="footnote-backref" href="#fnref_4_1" title="Jump to reference" aria-label="Jump to reference"&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/section&gt;&lt;/article&gt;</description></item><item><title>Powered by •</title><link>https://defn.io/2023/09/25/punct</link><guid>https://defn.io/2023/09/25/punct</guid><pubDate>Mon, 25 Sep 2023 20:30:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;Following in the long tradition of programmers writing blog engines
instead of blogging, this site is now powered by &lt;a href="https://joeldueck.com/"&gt;Joel Dueck&lt;/a&gt;'s &lt;a href="https://joeldueck.com/what-about/punct/"&gt;Punct&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;I'd been meaning to make the switch ever since Joel posted &lt;a href="https://www.youtube.com/watch?v=9zxna1tlvHU"&gt;a video
of Punct in Practice&lt;/a&gt; to the Racket Discord and I finally took the
plunge this weekend. You can find the source code for the new site &lt;a href="https://github.com/Bogdanp/defn.io"&gt;on
GitHub&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;The &lt;a href="https://github.com/Bogdanp/personal-website"&gt;old site&lt;/a&gt; was implemented using &lt;a href="https://gohugo.io/"&gt;Hugo&lt;/a&gt; and it served me well,
but I never learned how to use Hugo properly because I could never make
my way effectively around their documentation, so I was always a bit
wary of making changes to the structure of the site (for example, making
my own theme and other kinds of enhancements).&lt;/p&gt;&lt;p&gt;Punct is similar to other Racket publishing tools like &lt;a href="https://docs.racket-lang.org/pollen/"&gt;Pollen&lt;/a&gt; and
&lt;a href="https://docs.racket-lang.org/scribble/index.html"&gt;Scribble&lt;/a&gt; in that documents (i.e. pages) are just Racket modules with a
reader that makes it convenient to input regular markup, but that lets
you escape to and use inline Racket when necessary. This means that, at
any point in a document, you can fall back on the full power of Racket.
It's hard to overstate how nice this model is so I highly encourage you
to check out Joel's video and see for yourself.&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Restoring the Old Dashboard Feed on GitHub</title><link>https://defn.io/2023/09/23/restoring-the-github-feed</link><guid>https://defn.io/2023/09/23/restoring-the-github-feed</guid><pubDate>Sat, 23 Sep 2023 13:00:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;A couple weeks ago, GitHub changed its Dashboard feed implementation and
this new version has a lot less relevant information with respect to the
repositories I follow compared to the old one. Fortunately, the old feed
is still available at &lt;code&gt;/dashboard-feed&lt;/code&gt;. So, for now, you can restore
the old functionality using this quick and dirty user script:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-javascript"&gt;document.addEventListener("DOMContentLoaded", () =&amp;gt; {
  const $news = document.querySelector("#dashboard .news");
  if (!$news) return;
  let node = $news.firstChild;
  while (node !== null) {
    $news.removeChild(node);
    node = $news.firstChild;
  }

  const req = new XMLHttpRequest();
  req.open("GET", "/dashboard-feed", true);
  req.addEventListener("load", (e) =&amp;gt; {
    let $frame = document.createElement("iframe");
    $frame.style.display = "none";
    $frame.onload = () =&amp;gt; {
      $news.appendChild($frame.contentDocument.querySelector(".application-main"));
    };
    $frame.srcdoc = req.responseText;
    $news.appendChild($frame);
  });
  req.send(null);
})
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In &lt;a href="https://arc.net/"&gt;Arc&lt;/a&gt;, you can enable this script by going to github.com, creating
a new boost and placing the script in the boost's code section.  In
Firefox, you should be able to use a plugin like Tampermonkey to
achieve the same thing.&lt;/p&gt;&lt;p&gt;Hopefully, they'll keep the old feed around for a while, or bring the
new feed's functionality up to par with the old one in future.&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Distributing Mac Apps With GitHub Actions</title><link>https://defn.io/2023/09/22/distributing-mac-apps-with-github-actions</link><guid>https://defn.io/2023/09/22/distributing-mac-apps-with-github-actions</guid><pubDate>Fri, 22 Sep 2023 21:00:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;This week, I spent some time automating the build &amp;amp; distribution process
for &lt;a href="https://franz.defn.io"&gt;Franz&lt;/a&gt; and I wanted to jot down some quick notes about how it
works. Most of the steps are not specific to GitHub Actions so you could
replace it by your favorite CI.&lt;/p&gt;&lt;h2&gt;The Workflow&lt;/h2&gt;&lt;p&gt;Take a look at &lt;a href="https://github.com/Bogdanp/Franz/blob/388edfd7238839af52ecda9ad8554fba7e462db5/.github/workflows/build.yml#L105-L196"&gt;the workflow&lt;/a&gt; to follow along.&lt;/p&gt;&lt;p&gt;The &lt;code&gt;build_core_arm64&lt;/code&gt; and &lt;code&gt;build_core_x86_64&lt;/code&gt; jobs are concerned
with building the Racket core of the application and are relatively
uninteresting: install Racket, install the core dependencies, and
compile an object file with the core implementation. Finally, upload the
core objects and supporting files for use in &lt;code&gt;build_app&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;The &lt;code&gt;build_app&lt;/code&gt; job first downloads the core objects and installs a
Swift package the application depends on, then proceeds to build the
app, create a disk image containing the app, notarize the image, and,
finally, save the notarized &lt;code&gt;.dmg&lt;/code&gt;.&lt;/p&gt;&lt;h2&gt;Apple Developer Certificates&lt;/h2&gt;&lt;p&gt;This part is based on GitHub's own documentation for "&lt;a href="https://docs.github.com/en/actions/deployment/deploying-xcode-applications/installing-an-apple-certificate-on-macos-runners-for-xcode-development"&gt;Installing an
Apple certificate on macOS runners for Xcode development&lt;/a&gt;", though I
found I didn't need to export a provisioning profile and could just rely
on Xcode to automatically handle that for me.&lt;/p&gt;&lt;p&gt;I distribute the app using my Apple Developer ID (i.e. folks download a
&lt;code&gt;.dmg&lt;/code&gt; file directly from my website, not via the Mac App Store), so I
had to generate a couple certificates to use with the workflow. I did
this directly from Xcode by going to "Settings" -&amp;gt; "Accounts" -&amp;gt; "Manage
Certificates...".&lt;/p&gt;&lt;p&gt;&lt;img alt="The Xcode account settings pane." src="/img/franz-gha/01-xcode-accounts.png"/&gt;&lt;/p&gt;&lt;p&gt;I created a new "Apple Development Certificate" and a new "Developer ID
Application Certificate", then exported both to disk and assigned each a
strong password.&lt;/p&gt;&lt;p&gt;&lt;img alt="The Xcode certificates pane with an export menu." src="/img/franz-gha/02-xcode-export.png"/&gt;&lt;/p&gt;&lt;p&gt;I converted the certificates to base64 and stored them as GitHub Secrets
under my repository's settings. To make the certificates available
to Xcode during workflow runs, I create a keychain and import the
certificates into it:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-shell"&gt;MAC_DEV_CER_PATH=$RUNNER_TEMP/madev.p12
DEVELOPER_ID_CER_PATH=$RUNNER_TEMP/devid.p12
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
echo -n "$MAC_DEV_CER" | base64 --decode -o $MAC_DEV_CER_PATH
echo -n "$DEVELOPER_ID_CER" | base64 --decode -o $DEVELOPER_ID_CER_PATH
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security import $MAC_DEV_CER_PATH -P "$MAC_DEV_CER_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
security import $DEVELOPER_ID_CER_PATH -P "$DEVELOPER_ID_CER_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
security list-keychain -d user -s $KEYCHAIN_PATH
&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Building the App&lt;/h2&gt;&lt;p&gt;To build the app, I first run &lt;code&gt;xcodebuild&lt;/code&gt; to generate an &lt;code&gt;.Xcarchive&lt;/code&gt;
of the app's compiled objects and runtime support files. Just run the
&lt;code&gt;archive&lt;/code&gt; subcommand with the Xcode scheme to build and input and output
paths:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-shell"&gt;xcodebuild \
  archive \
  -project FranzCocoa.xcodeproj/ \
  -scheme Franz \
  -destination 'generic/platform=macOS' \
  -archivePath dist/Franz.xcarchive
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next, I run &lt;code&gt;xcodebuild&lt;/code&gt; again to export the archive to an &lt;code&gt;.app&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-shell"&gt;xcodebuild \
  -exportArchive \
  -archivePath dist/Franz.xcarchive \
  -exportOptionsPlist FranzCocoa/ExportOptions.plist \
  -exportPath dist/ \
  -allowProvisioningUpdates
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Figuring out the contents of the &lt;code&gt;ExportOptions.plist&lt;/code&gt; file was a bit
tricky. The set of available options is printed at the end of the output
for &lt;code&gt;xcodebuild -help&lt;/code&gt;. The right combination of options for my app
turned out to be:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-xml-property-list"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;
&amp;lt;!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"&amp;gt;
&amp;lt;plist version="1.0"&amp;gt;
  &amp;lt;dict&amp;gt;
    &amp;lt;key&amp;gt;compileBitcode&amp;lt;/key&amp;gt;
    &amp;lt;false/&amp;gt;
    &amp;lt;key&amp;gt;method&amp;lt;/key&amp;gt;
    &amp;lt;string&amp;gt;developer-id&amp;lt;/string&amp;gt;
    &amp;lt;key&amp;gt;signingStyle&amp;lt;/key&amp;gt;
    &amp;lt;string&amp;gt;automatic&amp;lt;/string&amp;gt;
    &amp;lt;key&amp;gt;stripSwiftSymbols&amp;lt;/key&amp;gt;
    &amp;lt;true/&amp;gt;
    &amp;lt;key&amp;gt;teamID&amp;lt;/key&amp;gt;
    &amp;lt;string&amp;gt;H3YE679B58&amp;lt;/string&amp;gt;
    &amp;lt;key&amp;gt;thinning&amp;lt;/key&amp;gt;
    &amp;lt;string&amp;gt;&amp;amp;lt;none&amp;amp;gt;&amp;lt;/string&amp;gt;
  &amp;lt;/dict&amp;gt;
&amp;lt;/plist&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Once the app is exported, I use &lt;a href="https://github.com/sindresorhus/create-dmg"&gt;create-dmg&lt;/a&gt; to create a nice-looking
disk image to distribute it with and then proceed to notarization.
I considered building up the image manually using &lt;code&gt;hdiutil&lt;/code&gt;, but
generating output as nice as what &lt;code&gt;create-dmg&lt;/code&gt; produces is relatively
hard (and involves, for example, editing &lt;code&gt;.DS_Store&lt;/code&gt; files), so
&lt;code&gt;create-dmg&lt;/code&gt; it is.&lt;/p&gt;&lt;h3&gt;Notarizing the App&lt;/h3&gt;&lt;p&gt;To notarize the app, I use Xcode's &lt;code&gt;notarytool&lt;/code&gt; utility:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-shell"&gt;xcrun notarytool submit \
  --team-id 'H3YE679B58' \
  --apple-id 'bogdan@defn.io' \
  --password "$NOTARY_PASSWORD" \
  --wait \
  dist/Franz.dmg
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In order to make notarization requests from within the workflow, I had
to create an app-specific password using the &lt;a href="https://appleid.apple.com/"&gt;Apple ID website&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Once notarization succeeds, I run the &lt;code&gt;stapler&lt;/code&gt; utility to staple the
notarization onto the disk image:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-shell"&gt;xcrun stapler staple dist/Franz.dmg
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And that's it. The final step after this is just to upload the image
artifact so I can grab it and manually&lt;sup&gt;&lt;a href="#fn_1" id="fnref_1_1"&gt;1&lt;/a&gt;&lt;/sup&gt; release it when I'm ready.&lt;/p&gt;&lt;section class="footnotes"&gt;&lt;ol&gt;&lt;li id="fn_1"&gt;&lt;p&gt;A process I'll automate some other time. &lt;a class="footnote-backref" href="#fnref_1_1" title="Jump to reference" aria-label="Jump to reference"&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/section&gt;&lt;/article&gt;</description></item><item><title>Franz now Source Available</title><link>https://defn.io/2023/08/10/ann-franz-source-available</link><guid>https://defn.io/2023/08/10/ann-franz-source-available</guid><pubDate>Thu, 10 Aug 2023 08:20:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;The source code for &lt;a href="https://franz.defn.io"&gt;Franz&lt;/a&gt;, a desktop client for &lt;a href="https://kafka.apache.org"&gt;Apache Kafka&lt;/a&gt;, is now
available for all to read on &lt;a href="https://github.com/Bogdanp/Franz"&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;One of my goals with writing software in &lt;a href="https://racket-lang.org"&gt;Racket&lt;/a&gt; is to help expand the
Racket ecosystem. I try do that by making parts of the apps I write
Open Source where possible&lt;sup&gt;&lt;a href="#fn_1" id="fnref_1_1"&gt;1&lt;/a&gt;&lt;/sup&gt; and by making small contributions back
to Racket itself. Additionally, I &lt;a href="https://racket.discourse.group/t/real-racket-applications-with-correct-idiomatic-style/1504"&gt;sometimes&lt;/a&gt; see new users
ask for examples of real-world applications built with Racket so I've
been trying to make the source code of my own apps available as well
(where possible; see also &lt;a href="https://github.com/Bogdanp/nemea"&gt;Nemea&lt;/a&gt; and &lt;a href="https://github.com/Bogdanp/Remember"&gt;Remember&lt;/a&gt;). My hope is that folks
interested in using Racket can get an idea of what using it in practice
looks like by viewing the code for these apps and I like the thought
of letting my own users see what code they're running when they use my
apps.&lt;/p&gt;&lt;section class="footnotes"&gt;&lt;ol&gt;&lt;li id="fn_1"&gt;&lt;p&gt;In the process of writing Franz, I've written a &lt;a href="https://github.com/Bogdanp/racket-kafka"&gt;Kafka client&lt;/a&gt;,
libraries for serializing and deserializing &lt;a href="https://github.com/Bogdanp/racket-avro"&gt;Avro&lt;/a&gt;, &lt;a href="https://github.com/Bogdanp/racket-messagepack"&gt;MessagePack&lt;/a&gt;
and &lt;a href="https://github.com/Bogdanp/racket-protocol-buffers"&gt;Protocol Buffer&lt;/a&gt; data, native decompressors for &lt;a href="https://github.com/Bogdanp/racket-lz4"&gt;LZ4&lt;/a&gt; and
&lt;a href="https://github.com/Bogdanp/racket-snappy"&gt;Snappy&lt;/a&gt;, and a hash-lang for &lt;a href="https://github.com/Bogdanp/racket-lua"&gt;Lua&lt;/a&gt; among &lt;a href="https://github.com/Bogdanp/noise"&gt;other things&lt;/a&gt;. &lt;a class="footnote-backref" href="#fnref_1_1" title="Jump to reference" aria-label="Jump to reference"&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/section&gt;&lt;/article&gt;</description></item><item><title>Deploying Racket Web Apps With Docker</title><link>https://defn.io/2023/05/19/racket-deployment-with-docker</link><guid>https://defn.io/2023/05/19/racket-deployment-with-docker</guid><pubDate>Fri, 19 May 2023 07:00:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;Since there was &lt;a href="https://www.reddit.com/r/Racket/comments/13lft3d/how_do_you_deploy_your_racket_code_to_a_server/"&gt;a question about deploying Racket code using
Docker&lt;/a&gt; on the Racket Reddit this morning, I figured I'd write a
quick follow-up to my post about &lt;a href="/2020/06/28/racket-deployment/"&gt;Deploying Racket Web Apps&lt;/a&gt;.
My preference is still to avoid using Docker and just use the method
described in that post by default. But, if I have to use Docker for
some reason, then I'll typically use a two-stage build to build a
distribution in the first stage and then copy that distribution into the
second stage in order to get a minimal Docker image that I can easily
ship around.&lt;/p&gt;&lt;p&gt;Given the following app, saved as &lt;code&gt;app.rkt&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;#lang racket/base

(require racket/async-channel
         web-server/http
         web-server/servlet-dispatch
         web-server/web-server)

(define ch (make-async-channel))
(define stop
  (serve
   #:dispatch (dispatch/servlet
               (lambda (_req)
                 (response/xexpr
                  '(h1 "Hello!"))))
   #:port 8000
   #:listen-ip "0.0.0.0"
   #:confirmation-channel ch))

(define ready-or-exn (sync ch))
(when (exn:fail? ready-or-exn)
  (raise ready-or-exn))

(with-handlers ([exn:break? void])
  (sync/enable-break never-evt))

(stop)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I would write the following &lt;code&gt;Dockerfile&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-Dockerfile"&gt;FROM racket/racket:8.9-full AS build

COPY app.rkt /code/
WORKDIR /code
RUN raco exe -o app app.rkt
RUN raco dist dist app

FROM debian:bullseye-slim AS final

RUN apt-get update -y &amp;amp;&amp;amp; \
    apt-get install -y --no-install-recommends dumb-init &amp;amp;&amp;amp; \
    rm -rf /var/lib/apt/lists/*

COPY --from=build /code/dist /app
CMD ["dumb-init", "/app/bin/app"]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When this image gets built, the &lt;code&gt;build&lt;/code&gt; stage creates a distribution
of the app that gets copied into the &lt;code&gt;final&lt;/code&gt; stage. At the end, the
build stage is discarded and the end result is a roughly 150MB Docker
image with just my code in it and a minimal Debian system. Not quite
as minimal as you can get out of using a similar method with Go, but
Racket distributions have a high baseline, so a real app wouldn't be
much bigger than this.&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Breaking Racket</title><link>https://defn.io/2023/03/31/breaking-racket</link><guid>https://defn.io/2023/03/31/breaking-racket</guid><pubDate>Fri, 31 Mar 2023 15:00:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;If you've looked at some of the concurrency and networking procedures
available in Racket, you might've noticed a bunch that follow the
naming pattern &lt;code&gt;&amp;lt;name&amp;gt;/enable-break&lt;/code&gt; and it might not have been
immediately obvious why or when you would use these variants of these
procedures over the regular ones.  Let's look at a snippet from the
documentation of &lt;code&gt;tcp-accept/enable-break&lt;/code&gt;:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;If breaking is disabled when &lt;code&gt;tcp-accept/enable-break&lt;/code&gt; is called,
then either ports are returned or the &lt;code&gt;exn:break&lt;/code&gt; exception is
raised, but not both.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;So, the procedure guarantees that either it will accept a connection
and return a pair of ports, or it will raise a break exception, if
breaking is disabled when it is called.  Sounds straightforward
enough, but it still might not be clear where this would come in
handy.  The part of the quote about "if breaking is disabled" is an
important hint.  Consider this simplified piece of code based on my
previous post re. the protohackers challenge:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define listener (tcp-listen 8000))
(with-handlers ([exn:break? void])
  (let loop ()
    (parameterize-break #f
      (define-values (in out)
        (tcp-accept listener))                     ;; •1
      (define conn-thd
        (thread (make-connection-handler in out))) ;; •2
      (thread (make-supervisor conn-thd in out)))  ;; •3
    (loop)))
(tcp-close listener)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We set up a listener, then enter a loop to start accepting new
connections and spawn threads to handle every connection.  For every
connection-handling thread, we spawn a supervising thread to clean up
resources once the handling thread is done.&lt;/p&gt;&lt;p&gt;We want to ensure that a handling thread gets spawned for every accepted
connection, so we want to guarantee that no breaks can be received
between •1 and •2. Likewise, we want to guarantee
that every handler thread has an associated supervision thread, so no
breaks should be allowed between •2 and •3 either.
So, we've wrapped •1, •2, and •3 in the
&lt;code&gt;parameterize-break&lt;/code&gt; form to disable breaks. However, accepting a
new connection will block the thread until a client actually tries
connecting to the server. This means that we can only stop this server
if we happen to send it a break at exactly the time after •3
returns, or by killing the process.&lt;/p&gt;&lt;p&gt;By replacing the call to &lt;code&gt;tcp-accept&lt;/code&gt; with &lt;code&gt;tcp-accept/enable-break&lt;/code&gt;,
we can preserve the same guarantees as before while also allowing
breaks to be raised before new connections are accepted.&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Protohackers Challenge in Racket Part 2</title><link>https://defn.io/2023/03/26/protohackers-part-2</link><guid>https://defn.io/2023/03/26/protohackers-part-2</guid><pubDate>Sun, 26 Mar 2023 07:15:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;I'm currently stuck waiting for a flight, so I figured I'd pick up
where I left off &lt;a href="/2023/03/25/protohackers-part-1/"&gt;yesterday&lt;/a&gt; &lt;sup&gt;&lt;a href="#fn_1" id="fnref_1_1"&gt;1&lt;/a&gt;&lt;/sup&gt; and implement a couple more of the
&lt;a href="https://protohackers.com/"&gt;Protohackers&lt;/a&gt; challenges.&lt;/p&gt;&lt;h2&gt;&lt;code&gt;run-server&lt;/code&gt;&lt;/h2&gt;&lt;p&gt;Since the accept loop was common between all the challenges so far, I
decided to extract and reuse it:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (run-server host port handle
                    #:backlog [backlog 511]
                    #:reuse? [reuse? #t]
                    #:timeout-evt-proc [make-timeout-evt values]
                    #:listen-proc [listen tcp-listen]
                    #:accept-proc [accept tcp-accept/enable-break]
                    #:close-proc [close tcp-close])
  (define listener
    (listen port backlog reuse? host))
  (define server-custodian
    (make-custodian))
  (define server-thd
    (parameterize ([current-custodian server-custodian])
      (thread
       (lambda ()
         (with-handlers ([exn:break? void])
           (let loop ()
             (parameterize-break #f
               (define-values (in out)
                 (accept listener))
               (define client-custodian
                 (make-custodian))
               (define client-thd
                 (thread
                  (lambda ()
                    (break-enabled #t)
                    (parameterize ([current-custodian client-custodian])
                      (handle in out)))))
               (thread
                (lambda ()
                  (sync (make-timeout-evt client-thd))
                  (close-output-port out)
                  (close-input-port in)
                  (custodian-shutdown-all client-custodian))))
             (loop)))
         (close listener)))))
  (lambda ()
    (break-thread server-thd)
    (thread-wait server-thd)
    (custodian-shutdown-all server-custodian)))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This version abstracts over the TCP procedures (you'll see why in a
bit), runs the accept loop in a thread and returns a procedure that
can be used to stop the server.  For convenience, I also wrote a
version that blocks the calling thread until a break is received:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define run-server*
  (make-keyword-procedure
   (lambda (kws kw-args . args)
     (define stop (keyword-apply run-server kws kw-args args))
     (with-handlers ([exn:break? void])
       (sync never-evt))
     (stop))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;With this in place, the 0th challenge from yesterday now looks like
this:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;#lang racket/base

(require racket/match)

(define (handle in out)
  (let loop ()
    (match (read-bytes 4096 in)
      [(? eof-object?)
       (void)]
      [bs
       (write-bytes bs out)
       (loop)])))

(module+ main
  (require "common.rkt")
  (run-server* "0.0.0.0" 8111 handle))
&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;4: Unusual Database Program&lt;/h2&gt;&lt;p&gt;This challenge switches things up and uses UDP instead of TCP.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;#lang racket/base

(require racket/match
         racket/port)

(define db
  (make-hash `(("version" . "Ken's Key-Value Store 1.0"))))

(define (handle in out)
  (match (port-&amp;gt;string in)
    [(regexp #rx"^([^=]*)=(.*)$" (list _ key value))
     (unless (equal? key "version")
       (hash-set! db key value))]
    [key
     (define value (hash-ref db key #f))
     (display key out)
     (when value
       (display "=" out)
       (display value out))]))

(module+ main
  (require racket/udp
           "common.rkt")

  (define (udp-listen port _backlog reuse? host)
    (define socket (udp-open-socket))
    (begin0 socket
      (udp-bind! socket host port reuse?)))

  (define (udp-accept listener)
    (define buf (make-bytes 65536))
    (parameterize-break #f
      (define-values (len hostname port)
        (udp-receive!/enable-break listener buf))
      (define client-in (open-input-bytes (subbytes buf 0 len)))
      (define-values (pipe-in client-out)
        (make-pipe))
      (thread
       (lambda ()
         (let loop ()
           (define len (read-bytes! buf pipe-in))
           (unless (eof-object? len)
             (udp-send-to listener hostname port buf 0 len)
             (loop)))))
      (values client-in client-out)))

  (run-server* "0.0.0.0" 8111 handle
               #:listen-proc udp-listen
               #:accept-proc udp-accept
               #:close-proc udp-close))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Since &lt;code&gt;run-server&lt;/code&gt; now abstracts over the &lt;code&gt;listen&lt;/code&gt;, &lt;code&gt;accept&lt;/code&gt;, and
&lt;code&gt;close&lt;/code&gt; procedures, we can massage the UDP API to fit into the
&lt;code&gt;run-server&lt;/code&gt; model.  To listen, we bind a socket to the given host and
port.  To accept, we receive a packet, wrap the data into an input
port and use a pipe to pump responses back to the client through the
same UDP socket.&lt;/p&gt;&lt;p&gt;The &lt;code&gt;handle&lt;/code&gt; procedure is straightforward: it uses a shared hash to
store and retrieve the entries.  Hashes are thread-safe so it's fine
to share the same hash between clients given that we don't have to
worry about data races for this particular problem.&lt;/p&gt;&lt;h2&gt;5: Mob in the Middle&lt;/h2&gt;&lt;p&gt;Back to TCP, this time implementing a MITM attack.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;#lang racket/base

(require racket/tcp)

(define (handle in out)
  (define-values (proxied-in proxied-out)
    (tcp-connect "chat.protohackers.com" 16963))
  (thread
   (lambda ()
     (pump proxied-in out)))
  (pump in proxied-out))

(define (pump in out)
  (define buf (make-bytes 4096))
  (let loop ([data #""])
    (define len
      (read-bytes-avail! buf in))
    (cond
      [(eof-object? len)
       (write-bytes (rewrite data) out)
       (flush-output out)]
      [else
       (loop (drain (bytes-append data (subbytes buf 0 len)) out))])))

(define (drain data out)
  (let loop ([data data])
    (cond
      [(bytes-index-of data 10)
       =&amp;gt; (λ (idx)
            (write-bytes (rewrite (subbytes data 0 idx)) out)
            (write-byte 10 out)
            (flush-output out)
            (loop (subbytes data (add1 idx))))]
      [else data])))

(define (rewrite data)
  (regexp-replace*
   #px#"(.?)(7[a-zA-Z0-9]{25,34})(.?)" data
   (λ (bs pre _addr post)
     (cond
       [(and (member pre '(#"" #" "))
             (member post '(#"" #" ")))
        (bytes-append pre #"7YWHMfk9JZe0LM0g1ZauHuiSxhI" post)]
       [else bs]))))

(define (bytes-index-of bs b)
  (for/first ([o (in-bytes bs)]
              [i (in-naturals)]
              #:when (= o b))
    i))

(module+ main
  (require "common.rkt")
  (run-server* "0.0.0.0" 8111 handle))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is the first challenge where we act as a TCP client.  For every
connection, we make our own connection to the upstream chat server.
Then, we pipe all output from the chat server to the client, rewriting
any lines containing cryptocurrency addresses.  We do the same for
input from the client to the chat server.&lt;/p&gt;&lt;p&gt;The tricky part about this challenge is that we can't just use
&lt;code&gt;read-line&lt;/code&gt; because the client might terminate the connection in the
middle of writing a line and &lt;code&gt;read-line&lt;/code&gt; makes no distinction between
the end of input and the end of a line.  So, we pump input from one
side to the other in 4k chunks, searching for newlines after every
chunk and rewriting complete lines.&lt;/p&gt;&lt;h2&gt;6: Speed Daemon&lt;/h2&gt;&lt;p&gt;This challenge cranks up the difficulty, so I'll split the solution
into sections below.&lt;/p&gt;&lt;h3&gt;Common Bits&lt;/h3&gt;&lt;p&gt;The imports and a logger are shared between the three sections.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;#lang racket/base

(require (for-syntax racket/base
                     racket/syntax
                     syntax/parse)
         racket/match
         racket/math
         racket/random
         threading
         (prefix-in proto: "006.bnf"))

(define-logger protohackers)
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;Message Parsing&lt;/h3&gt;&lt;p&gt;Once again, I've opted to use &lt;a href="https://docs.racket-lang.org/binfmt-manual/index.html"&gt;binfmt&lt;/a&gt; to handle the message parsing,
but this time around I've decided to parse the data into actual
structs.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define *message-readers*
  (make-hasheqv))

(define-values (prop:message-writer _prop:message-writer? message-writer)
  (make-struct-type-property 'message-writer))

(define-syntax (define-message stx)
  (syntax-parse stx
    [(_ message-id:id
        ([field-id:id field-sym:id] ...)
        #:tag tag-number:expr
        {~alt
         {~optional {~seq #:parser parser-proc}}
         {~optional {~seq #:unparser unparser-proc}}} ...)
     #:with (field-accessor-id ...) (for/list ([field-id-stx (in-list (syntax-e #'(field-id ...)))])
                                      (format-id field-id-stx "~a-~a" #'message-id field-id-stx))
     #:with read-message-id (format-id #'message-id "read-~a" #'message-id)
     #:with write-message-id (format-id #'message-id "write-~a" #'message-id)
     #:with default-parser-id (format-id #'message-id "proto:~a" #'message-id)
     #:with default-unparser-id (format-id #'message-id "proto:un-~a" #'message-id)
     #'(begin
         (struct message-id (field-id ...)
           #:transparent
           #:property prop:message-writer
           (λ () write-message-id))
         (define (read-message-id in)
           (define data ({~? parser-proc default-parser-id} in))
           (define msg (message-id (and~&amp;gt; (assq 'field-sym data) cdr) ...))
           (begin0 msg
             (log-protohackers-debug "read ~s" msg)))
         (define (write-message-id m out)
           (log-protohackers-debug "write ~s" m)
           (define msg `((num_1 . ,tag-number) (field-sym . ,(field-accessor-id m)) ...))
           ({~? unparser-proc default-unparser-id} msg out)
           (flush-output out))
         (hash-set! *message-readers* tag-number read-message-id))]))

(define-message Error
  ([message Str_1])
  #:tag #x10)

(define-message Plate
  ([plate PlateStr_1]
   [timestamp Timestamp_1])
  #:tag #x20)

(define-message Ticket
  ([plate PlateStr_1]
   [road Road_1]
   [mile1 Mile_1]
   [timestamp1 Timestamp_1]
   [mile2 Mile_2]
   [timestamp2 Timestamp_2]
   [speed Speed_1])
  #:tag #x21)

(define-message WantHeartbeat
  ([interval Interval_1])
  #:tag #x40)

(define-message Heartbeat ()
  #:tag #x41
  #:unparser (λ (_ out)
               (write-byte #x41 out)
               (flush-output out)))

(define-message Camera
  ([road Road_1]
   [mile Mile_1]
   [limit Limit_1])
  #:tag #x80
  #:parser proto:IAmCamera
  #:unparser proto:un-IAmCamera)

(define-message Dispatcher
  ([roads Road_1])
  #:tag #x81
  #:parser proto:IAmDispatcher
  #:unparser proto:un-IAmDispatcher)

(define (read-message in)
  (match (peek-byte in)
    [(? eof-object?) eof]
    [tag-number
     (with-handlers ([exn:fail?
                      (λ (e)
                        (begin0 #f
                          (log-protohackers-error "failed to read message with tag ~s: ~a" tag-number (exn-message e))))])
       ((hash-ref *message-readers* tag-number) in))]))

(define (write-message m out)
  (((message-writer m)) m out))

(define (write-message* m out)
  (with-handlers ([exn:fail?
                   (λ (e)
                     (log-protohackers-warning "failed to write message: ~s~n  error: ~a" m (exn-message e)))])
    (write-message m out)))

(define (write-error msg out)
  (write-message* (Error (string-&amp;gt;bytes/utf-8 msg)) out))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;define-message&lt;/code&gt; form generates a struct with the given fields, a
reader procedure that parses data off of a port and destructures it
onto a struct instance and a writer procedure that does the reverse.
It then registers the reader procedure with the global read registry
by tag and associates the write procedure with the struct using a
custom struct type property.&lt;/p&gt;&lt;p&gt;The &lt;code&gt;read-message&lt;/code&gt; and &lt;code&gt;write-message&lt;/code&gt; procedures use the
aforementioned registry and struct type property to generically read
and write messages.&lt;/p&gt;&lt;h3&gt;Dispatcher&lt;/h3&gt;&lt;p&gt;Similar to challenge 3, we keep track of shared state using an actor
implemented with a thread and a channel.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(struct dispatcher (ch thd))

(define (make-dispatcher)
  (define ch (make-channel))
  (define thd
    (thread
     (lambda ()
       (let loop ([st (make-state)])
         (let-values ([(tickets ticketed-st) (get-tickets st)])
           (cond
             [(null? tickets)
              (apply
               sync
               (handle-evt
                ch
                (lambda (msg)
                  (log-protohackers-debug "handling dispatcher message: ~s" msg)
                  (match msg
                    [`(add-port ,out ,res-ch ,nack)
                     (define-values (id next-st)
                       (add-port st out))
                     (loop (add-request next-st `(,id ,res-ch ,nack)))]
                    [`(add-dispatcher ,id ,roads ,res-ch ,nack)
                     (loop (~&amp;gt; (add-dispatcher st id roads)
                               (add-request `(ok ,res-ch ,nack))))]
                    [`(remove-port ,id ,res-ch ,nack)
                     (loop (~&amp;gt; (remove-port st id)
                               (add-request `(ok ,res-ch ,nack))))]
                    [`(update-heartbeat ,id ,interval ,res-ch ,nack)
                     (loop (~&amp;gt; (update-heartbeat st id interval)
                               (add-request `(ok ,res-ch ,nack))))]
                    [`(snap ,plate-number ,road ,mile ,limit ,timestamp ,res-ch ,nack)
                     (loop (~&amp;gt; (track-plate st plate-number road mile limit timestamp)
                               (add-request `(ok ,res-ch ,nack))))]
                    [_
                     (log-protohackers-error "unexpected dispatcher message: ~s~n" msg)])))
               (handle-evt
                (heartbeat-evt st)
                (lambda (id interval)
                  (define out (ref-port st id))
                  (write-message* (Heartbeat) out)
                  (loop (update-heartbeat st id interval))))
               (append
                (for/list ([req (in-list (state-requests st))])
                  (match-define `(,res ,res-ch ,_) req)
                  (handle-evt
                   (channel-put-evt res-ch res)
                   (lambda (_)
                     (loop (remove-request st req)))))
                (for/list ([req (in-list (state-requests st))])
                  (match-define `(,_ ,_ ,nack) req)
                  (handle-evt nack (λ () (loop (remove-request st req)))))))]
             [else
              (for ([ticket (in-list tickets)])
                (define road (Ticket-road ticket))
                (define id (random-ref (hash-ref (state-dispatchers st) road)))
                (define out (ref-port st id))
                (write-message* ticket out))
              (loop ticketed-st)]))))))
  (dispatcher ch thd))

(define (make-dispatcher-evt d command . args)
  (match-define (dispatcher ch thd) d)
  (define res-ch (make-channel))
  (nack-guard-evt
   (lambda (nack)
     (begin0 res-ch
       (thread-resume thd (current-thread))
       (channel-put ch `(,command ,@args ,res-ch ,nack))))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The actor receives messages on a channel and updates its internal
state accordingly, checking to see if it has to dispatch any tickets
after every update.  When it has tickets to dispatch, it first writes
them all out to their destination dispatchers and then resumes
handling messages.&lt;/p&gt;&lt;p&gt;The actor's state is implemented using a set of structs and several
functions that operate on them:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(struct heartbeat (deadline interval))
(struct observation (mile limit timestamp) #:transparent)
(struct plate (observations-by-road ticket-days) #:transparent)
(struct state
  (id-seq      ;; nonnegative-integer
   ports       ;; id -&amp;gt; output-port
   heartbeats  ;; id -&amp;gt; heartbeat
   dispatchers ;; road -&amp;gt; listof id
   plates      ;; plate-number -&amp;gt; listof plate
   requests    ;; listof (res res-ch nack)
   )
  #:transparent)

(define (make-plate)
  (plate (hasheqv) ;; observations-by-road
         (hasheqv) ;; ticket-days
         ))

(define (add-observation p road obs)
  (match-define (plate roads _) p)
  (define next-roads
    (hash-update
     roads road
     (λ (obss)
       (cons obs obss))
     null))
  (struct-copy plate p [observations-by-road next-roads]))

(define (add-ticket-days p timestamp1 timestamp2)
  (define ticket-days
    (for/fold ([ticket-days (plate-ticket-days p)])
              ([d (in-range (-&amp;gt;day timestamp1)
                            (add1 (-&amp;gt;day timestamp2)))])
      (hash-set ticket-days d #t)))
  (struct-copy plate p [ticket-days ticket-days]))

(define (ticketed-on? p timestamp)
  (hash-has-key?
   (plate-ticket-days p)
   (-&amp;gt;day timestamp)))

(define (make-state)
  (state 0         ;; id-seq
         (hasheqv) ;; ports
         (hasheqv) ;; heartbeats
         (hasheqv) ;; dispatchers
         (hash)    ;; plates
         null      ;; requests
         ))

(define (add-port st out)
  (define id (state-id-seq st))
  (values id (struct-copy state st
                          [id-seq (add1 id)]
                          [ports (hash-set (state-ports st) id out)])))

(define (ref-port st id)
  (hash-ref (state-ports st) id))

(define (remove-port st id)
  (struct-copy state st
               [ports (hash-remove (state-ports st) id)]
               [heartbeats (hash-remove (state-heartbeats st) id)]
               [dispatchers (for/hash ([(road ids) (in-hash (state-dispatchers st))])
                              (values road (remq id ids)))]))

(define (update-heartbeat st id interval)
  (cond
    [(zero? interval)
     (struct-copy state st [heartbeats (hash-remove (state-heartbeats st) id)])]
    [else
     (define beat (heartbeat (+ (current-inexact-monotonic-milliseconds) interval) interval))
     (struct-copy state st [heartbeats (hash-set (state-heartbeats st) id beat)])]))

(define (heartbeat-evt st)
  (define heartbeats
    (state-heartbeats st))
  (cond
    [(hash-empty? heartbeats)
     never-evt]
    [else
     (match-define (cons id (heartbeat deadline interval))
       (car (sort (hash-&amp;gt;list (state-heartbeats st)) #:key (compose1 heartbeat-deadline cdr) &amp;lt;)))
     (handle-evt
      (alarm-evt deadline #t)
      (lambda (_)
        (values id interval)))]))

(define (add-dispatcher st id roads)
  (define next-dispatchers
    (for/fold ([dispatchers (state-dispatchers st)])
              ([road (in-list roads)])
      (hash-update dispatchers road (λ (ids) (cons id ids)) null)))
  (struct-copy state st [dispatchers next-dispatchers]))

(define (track-plate st plate-number road mile limit timestamp)
  (define obs (observation mile limit timestamp))
  (define next-plates
    (hash-update
     (state-plates st) plate-number
     (λ (p) (add-observation p road obs))
     make-plate))
  (struct-copy state st [plates next-plates]))

(define (get-tickets st)
  (define dispatchers (state-dispatchers st))
  (define-values (tickets plates)
    (for*/fold ([tickets null]
                [plates (state-plates st)])
               ([(plate-number p) (in-hash (state-plates st))]
                [(road observations) (in-hash (plate-observations-by-road p))]
                #:unless (null? (hash-ref dispatchers road null)))
      (define sorted-observations
        (sort observations #:key observation-timestamp &amp;gt;))
      (define-values (next-tickets next-plate)
        (for/fold ([tickets tickets] [p p])
                  ([obs2 (in-list sorted-observations)]
                   [obs1 (in-list (cdr sorted-observations))]
                   #:unless (ticketed-on? p (observation-timestamp obs2))
                   #:unless (ticketed-on? p (observation-timestamp obs1))
                   #:unless (= (observation-timestamp obs2)
                               (observation-timestamp obs1)))
          (match-define (observation mile1 limit timestamp1) obs1)
          (match-define (observation mile2 _     timestamp2) obs2)
          (define speed
            (/ (abs (- mile2 mile1))
               (/ (- timestamp2 timestamp1) 3600)))
          (cond
            [(&amp;lt;= speed limit)
             (values tickets p)]
            [else
             (define ticket
               (Ticket plate-number road mile1 timestamp1 mile2 timestamp2 (exact-round (* speed 100))))
             (values
              (cons ticket tickets)
              (add-ticket-days p timestamp1 timestamp2))])))
      (values next-tickets (hash-set plates plate-number next-plate))))
  (values tickets (struct-copy state st [plates plates])))

(define (add-request st req)
  (struct-copy state st [requests (cons req (state-requests st))]))

(define (remove-request st req)
  (struct-copy state st [requests (remq req (state-requests st))]))

(define (-&amp;gt;day ts)
  (quotient ts 86400))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This makes it easy to test the state and ticketing implementation in
isolation:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(module+ test
  (require racket/port
           rackunit)
  (define (get-tickets* st)
    (define-values (tickets _)
      (get-tickets st))
    tickets)
  (check-equal?
   (get-tickets* (make-state))
   null)
  (check-equal?
   (~&amp;gt; (make-state)
       (track-plate "ABC" 1 0 80 0)
       (track-plate "ABC" 1 80 80 3600)
       (get-tickets*))
   null
   "within limit")
  (check-equal?
   (~&amp;gt; (make-state)
       (track-plate "ABC" 1 0 80 0)
       (track-plate "ABC" 1 80 80 1800)
       (get-tickets*))
   null
   "speed exceeded but no dispatcher")
  (check-equal?
   (let*-values ([(st) (make-state)]
                 [(id st) (add-port st (open-output-nowhere))])
     (~&amp;gt; (add-dispatcher st id '(1))
         (track-plate "ABC" 1 0 80 0)
         (track-plate "ABC" 1 80 80 1800)
         (get-tickets*)))
   (list (Ticket "ABC" 1 0 0 80 1800 16000))
   "speed exceeded with dispatcher")
  (check-equal?
   (let*-values ([(st) (make-state)]
                 [(id st) (add-port st (open-output-nowhere))]
                 [(_tickets st)
                  (~&amp;gt; (add-dispatcher st id '(1))
                      (track-plate "ABC" 1 0 80 0)
                      (track-plate "ABC" 1 80 80 1800)
                      (get-tickets))]
                 [(tickets _st)
                  (~&amp;gt; (track-plate st "ABC" 1 160 80 3600)
                      (get-tickets))])
     tickets)
   null
   "speed exceeded twice in same day")
  (check-equal?
   (let*-values ([(st) (make-state)]
                 [(id st) (add-port st (open-output-nowhere))]
                 [(_tickets st)
                  (~&amp;gt; (add-dispatcher st id '(1))
                      (track-plate "ABC" 1 0 80 0)
                      (track-plate "ABC" 1 80 80 1800)
                      (get-tickets))]
                 [(tickets _st)
                  (~&amp;gt; (track-plate st "ABC" 1 16000 80 86400)
                      (track-plate "ABC" 1 32000 80 (* 2 86400))
                      (get-tickets))])
     tickets)
   (list (Ticket "ABC" 1 16000 86400 32000 172800 66667))
   "speed exceeded next day"))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I'm using functional updates to alter the state between actor ticks.
This is fine for the purposes of this challenge, but if performance
were to become an issue, I'd probably switch to a mutable
implementation.&lt;/p&gt;&lt;p&gt;The challenge is unclear on what should happen should an observation
arrive arbitrarily late, so we keep all observations around
perpetually.  In a real system, we'd want to expire these.&lt;/p&gt;&lt;h3&gt;Server&lt;/h3&gt;&lt;p&gt;The server implementation simply accepts connections and sends
messages to the dispatcher on their behalf.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define-syntax-rule (send d command . args)
  (sync (make-dispatcher-evt d 'command . args)))

(define ((make-handler d) in out)
  (define id (send d add-port out))
  (dynamic-wind
    void
    (lambda ()
      (let loop ([ob #f])
        (match (read-message in)
          [(? eof-object?)
           (void)]
          [(? Camera? camera)
           #:when (not ob)
           (loop camera)]
          [(and (Dispatcher roads) dispatcher)
           #:when (not ob)
           (send d add-dispatcher id roads)
           (loop dispatcher)]
          [(WantHeartbeat interval)
           (send d update-heartbeat id (* (/ interval 10.0) 1000))
           (loop ob)]
          [(Plate plate-number timestamp)
           #:when (Camera? ob)
           (match-define (Camera road mile limit) ob)
           (send d snap plate-number road mile limit timestamp)
           (loop ob)]
          [_
           (write-error "unexpected message" out)])))
    (lambda ()
      (send d remove-port id))))

(module+ main
  (require "common.rkt")
  (run-server* "0.0.0.0" 8111 (make-handler (make-dispatcher))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Every new client is registered with the dispatcher and receives a
unique id.  We use &lt;code&gt;dynamic-wind&lt;/code&gt; to ensure that each client is
deregistered on failure or on connection close.&lt;/p&gt;&lt;p&gt;That's all I have for this post.  As before, you can find these
solutions in full on &lt;a href="https://github.com/Bogdanp/racket-protohackers"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;&lt;section class="footnotes"&gt;&lt;ol&gt;&lt;li id="fn_1"&gt;&lt;p&gt;I wrote this post last Sunday, but only published it today
(Wednesday, March 26) after tweaking the post and solutions a bit. &lt;a class="footnote-backref" href="#fnref_1_1" title="Jump to reference" aria-label="Jump to reference"&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/section&gt;&lt;/article&gt;</description></item><item><title>Protohackers Challenge in Racket Part 1</title><link>https://defn.io/2023/03/25/protohackers-part-1</link><guid>https://defn.io/2023/03/25/protohackers-part-1</guid><pubDate>Sat, 25 Mar 2023 11:23:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;Someone on the Racket Discord recently mentioned the &lt;a href="https://protohackers.com/"&gt;Protohackers&lt;/a&gt;
project and I figured it'd be fun to write about Racket solutions to the
challenges available on the website.&lt;/p&gt;&lt;h2&gt;0: Smoke Test&lt;/h2&gt;&lt;p&gt;The 0th challenge is a basic echo server.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;#lang racket/base

(require racket/match
         racket/tcp)

(define (handle in out)
  (let loop ()
    (match (read-bytes 4096 in)
      [(? eof-object?)
       (void)]
      [bs
       (write-bytes bs out)
       (loop)])))

(module+ main
  (define listener
    (tcp-listen 8111 512 #t "0.0.0.0"))
  (define server-custodian
    (make-custodian))
  (with-handlers ([exn:break? void])
    (parameterize ([current-custodian server-custodian])
      (let loop ()
        (parameterize-break #f
          (define-values (in out)
            (tcp-accept/enable-break listener))
          (define client-custodian
            (make-custodian))
          (define client-thd
            (parameterize ([current-custodian client-custodian])
              (thread
               (lambda ()
                 (break-enabled #t)
                 (handle in out)))))
          (thread
           (lambda ()
             (sync client-thd)
             (close-output-port out)
             (close-input-port in)
             (custodian-shutdown-all client-custodian))))
        (loop))))
  (custodian-shutdown-all server-custodian)
  (tcp-close listener))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;All we have to do is start a TCP listener, then accept new connections
in a loop. For every new connection, we open a thread to handle it and
a thread to close the connection and shut down the client custodian
after the handling thread is done. Wrapping every client in a custodian
ensures the handlers cannot leak resources (other threads, ports, etc.)
after they exit. We disable breaks during connection setup so that
an ill-timed break won't leave connections in a half-set-up state.
On break (&lt;code&gt;SIGINT&lt;/code&gt;, &lt;code&gt;SIGTERM&lt;/code&gt;, or other signals), we terminate all
running handler threads and their associated resources then close the
TCP listener.&lt;/p&gt;&lt;p&gt;The &lt;code&gt;handle&lt;/code&gt; procedure reads data in up to 4096 byte chunks and writes
it back to the client. On &lt;code&gt;EOF&lt;/code&gt;, &lt;code&gt;read-bytes&lt;/code&gt; returns a special &lt;code&gt;EOF&lt;/code&gt;
value and, in that case, we simply exit the loop.&lt;/p&gt;&lt;p&gt;One notable thing about this server is we have no limit on the number
of concurrent clients, so it is easy to flood. We also have no limit
on connection duration, though that would be easy to add to the
handler-supervising thread by using &lt;code&gt;sync/timeout&lt;/code&gt; instead of &lt;code&gt;sync&lt;/code&gt;.&lt;/p&gt;&lt;h2&gt;1: Prime Time&lt;/h2&gt;&lt;p&gt;This challenge requires us to do some JSON parsing and primality
checking.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;#lang racket/base

(require json
         racket/match
         racket/tcp)

(define (prime? n)
  (let ([n (truncate n)])
    (and (not (negative? n))
         (not (= n 0))
         (not (= n 1))
         (or (= n 2)
             (not
              (for/or ([i (in-range 2 (add1 (sqrt n)))])
                (zero? (modulo n i))))))))

(define (handle in out)
  (with-handlers ([exn:fail?
                   (λ (e)
                     ((error-display-handler) (format "client error: ~a" (exn-message e)) e)
                     (displayln "request malformed" out))])
    (let loop ()
      (define line (read-line in))
      (match (string-&amp;gt;jsexpr line)
        [(hash-table
          ['method "isPrime"]
          ['number (? number? n)])
         (write-json (hasheq 'method "isPrime" 'prime (prime? n)) out)
         (newline out)
         (flush-output out)
         (loop)]
        [_
         (displayln "request malformed" out)]))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The TCP listening bits haven't changed from the first challenge, so I've
elided them here. The &lt;code&gt;handle&lt;/code&gt; proc now reads one line at a time from
the client and attempts to parse it as a JSON value. Any parsing error,
as well as any validation error causes the handler to exit the loop and
close the connection after writing a message to the client. Well-formed
messages are validated in the first match clause and every valid request
is followed by a valid response and a newline.&lt;/p&gt;&lt;p&gt;Note that &lt;code&gt;read-line&lt;/code&gt; will happily buffer data in memory until it
sees a linefeed character, meaning an adversarial client could easily
crash this server by sending it a very long stream of non-linefeed
characters&lt;sup&gt;&lt;a href="#fn_1" id="fnref_1_1"&gt;1&lt;/a&gt;&lt;/sup&gt;. Also note how the error handlers are set up outside the
loop. The body of the &lt;code&gt;with-handlers&lt;/code&gt; form is not in tail position,
so if we'd have set up the handlers inside the loop, we'd be creating
unnecessary frames on every iteration, increasing memory consumption.&lt;/p&gt;&lt;h2&gt;2: Means to an End&lt;/h2&gt;&lt;p&gt;This challenge requires us to parse a custom binary format and store a
little bit of state for each client.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;#lang racket/base

(require racket/match
         racket/math
         racket/port
         racket/tcp
         "002.bnf")

(define (get-avg-price prices min-time max-time)
  (for/fold ([n 0] [s 0] #:result (if (zero? n) 0 (exact-round (/ s n))))
            ([(timestamp price) (in-hash prices)]
             #:when (and (&amp;gt;= timestamp min-time)
                         (&amp;lt;= timestamp max-time)))
    (values (add1 n) (+ s price))))

(define (handle in out)
  (let loop ([prices (hasheqv)])
    (define data (read-bytes 9 in))
    (unless (eof-object? data)
      (match (call-with-input-bytes data Message)
        [`((char_1 . #\I)
           (Timestamp_1 . ,timestamp)
           (Price_1 . ,price))
         (loop (hash-set prices timestamp price))]
        [`((char_1 . #\Q)
           (MinTime_1 . ,min-time)
           (MaxTime_1 . ,max-time))
         (un-Price (get-avg-price prices min-time max-time) out)
         (flush-output out)
         (loop prices)]))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Just like with challenge 1, the listener bits have not changed. However,
I did decide to overengineer this solution a little by using &lt;a href="https://docs.racket-lang.org/binfmt-manual/index.html"&gt;binfmt&lt;/a&gt; to
parse the binary format. The contents of "0002.bnf" are:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;#lang binfmt

Message = Insert | Query;

Insert = 'I' Timestamp Price;
Timestamp = i32be;
Price = i32be;

Query = 'Q' MinTime MaxTime;
MinTime = i32be;
MaxTime = i32be;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;handler&lt;/code&gt; proc reads 9 bytes from the client at a time and tries to
parse a &lt;code&gt;Message&lt;/code&gt; out of them. When it receives an insert message, it
updates the price slot for that timestamp and when it receives a query
message, it computes an average price and responds to the client.&lt;/p&gt;&lt;p&gt;Like the previous challange, this server is susceptible to an attack
where an adversarial client could send it enough prices to exhaust
available memory and cause it to crash.&lt;/p&gt;&lt;h2&gt;3: Budget Chat&lt;/h2&gt;&lt;p&gt;This challenge requires us to implement a chat room.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;#lang racket/base

(require racket/match
         racket/string
         racket/tcp)

(define room-ch (make-channel))
(define room-thd
  (thread/suspend-to-kill
   (lambda ()
     (let loop ([users (hasheq)]
                [reqs null])
       (apply
        sync
        (handle-evt
         room-ch
         (lambda (msg)
           (match msg
             [`(join ,name ,out ,res-ch ,nack)
              (cond
                [(hash-has-key? users name)
                 (define req `((fail "username-taken") ,res-ch ,nack))
                 (loop users (cons req reqs))]
                [else
                 (define req `((ok ,(hash-keys users)) ,res-ch ,nack))
                 (broadcast users (format "* ~a has joined the room~n" name))
                 (loop (hash-set users name out) (cons req reqs))])]
             [`(broadcast ,name ,message ,res-ch ,nack)
              (define req `((ok) ,res-ch ,nack))
              (broadcast (hash-remove users name) (format "[~a] ~a~n" name message))
              (loop users (cons req reqs))]
             [`(leave ,name ,res-ch ,nack)
              (define req `((ok) ,res-ch ,nack))
              (define remaining-users (hash-remove users name))
              (broadcast remaining-users (format "* ~a has left the room~n" name))
              (loop remaining-users (cons req reqs))]
             [_
              (log-warning "malformed message: ~s" msg)
              (loop users reqs)])))
        (append
         (for/list ([req (in-list reqs)])
           (match-define `(,res ,res-ch ,_) req)
           (handle-evt
            (channel-put-evt res-ch res)
            (λ (_) (loop users (remq req reqs)))))
         (for/list ([req (in-list reqs)])
           (match-define `(,_ ,_ ,nack) req)
           (handle-evt nack (λ (_) (loop users (remq req reqs)))))))))))

(define (make-room-evt command . args)
  (define res-ch (make-channel))
  (nack-guard-evt
   (lambda (nack)
     (begin0 res-ch
       (thread-resume room-thd (current-thread))
       (channel-put room-ch `(,command ,@args ,res-ch ,nack))))))

(define (handle in out)
  (fprintf* out "Welcome to budgetchat! What shall I call you?~n")
  (match (read-line in 'any)
    [(regexp #px"^[a-zA-Z0-9]{1,16}$" (list (app string-&amp;gt;symbol name)))
     (match (sync (make-room-evt 'join name out))
       [`(fail ,message)
        (fprintf out "error: ~a~n" message)]
       [`(ok ,names)
        (fprintf* out "* The room contains: ~a~n" (string-join (map symbol-&amp;gt;string (sort names symbol&amp;lt;?))))
        (with-handlers ([exn:fail? (λ (e) ((error-display-handler) (format "client error: ~a" (exn-message e)) e))])
          (let loop ()
            (define data
              (read-line in 'any))
            (unless (eof-object? data)
              (sync (make-room-evt 'broadcast name data))
              (loop))))
        (sync (make-room-evt 'leave name))])]
    [_
     (fprintf* out "error: invalid name~n")]))

(define (broadcast users message)
  (for ([out (in-hash-values users)])
    (with-handlers ([exn:fail? void])
      (fprintf* out message))))

(define (fprintf* out msg . args)
  (apply fprintf out msg args)
  (flush-output out))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The implementation for this challenge is more complex since it involves
keeping track of shared state and broadcasting messages to multiple
clients. I opted to make a shared thread to keep track of user sessions.
The contents of the loop inside &lt;code&gt;room-thd&lt;/code&gt; is a common pattern I
use when implementing stateful actors in Racket, stolen from &lt;a href="https://www-old.cs.utah.edu/plt/publications/pldi04-ff.pdf"&gt;this
paper&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;The room is itself a sort of server. It receives messages via the
&lt;code&gt;room-ch&lt;/code&gt; where each message is expected to have a channel on which
responses can be sent, and a negative acknowledgement &lt;code&gt;evt&lt;/code&gt;. For
every message it receives, it updates its internal state and responds
to whatever requests it can, or removes any requests whose negative
acknowledgement event is ready for synchronization (i.e. requests
that have been abandoned ). The &lt;code&gt;make-room-evt&lt;/code&gt; procedure generates a
synchronizable event that sends a request to the room and receives a
response when synchronized.&lt;/p&gt;&lt;p&gt;The handler follows the initialization sequence by asking the client for
a nickname, validating it and then entering the messaging loop. When a
client joins the room, it sends its nick along with its output port to
the room. The room then writes to the client's output port whenever new
information is broadcast.&lt;/p&gt;&lt;p&gt;This server is susceptible to the same attacks as the ones before
it: it uses &lt;code&gt;read-line&lt;/code&gt; to read messages and it doesn't limit the
number of concurrent users. The former we can fix by writing a custom
&lt;code&gt;read-line*&lt;/code&gt; function that rejects lines longer than 1024 bytes (eg, by
using &lt;code&gt;peek-bytes&lt;/code&gt;) and the latter we can fix by making the room reject
&lt;code&gt;join&lt;/code&gt; requests when the number of users exceeds some limit.&lt;/p&gt;&lt;p&gt;That's all for today. You can find these servers in full on &lt;a href="https://github.com/Bogdanp/racket-protohackers"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;&lt;section class="footnotes"&gt;&lt;ol&gt;&lt;li id="fn_1"&gt;&lt;p&gt;Ryan Culpepper pointed out on the Racket Discord that we could
combine &lt;code&gt;read-line&lt;/code&gt; with &lt;code&gt;make-limited-input-port&lt;/code&gt; in order to
limit the amount of data &lt;code&gt;read-line&lt;/code&gt; will buffer in memory. &lt;a class="footnote-backref" href="#fnref_1_1" title="Jump to reference" aria-label="Jump to reference"&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/section&gt;&lt;/article&gt;</description></item><item><title>Racketfest 2023 Talk: Native Apps with Racket</title><link>https://defn.io/2023/03/19/racketfest-talk-2023</link><guid>https://defn.io/2023/03/19/racketfest-talk-2023</guid><pubDate>Sun, 19 Mar 2023 19:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;&lt;a href="https://racketfest.com"&gt;Racketfest&lt;/a&gt; 2023 was held yesterday and I gave a short talk about
building native apps with Racket.  Nothing new if you've read my
recent posts, but below is a transcript.  A recording might also be
posted later, in which case I'll update this post to link to it.&lt;/p&gt;&lt;h2&gt;Transcript&lt;/h2&gt;&lt;h3&gt;Native Apps&lt;/h3&gt;&lt;p&gt;&lt;img alt="Slide number 0." src="/img/racketfest2023-slides/slide-0.jpg"/&gt;&lt;/p&gt;&lt;p&gt;My name is Bogdan Popa, and today I will be talking about an approach
I've been using to build native desktop apps with Racket.&lt;/p&gt;&lt;h3&gt;Native Applications?&lt;/h3&gt;&lt;p&gt;&lt;img alt="Slide number 1." src="/img/racketfest2023-slides/slide-1.jpg"/&gt;&lt;/p&gt;&lt;p&gt;First, what do I mean by "Native Application"? I mean an application
that uses the system libraries and frameworks for building
applications.  Applications that look and feel like other applications
that ship with the system and that implies they have access to all
available widgets on the system.&lt;/p&gt;&lt;h3&gt;With Racket?&lt;/h3&gt;&lt;p&gt;&lt;img alt="Slide number 2." src="/img/racketfest2023-slides/slide-2.jpg"/&gt;&lt;/p&gt;&lt;p&gt;Why with Racket?  Because I like using the language and I've built up
a large collection of libraries over the years and because I'd like
the core logic to be portable between operating systems.&lt;/p&gt;&lt;h3&gt;Approaches&lt;/h3&gt;&lt;p&gt;&lt;img alt="Slide number 3." src="/img/racketfest2023-slides/slide-3.jpg"/&gt;&lt;/p&gt;&lt;p&gt;So far, I've used three approaches to building desktop apps in Racket:&lt;/p&gt;&lt;ol class="tight" start="1"&gt;&lt;li&gt;&lt;code&gt;racket/gui&lt;/code&gt;&lt;/li&gt;&lt;li&gt;Embedding Racket as a subprocess.&lt;/li&gt;&lt;li&gt;Embedding Racket directly, which is the focus of this talk.&lt;/li&gt;&lt;/ol&gt;&lt;h2&gt;racket/gui{,easy}&lt;/h2&gt;&lt;p&gt;&lt;img alt="Slide number 4." src="/img/racketfest2023-slides/slide-4.jpg"/&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;racket/gui&lt;/code&gt; comes with Racket and supports a combination of native
and custom widgets on Linux, macOS and Windows.  But, the set of
widgets it supports out of the box is limited, and not all widgets
(eg. input fields) are truly native.  Additionally, because it aims to
support all of the aforementioned platforms, it's hard to extend it
with new widgets because analogs of a widget on one platform might not
exist on others.&lt;/p&gt;&lt;h3&gt;Embedding as Subprocess&lt;/h3&gt;&lt;p&gt;&lt;img alt="Slide number 5." src="/img/racketfest2023-slides/slide-5.jpg"/&gt;&lt;/p&gt;&lt;p&gt;Another approach I've used is embedding Racket as a subprocess.  The
idea here being that the GUI app runs Racket in a subprocess and
communicates with it via pipes.  I've actually shipped an
&lt;a href="/2020/01/02/ann-remember/"&gt;app&lt;/a&gt; to the Mac App Store using this approach.&lt;/p&gt;&lt;p&gt;One downside with this approach is that memory consumption is
relatively high (but that's more of a Racket problem in general, than
one particular to this approach).  Another is that, since these are
two separate processes, the Racket code can't call back into Swift
without help.&lt;/p&gt;&lt;h3&gt;Embedding Directly&lt;/h3&gt;&lt;p&gt;&lt;img alt="Slide number 6." src="/img/racketfest2023-slides/slide-6.jpg"/&gt;&lt;/p&gt;&lt;p&gt;The approach I used with &lt;a href="https://franz.defn.io"&gt;Franz&lt;/a&gt; is to compile Racket as a static
library and link it into a Swift app.  Like in the subprocess
approach, I've opted to run the Racket runtime in its own thread and
communicate with it via pipes&lt;sup&gt;&lt;a href="#fn_1" id="fnref_1_1"&gt;1&lt;/a&gt;&lt;/sup&gt;.  By running Racket in its own OS
thread, it can keep scheduling its own threads as normal and I can run
an RPC server that listens on a pipe for requests and serves responses
asynchronously.  An advantage over the previous approach is here the
Swift and Racket sides share memory so it's possible for the Racket
side to call out to Swift directly.&lt;/p&gt;&lt;p&gt;Memory use is still a downside, though slightly better than the
subprocess approach because process overhead is reduced and things
like shared libraries can be shared between the two runtimes.&lt;/p&gt;&lt;h3&gt;Demo&lt;/h3&gt;&lt;p&gt;&lt;center&gt;&lt;p&gt;&lt;em&gt;[No transcript for the demo portion, but see the &lt;a href="https://franz.defn.io"&gt;Franz website&lt;/a&gt;.]&lt;/em&gt;&lt;/p&gt;&lt;/center&gt;&lt;/p&gt;&lt;h3&gt;Code Stats&lt;/h3&gt;&lt;p&gt;&lt;img alt="Slide number 8." src="/img/racketfest2023-slides/slide-8.jpg"/&gt;&lt;/p&gt;&lt;p&gt;In terms of code, the Swift portion is about 9k lines and the Racket
portion about 18k lines, but the Racket portion also includes the
Kafka &lt;a href="https://github.com/Bogdanp/racket-kafka"&gt;client&lt;/a&gt;.&lt;/p&gt;&lt;h3&gt;How it Works&lt;/h3&gt;&lt;p&gt;&lt;img alt="Slide number 9." src="/img/racketfest2023-slides/slide-9.jpg"/&gt;&lt;/p&gt;&lt;p&gt;As mentioned, this approach works by compiling Racket to a static
library and linking it directly into a Swift application.  The Racket
code is then compiled using &lt;code&gt;raco ctool&lt;/code&gt; to a &lt;code&gt;.zo&lt;/code&gt; bytecode file and
shipped alongside the app.&lt;/p&gt;&lt;h3&gt;How it Works (cont'd)&lt;/h3&gt;&lt;p&gt;&lt;img alt="Slide number 10." src="/img/racketfest2023-slides/slide-10.jpg"/&gt;&lt;/p&gt;&lt;p&gt;On boot, the Swift application starts Racket in a background thread,
loads the &lt;code&gt;.zo&lt;/code&gt; code from the application bundle, and there's a small
interface for setting up the RPC system between the two languages.  A
"main" procedure is loaded from the &lt;code&gt;.zo&lt;/code&gt; code, then that procedure is
called with a set of pipe file descriptors, which are then converted
into ports on the Racket side.&lt;/p&gt;&lt;h3&gt;Noise&lt;/h3&gt;&lt;p&gt;&lt;img alt="Slide number 11." src="/img/racketfest2023-slides/slide-11.jpg"/&gt;&lt;/p&gt;&lt;p&gt;To abstract over some of this stuff, I've written a set of Swift and
Racket libraries (with plans to add C# support for Windows soon).
They live under the Noise repo, linked at the top, and they are split
up in roughly three layers.  The lowest layer handles embedding
&lt;code&gt;libracketcs&lt;/code&gt; and provides a Swift wrapper for its C ABI.  The layer
above that implements a protocol for serializing and deserializing
data between the two languages and the layer above that implements a
protocol for communicating via pipes.&lt;/p&gt;&lt;h3&gt;NoiseRacket&lt;/h3&gt;&lt;p&gt;&lt;img alt="Slide number 12." src="/img/racketfest2023-slides/slide-12.jpg"/&gt;&lt;/p&gt;&lt;p&gt;NoiseRacket is the lowest layer and, as mentioned, it handles the
embedding of &lt;code&gt;libracketcs&lt;/code&gt;.  From Swift code, you just import &lt;code&gt;Noise&lt;/code&gt;,
then create an instance of the &lt;code&gt;Racket&lt;/code&gt; class to initialize the Racket
runtime.  Then, every time you want to call Racket, you pass a closure
to that object's &lt;code&gt;bracket&lt;/code&gt; method.  In this example, we load a &lt;code&gt;.zo&lt;/code&gt;
file, construct a module path and require a function named &lt;code&gt;fib&lt;/code&gt; then
apply it and print the result.  As you can see, this is pretty
laborious.&lt;/p&gt;&lt;h3&gt;NoiseSerde&lt;/h3&gt;&lt;p&gt;&lt;img alt="Slide number 13." src="/img/racketfest2023-slides/slide-13.jpg"/&gt;&lt;/p&gt;&lt;p&gt;NoiseSerde provides a set of macros on the Racket side and a code
generator that produces Swift code from record and enum definitions.
The example on the left expands to a struct definition that knows how
to serialize and deserialize itself.  Additionally, it stores
information for the code generator so that it can produce a matching
struct on the Swift side so that the two sides can pass data around
transparently.&lt;/p&gt;&lt;h3&gt;NoiseBackend&lt;/h3&gt;&lt;p&gt;&lt;img alt="Slide number 14." src="/img/racketfest2023-slides/slide-14.jpg"/&gt;&lt;/p&gt;&lt;p&gt;This layer provides a macro for defining remote procedure calls in
Racket.  On the Racket side, defined RPCs expand to regular functions,
but they get registered with the RPC server.  On the Swift side, they
expand to method declarations that handle the details of serializing
arguments and returning a &lt;code&gt;Future&lt;/code&gt; value representing the
to-be-received response.&lt;/p&gt;&lt;h3&gt;Performance&lt;/h3&gt;&lt;p&gt;&lt;img alt="Slide number 15." src="/img/racketfest2023-slides/slide-15.jpg"/&gt;&lt;/p&gt;&lt;p&gt;This might sound like it's a lot of overhead, but in practice it
isn't.  The cost of the RPCs themselves is negligible, and the cost of
ser/de on average is on the order of 1 to 100 microseconds.  All of
this is dwarfed by the overhead of the business logic
(i.e. communicating with Kafka, which takes on the order of 1ms+ even
on loopback).&lt;/p&gt;&lt;h3&gt;Closing Thoughts&lt;/h3&gt;&lt;p&gt;&lt;img alt="Slide number 16." src="/img/racketfest2023-slides/slide-16.jpg"/&gt;&lt;/p&gt;&lt;p&gt;I'm very happy with this approach.  It feels natural to write apps in
this way, and the app that I demoed is already in customers' hands
(some even paid for it!).  In future, I plan to work on Windows
support and we'll have to see how that pans out.  All that said, if
you want to make cross-platform desktop apps, probably something like
Electron is a safer bet, despite not being native.  This approach is
still quite a lot of work.  But, if you're a crazy-person who really
wants to use Racket to make desktop apps for some reason, give it a
try.&lt;/p&gt;&lt;h3&gt;Thanks&lt;/h3&gt;&lt;p&gt;&lt;img alt="Slide number 17." src="/img/racketfest2023-slides/slide-17.jpg"/&gt;&lt;/p&gt;&lt;p&gt;Thank you for attending my talk!  You can find Franz at
&lt;a href="https://franz.defn.io"&gt;franz.defn.io&lt;/a&gt;, and &lt;a href="https://github.com/Bogdanp/Noise"&gt;Noise&lt;/a&gt; on GitHub.&lt;/p&gt;&lt;section class="footnotes"&gt;&lt;ol&gt;&lt;li id="fn_1"&gt;&lt;p&gt;A couple folks asked about why I opted to serialize the data
between Racket and Swift instead of just sharing the objects
directly between the two languages.  After thinking about it for a
bit, I remembered two main reasons why I preferred the serde
approach: 1) I didn't want to have to interrupt the Racket runtime
every time the Swift side needed to access a Racket object and 2)
the Racket CS GC is free to move objects in memory.  While there
are ways to tell the runtime not to move individual values, it
just doesn't seem worth it as long as the serde approach doesn't
add tons of overhead. &lt;a class="footnote-backref" href="#fnref_1_1" title="Jump to reference" aria-label="Jump to reference"&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/section&gt;&lt;/article&gt;</description></item><item><title>Announcing racket-protocol-buffers</title><link>https://defn.io/2023/02/22/ann-racket-protocol-buffers</link><guid>https://defn.io/2023/02/22/ann-racket-protocol-buffers</guid><pubDate>Wed, 22 Feb 2023 11:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;A couple of releases back, I added support for schema registries
to &lt;a href="https://franz.defn.io"&gt;Franz&lt;/a&gt;. Some of its users use &lt;a href="https://protobuf.dev/"&gt;Protocol Buffers&lt;/a&gt; to serialize
their data, so I needed to be able to support that use case. So, I
wrote a parser for the &lt;code&gt;proto2&lt;/code&gt; and &lt;code&gt;proto3&lt;/code&gt; specs and a minimal
serializer/deserializer implementation that doesn't require code
generation on top of that.&lt;/p&gt;&lt;p&gt;You can find the &lt;a href="https://pkgs.racket-lang.org/package/protocol-buffers"&gt;package&lt;/a&gt; and &lt;a href="https://docs.racket-lang.org/protocol-buffers-manual/index.html"&gt;docs&lt;/a&gt; on the Package server
and the &lt;a href="https://github.com/Bogdanp/racket-protocol-buffers"&gt;source&lt;/a&gt; on GitHub.&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Safe Foreign Callouts from Racket to Swift</title><link>https://defn.io/2023/02/04/racket-foreign-callouts-to-swift</link><guid>https://defn.io/2023/02/04/racket-foreign-callouts-to-swift</guid><pubDate>Sat, 4 Feb 2023 13:47:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;In anticipation of working on the Windows &amp;amp; Linux versions of &lt;a href="https://franz.defn.io"&gt;Franz&lt;/a&gt;,
I've wanted to move its auto-update implementation from Swift into
Franz' Racket core.  The reason I implemented the auto-update code in
Swift in the first place is because of the way the Swift part normally
communicates with the Racket part: the core Racket code runs in its
own thread and the Swift part communicates with it asynchronously via
pipes.  So, until a couple of days ago, I didn't have an easy way for
Racket code to trigger the execution of Swift code on its own.&lt;/p&gt;&lt;p&gt;All of the code that handles embedding Racket inside Swift, code
generation and the general communication mechanism is open source and
lives in &lt;a href="https://github.com/Bogdanp/noise"&gt;Noise&lt;/a&gt;, so that's where you can find the full implementation
of the approach I describe in this post (specifically, commits
&lt;a href="https://github.com/Bogdanp/Noise/commit/0a585be4f7816144f4943a996b58fd27ab5e2d2e"&gt;&lt;code&gt;0a585be&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/Bogdanp/Noise/commit/2f6c37e0d26d13f84e1e68650c4bca76cae0bfae"&gt;&lt;code&gt;2f6c37e&lt;/code&gt;&lt;/a&gt;).&lt;/p&gt;&lt;h2&gt;Low Level Bits&lt;/h2&gt;&lt;p&gt;Swift has its own calling convention, but it supports declaring
procedures (but not closures) as using the C calling convention via
the &lt;code&gt;@convention(c)&lt;/code&gt; attribute.  For example:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-swift"&gt;var add1: @convention(c) (Int) -&amp;gt; Int = { x in x + 1 }
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This attribute makes it so that you can transparently pass such
procedures around in places where you would normally store C function
pointers.  In my case, though, I didn't want to have to write any C
code to support this functionality.  Instead, I needed to be able to
get the raw pointer addresses of the procedures so that I could
serialize them and send them (over the aforementioned pipes) to the
Racket side.  Thankfully, there is a way to do this in Swift, via
&lt;code&gt;unsafeBitCast&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-swift"&gt;let ptr = unsafeBitCast(add1, Optional&amp;lt;UnsafeRawPointer&amp;gt;.self)
let addr = Int(bitPattern: ptr!)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;With a raw pointer in hand, all I have to do is make an RPC to the
Racket side to tell it to register a callout at that pointer's
address.  The Racket side can then take that address and construct a
foreign procedure using its FFI facilities:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(require ffi/unsafe)

(define add1-type
  (_func _int -&amp;gt; _int))       ;; 1
(define add1
  (let ([p (malloc _intptr)]) ;; 2
    (ptr-set! p _intptr addr) ;; 3
    (ptr-ref p add1-type)))   ;; 4
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There's no direct way to convert an address to a pointer using the FFI
library.  Instead, I have to allocate a bit of memory (2), write the
address to that memory (3) and then read the address out as a foreign
procedure (4).  Additionally, I have to know what the signature of
that procedure is (1) to be able to call it later. &lt;sup&gt;&lt;a href="#fn_1" id="fnref_1_1"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;p&gt;Putting these bits together, I came up with a small abstraction on
&lt;a href="https://github.com/Bogdanp/Noise/blob/9dc4b05a6e2fbb390f2bc1d47998d02fdc651827/Racket/noise-serde-lib/unsafe/callout.rkt"&gt;the Racket side&lt;/a&gt; to wrap the FFI code needed to
turn a raw address into a foreign procedure:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(struct callout-box (type [proc #:mutable])
  #:property prop:procedure (λ (b . args)
                              (define proc (callout-box-proc b))
                              (unless proc
                                (error 'callout-box "procedure not installed"))
                              (apply (callout-box-proc b) args)))

(define (make-callout-box type)
  (callout-box type #f))

(define (callout-box-install! b addr)
  (define p (malloc _intptr))
  (ptr-set! p _intptr addr)
  (set-callout-box-proc! b (ptr-ref p (callout-box-type b))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And, on &lt;a href="https://github.com/Bogdanp/Noise/blob/9dc4b05a6e2fbb390f2bc1d47998d02fdc651827/Sources/NoiseBackend/Callout.swift"&gt;the Swift side&lt;/a&gt;, I devised a little interface
for installing arbitrary callbacks (on the Swift side) as callouts (on
the Racket side) by using a trampoline (some details, such as locking
around &lt;code&gt;callbacks&lt;/code&gt;, elided for brevity):&lt;/p&gt;&lt;pre&gt;&lt;code info="language-swift"&gt;public func installCallback(id: UInt64, proc: @escaping (Data) -&amp;gt; Void) -&amp;gt; Future&amp;lt;String, Void&amp;gt; {
  callbacks[id] = proc
  let ptr = unsafeBitCast(callbackHandler, to: Optional&amp;lt;UnsafeRawPointer&amp;gt;.self)
  let addr = Int(bitPattern: ptr!)
  installCallback(id, addr)  // RPC to Racket
}

fileprivate var callbacks = [UInt64: (Data) -&amp;gt; Void]()
fileprivate let callbackHandler: @convention(c) (UInt64, Int, UnsafePointer&amp;lt;CChar&amp;gt;) -&amp;gt; Void = { id, len, ptr in
  let data = ...  // based on len and ptr
  let proc = callbacks[id]
  proc!(data)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Whenever &lt;code&gt;installCallback&lt;/code&gt; is called with a closure, it stores the
closure in a global hash and calls an RPC on the Racket side to
register the &lt;code&gt;callbackHandler&lt;/code&gt; as the foreign procedure for that
closure.  The callback handler ends up always being the same, which is
a little wasteful, but this keeps the implementation really
straightforward so I'm not too bothered by it.&lt;/p&gt;&lt;h2&gt;Mid Level Bits&lt;/h2&gt;&lt;p&gt;You've probably noticed the &lt;code&gt;id&lt;/code&gt; argument to &lt;code&gt;installCallback&lt;/code&gt;.  The
Racket and Swift sides need to sync on these ids to know which callout
connects to with callback.  So, on &lt;a href="https://github.com/Bogdanp/Noise/blob/9dc4b05a6e2fbb390f2bc1d47998d02fdc651827/Racket/noise-serde-lib/private/callout.rkt"&gt;the Racket side&lt;/a&gt;
there is a syntactic form for declaring callouts:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define-callout (hello-cb [name : String] [age : Varint]))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The callouts themselves can have arbitrary arguments and the data is
automatically serialized when a callout procedure is executed, which
is why the callback handler's type contains a size and a data pointer
in addition to the callback id.  We'll get to how this works on the
Swift side toward the end of the article.&lt;/p&gt;&lt;p&gt;The &lt;a href="https://github.com/Bogdanp/Noise/blob/9dc4b05a6e2fbb390f2bc1d47998d02fdc651827/Racket/noise-serde-lib/private/callout.rkt"&gt;implementation of define-callout&lt;/a&gt; is fairly
straightforward.  It starts with the well-known signature for callout
handlers (the prototype of &lt;code&gt;callbackHandler&lt;/code&gt;):&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define callout-type
  (_func _int _size _bytes -&amp;gt; _void))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Following that, there are some structure definitions to keep track of
the callout metadata at runtime, a global registry for this metadata
(indexed by callout id), and a helper function to perform the
callouts:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(struct callout-arg (name type))
(struct callout-info ([id #:mutable] name args cbox))
(define callout-infos (make-hasheqv))

(define (do-callout info arg-pairs)
  (define id (callout-info-id info))
  (define cbox (callout-info-cbox info))
  (define bs
    (call-with-output-bytes
     (lambda (out)
       (for ([p (in-list arg-pairs)])
         (write-field (car p) (cdr p) out)))))
  (cbox id (bytes-length bs) bs))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;arg-pairs&lt;/code&gt; argument to &lt;code&gt;do-callout&lt;/code&gt; is a list of &lt;code&gt;cons&lt;/code&gt; pairs
that contains the serializable type of each argument and the
argument's runtime value.  It takes those arguments, serializes them
into a byte string and then executes the callout using the callout's
id and the serialized data.&lt;/p&gt;&lt;p&gt;The definition of &lt;code&gt;define-callout&lt;/code&gt; itself is as follows (with a couple
small details simplified):&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define-syntax (define-callout stx)
  (syntax-parse stx
    #:literals (:)
    [(_ (name:id [arg-name:id : arg-type:expr] ...+))
     #:fail-unless (valid-name-stx? #'name)
     "callout names may only contain alphanumeric characters, dashes and underscores"
     #'(begin
         (define (name arg-name ...) ;; 1
           (do-callout info (list (cons (-&amp;gt;field-type 'Callout arg-type) arg-name) ...)))
         (define args
           (for/list ([n (in-list (list 'arg-name ...))]
                      [t (in-list (list arg-type ...))])
             (callout-arg n (-&amp;gt;field-type 'Callout t))))
         (define cbox
           (make-callout-box callout-type))
         (define info ;; 2
           (callout-info #f 'name args cbox))
         (hash-set! callout-infos (next-callout-id!) info) ;; 3
         )]))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Every use of &lt;code&gt;define-callout&lt;/code&gt; expands to a definition of a procedure
with the given name (1) that delegates to the &lt;code&gt;do-callout&lt;/code&gt; helper
when it itself is called and a metadata definition for the callout
that is registered with the global registry (2, 3).&lt;/p&gt;&lt;p&gt;Finally, the RPC to install these callbacks that I mentioned at the
end of the first section looks like this:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define-rpc (install-callback [internalWithId id : UVarint]
                              [andAddr addr : Varint])
  (define cbox (callout-info-cbox (hash-ref callout-infos id)))
  (callout-box-install! cbox addr))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When applied, it looks up the runtime info for the callout with the
given id, extracts its box and installs the procedure at that address
into the box.&lt;/p&gt;&lt;h2&gt;High Level Bits&lt;/h2&gt;&lt;p&gt;You might be wondering why the &lt;code&gt;callout-info&lt;/code&gt; needs to remember the
argument types for the callout, since we never used them again above.
This leads us to the final piece of this system, namely the code
generation part.&lt;/p&gt;&lt;p&gt;Noise generates Swift code to handle data type serialization and
deserialization, RPCs and, now, callouts.  To do this, it uses that
same runtime callout metadata described above.&lt;/p&gt;&lt;p&gt;When generating the &lt;code&gt;Backend&lt;/code&gt; class for a project, it produces methods
for all the RPCs and then it turns to callouts, the code for which
looks like this:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define sorted-callout-ids (sort (hash-keys callout-infos) &amp;lt;))
(for ([id (in-list sorted-callout-ids)])
  (match-define (callout-info _ name args _cbox)
    (hash-ref callout-infos id))
  (define proc-name (~name name))
  (define proc-type
    (format "@escaping (~a) -&amp;gt; Void"
            (string-join
             (map (compose1 swift-type callout-arg-type) args)
             ", ")))
  (fprintf out "~n")
  (fprintf out "  public func installCallback(~a proc: ~a) -&amp;gt; Future&amp;lt;String, Void&amp;gt; {~n" proc-name proc-type)
  (fprintf out "    return NoiseBackend.installCallback(id: ~a, rpc: self.installCallback(internalWithId:andAddr:)) { inp in~n" id)
  (fprintf out "      var buf = Data(count: 8*1024)~n")
  (fprintf out "      proc(~n")
  (define last-idx (sub1 (length args)))
  (for ([(arg idx) (in-indexed (in-list args))])
    (match-define (callout-arg _name type) arg)
    (define maybe-comma (if (= idx last-idx) "" ","))
    (fprintf out "        ~a.read(from: inp, using: &amp;amp;buf)~a~n" (swift-type type) maybe-comma))
  (fprintf out "      )~n")
  (fprintf out "    }~n")
  (fprintf out "  }~n"))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That is, for every known callout, it generates a Swift method named
&lt;code&gt;installCallback(calloutName:)&lt;/code&gt;.  To give a concrete example, here is
what the installer for the &lt;code&gt;hello-cb&lt;/code&gt; example from earlier in this
article would look like:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-swift"&gt;public func installCallback(helloCb proc: @escaping (String, Varint) -&amp;gt; Void) -&amp;gt; Future&amp;lt;String, Void&amp;gt; {
  return NoiseBackend.installCallback(id: 0, rpc: self.installCallback(internalWithId:andAddr:)) { inp in
    var buf = Data(count: 8*1024)
    proc(
      String.read(from: inp, using: &amp;amp;buf),
      Varint.read(from: inp, using: &amp;amp;buf)
    )
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Which you would use from the Swift side like so:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-swift"&gt;Backend.shared.installCallback(helloCb: { name, age in
  print("hello \(name), I hear you're \(age) years old!")
})
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And you would call from the Racket side like so:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(hello-cb "Bogdan" 30)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And you wouldn't need to worry about most of the details I've written
about above.&lt;/p&gt;&lt;section class="footnotes"&gt;&lt;ol&gt;&lt;li id="fn_1"&gt;&lt;p&gt;&lt;a href="https://github.com/samdphillips"&gt;Sam Phillips&lt;/a&gt; pointed out on the Racket Discord that there is
actually a helper for this in &lt;code&gt;ffi-lib&lt;/code&gt;, namely &lt;a href="https://docs.racket-lang.org/foreign/Miscellaneous_Support.html#%28def._%28%28lib._ffi%2Funsafe..rkt%29._cast%29%29"&gt;&lt;code&gt;cast&lt;/code&gt;&lt;/a&gt;.  So this
&lt;code&gt;let&lt;/code&gt; can be replaced with &lt;code&gt;(cast addr _intptr add1-type)&lt;/code&gt;. &lt;a class="footnote-backref" href="#fnref_1_1" title="Jump to reference" aria-label="Jump to reference"&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/section&gt;&lt;/article&gt;</description></item><item><title>This is Fine</title><link>https://defn.io/2022/12/18/this-is-fine</link><guid>https://defn.io/2022/12/18/this-is-fine</guid><pubDate>Sun, 18 Dec 2022 15:25:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;&lt;i&gt;This post is a dumb rant, but I needed to vent.&lt;/i&gt;&lt;/p&gt;&lt;p&gt;As I often do when I'm bored or procrastinating, I decided to update
some of the software on my machine today.  As usual, this was a
mistake.&lt;/p&gt;&lt;p&gt;I ran the following command:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ sudo port upgrade yubikey-manager ykpers
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And its output was:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;---&amp;gt;  Computing dependencies for py310-semantic_version
---&amp;gt;  Fetching archive for py310-semantic_version
---&amp;gt;  Attempting to fetch py310-semantic_version-2.10.0_0.darwin_any.noarch.tbz2 from https://packages.macports.org/py310-semantic_version
---&amp;gt;  Attempting to fetch py310-semantic_version-2.10.0_0.darwin_any.noarch.tbz2.rmd160 from https://packages.macports.org/py310-semantic_version
---&amp;gt;  Installing py310-semantic_version @2.10.0_0
---&amp;gt;  Activating py310-semantic_version @2.10.0_0
---&amp;gt;  Cleaning py310-semantic_version
---&amp;gt;  Computing dependencies for py310-typing_extensions
---&amp;gt;  Fetching archive for py310-typing_extensions
---&amp;gt;  Attempting to fetch py310-typing_extensions-4.4.0_0.darwin_any.noarch.tbz2 from https://packages.macports.org/py310-typing_extensions
---&amp;gt;  Attempting to fetch py310-typing_extensions-4.4.0_0.darwin_any.noarch.tbz2.rmd160 from https://packages.macports.org/py310-typing_extensions
---&amp;gt;  Installing py310-typing_extensions @4.4.0_0
---&amp;gt;  Activating py310-typing_extensions @4.4.0_0
---&amp;gt;  Cleaning py310-typing_extensions
---&amp;gt;  Computing dependencies for jemalloc
---&amp;gt;  Fetching archive for jemalloc
---&amp;gt;  Attempting to fetch jemalloc-5.3.0_2.darwin_22.arm64.tbz2 from https://packages.macports.org/jemalloc
---&amp;gt;  Attempting to fetch jemalloc-5.3.0_2.darwin_22.arm64.tbz2 from https://nue.de.packages.macports.org/jemalloc
---&amp;gt;  Attempting to fetch jemalloc-5.3.0_2.darwin_22.arm64.tbz2 from https://mse.uk.packages.macports.org/jemalloc
---&amp;gt;  Fetching distfiles for jemalloc
---&amp;gt;  Attempting to fetch jemalloc-5.3.0.tar.bz2 from https://distfiles.macports.org/jemalloc
---&amp;gt;  Verifying checksums for jemalloc
---&amp;gt;  Extracting jemalloc
---&amp;gt;  Applying patches to jemalloc
---&amp;gt;  Configuring jemalloc
---&amp;gt;  Building jemalloc
---&amp;gt;  Staging jemalloc into destroot
---&amp;gt;  Installing jemalloc @5.3.0_2
---&amp;gt;  Activating jemalloc @5.3.0_2
---&amp;gt;  Cleaning jemalloc
---&amp;gt;  Computing dependencies for libssh2
---&amp;gt;  Fetching archive for libssh2
---&amp;gt;  Attempting to fetch libssh2-1.10.0_0.darwin_22.arm64.tbz2 from https://packages.macports.org/libssh2
---&amp;gt;  Attempting to fetch libssh2-1.10.0_0.darwin_22.arm64.tbz2 from https://nue.de.packages.macports.org/libssh2
---&amp;gt;  Attempting to fetch libssh2-1.10.0_0.darwin_22.arm64.tbz2 from https://mse.uk.packages.macports.org/libssh2
---&amp;gt;  Fetching distfiles for libssh2
---&amp;gt;  Attempting to fetch libssh2-1.10.0.tar.gz from https://distfiles.macports.org/libssh2
---&amp;gt;  Verifying checksums for libssh2
---&amp;gt;  Extracting libssh2
---&amp;gt;  Configuring libssh2
Warning: Configuration logfiles contain indications of -Wimplicit-function-declaration; check that features were not accidentally disabled:
  strchr: found in libssh2-1.10.0/config.log
---&amp;gt;  Building libssh2
---&amp;gt;  Staging libssh2 into destroot
---&amp;gt;  Installing libssh2 @1.10.0_0
---&amp;gt;  Activating libssh2 @1.10.0_0
---&amp;gt;  Cleaning libssh2
---&amp;gt;  Computing dependencies for libgit2
---&amp;gt;  Fetching archive for libgit2
---&amp;gt;  Attempting to fetch libgit2-1.5.0_0+threadsafe.darwin_22.arm64.tbz2 from https://packages.macports.org/libgit2
---&amp;gt;  Attempting to fetch libgit2-1.5.0_0+threadsafe.darwin_22.arm64.tbz2 from https://nue.de.packages.macports.org/libgit2
---&amp;gt;  Attempting to fetch libgit2-1.5.0_0+threadsafe.darwin_22.arm64.tbz2 from https://mse.uk.packages.macports.org/libgit2
---&amp;gt;  Fetching distfiles for libgit2
---&amp;gt;  Attempting to fetch libgit2-1.5.0.tar.gz from https://distfiles.macports.org/libgit2
---&amp;gt;  Verifying checksums for libgit2
---&amp;gt;  Extracting libgit2
---&amp;gt;  Applying patches to libgit2
---&amp;gt;  Configuring libgit2
---&amp;gt;  Building libgit2
---&amp;gt;  Staging libgit2 into destroot
---&amp;gt;  Installing libgit2 @1.5.0_0+threadsafe
---&amp;gt;  Activating libgit2 @1.5.0_0+threadsafe
---&amp;gt;  Cleaning libgit2
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That's more stuff than I'd expect, but whatever.  So far so good...&lt;/p&gt;&lt;pre&gt;&lt;code&gt;---&amp;gt;  Computing dependencies for rust
---&amp;gt;  Fetching archive for rust
---&amp;gt;  Attempting to fetch rust-1.61.0_2.darwin_22.arm64.tbz2 from https://packages.macports.org/rust
---&amp;gt;  Attempting to fetch rust-1.61.0_2.darwin_22.arm64.tbz2 from https://nue.de.packages.macports.org/rust
---&amp;gt;  Attempting to fetch rust-1.61.0_2.darwin_22.arm64.tbz2 from https://mse.uk.packages.macports.org/rust
---&amp;gt;  Fetching distfiles for rust
---&amp;gt;  Attempting to fetch rustc-1.61.0-src.tar.gz from https://static.rust-lang.org/dist
---&amp;gt;  Attempting to fetch rust-std-1.60.0-aarch64-apple-darwin.tar.gz from https://static.rust-lang.org/dist
---&amp;gt;  Attempting to fetch rustc-1.60.0-aarch64-apple-darwin.tar.gz from https://static.rust-lang.org/dist
---&amp;gt;  Attempting to fetch cargo-1.60.0-aarch64-apple-darwin.tar.gz from https://static.rust-lang.org/dist
---&amp;gt;  Attempting to fetch addr2line-0.16.0.crate from https://crates.io/api/v1/crates/addr2line/0.16.0/download?dummy=
---&amp;gt;  Attempting to fetch adler-0.2.3.crate from https://crates.io/api/v1/crates/adler/0.2.3/download?dummy=
---&amp;gt;  Attempting to fetch ahash-0.7.4.crate from https://crates.io/api/v1/crates/ahash/0.7.4/download?dummy=
---&amp;gt;  Attempting to fetch aho-corasick-0.7.18.crate from https://crates.io/api/v1/crates/aho-corasick/0.7.18/download?dummy=
---&amp;gt;  Attempting to fetch ammonia-3.1.3.crate from https://crates.io/api/v1/crates/ammonia/3.1.3/download?dummy=
---&amp;gt;  Attempting to fetch annotate-snippets-0.8.0.crate from https://crates.io/api/v1/crates/annotate-snippets/0.8.0/download?dummy=
---&amp;gt;  Attempting to fetch ansi_term-0.12.1.crate from https://crates.io/api/v1/crates/ansi_term/0.12.1/download?dummy=
---&amp;gt;  Attempting to fetch anyhow-1.0.51.crate from https://crates.io/api/v1/crates/anyhow/1.0.51/download?dummy=
---&amp;gt;  Attempting to fetch array_tool-1.0.3.crate from https://crates.io/api/v1/crates/array_tool/1.0.3/download?dummy=
---&amp;gt;  Attempting to fetch arrayvec-0.7.0.crate from https://crates.io/api/v1/crates/arrayvec/0.7.0/download?dummy=
---&amp;gt;  Attempting to fetch askama-0.11.0.crate from https://crates.io/api/v1/crates/askama/0.11.0/download?dummy=
---&amp;gt;  Attempting to fetch askama_derive-0.11.0.crate from https://crates.io/api/v1/crates/askama_derive/0.11.0/download?dummy=
---&amp;gt;  Attempting to fetch askama_escape-0.10.2.crate from https://crates.io/api/v1/crates/askama_escape/0.10.2/download?dummy=
---&amp;gt;  Attempting to fetch askama_shared-0.12.0.crate from https://crates.io/api/v1/crates/askama_shared/0.12.0/download?dummy=
---&amp;gt;  Attempting to fetch atty-0.2.14.crate from https://crates.io/api/v1/crates/atty/0.2.14/download?dummy=
---&amp;gt;  Attempting to fetch autocfg-1.1.0.crate from https://crates.io/api/v1/crates/autocfg/1.1.0/download?dummy=
---&amp;gt;  Attempting to fetch bitflags-1.2.1.crate from https://crates.io/api/v1/crates/bitflags/1.2.1/download?dummy=
---&amp;gt;  Attempting to fetch bitmaps-2.1.0.crate from https://crates.io/api/v1/crates/bitmaps/2.1.0/download?dummy=
---&amp;gt;  Attempting to fetch block-buffer-0.7.3.crate from https://crates.io/api/v1/crates/block-buffer/0.7.3/download?dummy=
---&amp;gt;  Attempting to fetch block-buffer-0.10.2.crate from https://crates.io/api/v1/crates/block-buffer/0.10.2/download?dummy=
---&amp;gt;  Attempting to fetch block-padding-0.1.5.crate from https://crates.io/api/v1/crates/block-padding/0.1.5/download?dummy=
---&amp;gt;  Attempting to fetch bstr-0.2.13.crate from https://crates.io/api/v1/crates/bstr/0.2.13/download?dummy=
---&amp;gt;  Attempting to fetch byte-tools-0.3.1.crate from https://crates.io/api/v1/crates/byte-tools/0.3.1/download?dummy=
---&amp;gt;  Attempting to fetch bytecount-0.6.2.crate from https://crates.io/api/v1/crates/bytecount/0.6.2/download?dummy=
---&amp;gt;  Attempting to fetch byteorder-1.3.4.crate from https://crates.io/api/v1/crates/byteorder/1.3.4/download?dummy=
---&amp;gt;  Attempting to fetch bytes-1.0.1.crate from https://crates.io/api/v1/crates/bytes/1.0.1/download?dummy=
---&amp;gt;  Attempting to fetch bytesize-1.0.1.crate from https://crates.io/api/v1/crates/bytesize/1.0.1/download?dummy=
---&amp;gt;  Attempting to fetch camino-1.0.5.crate from https://crates.io/api/v1/crates/camino/1.0.5/download?dummy=
---&amp;gt;  Attempting to fetch cargo-platform-0.1.2.crate from https://crates.io/api/v1/crates/cargo-platform/0.1.2/download?dummy=
---&amp;gt;  Attempting to fetch cargo_metadata-0.14.0.crate from https://crates.io/api/v1/crates/cargo_metadata/0.14.0/download?dummy=
---&amp;gt;  Attempting to fetch cc-1.0.69.crate from https://crates.io/api/v1/crates/cc/1.0.69/download?dummy=
---&amp;gt;  Attempting to fetch cfg-if-0.1.10.crate from https://crates.io/api/v1/crates/cfg-if/0.1.10/download?dummy=
---&amp;gt;  Attempting to fetch cfg-if-1.0.0.crate from https://crates.io/api/v1/crates/cfg-if/1.0.0/download?dummy=
---&amp;gt;  Attempting to fetch chalk-derive-0.80.0.crate from https://crates.io/api/v1/crates/chalk-derive/0.80.0/download?dummy=
---&amp;gt;  Attempting to fetch chalk-engine-0.80.0.crate from https://crates.io/api/v1/crates/chalk-engine/0.80.0/download?dummy=
---&amp;gt;  Attempting to fetch chalk-ir-0.80.0.crate from https://crates.io/api/v1/crates/chalk-ir/0.80.0/download?dummy=
---&amp;gt;  Attempting to fetch chalk-solve-0.80.0.crate from https://crates.io/api/v1/crates/chalk-solve/0.80.0/download?dummy=
---&amp;gt;  Attempting to fetch chrono-0.4.19.crate from https://crates.io/api/v1/crates/chrono/0.4.19/download?dummy=
---&amp;gt;  Attempting to fetch clap-2.34.0.crate from https://crates.io/api/v1/crates/clap/2.34.0/download?dummy=
---&amp;gt;  Attempting to fetch clap-3.1.1.crate from https://crates.io/api/v1/crates/clap/3.1.1/download?dummy=
---&amp;gt;  Attempting to fetch cmake-0.1.44.crate from https://crates.io/api/v1/crates/cmake/0.1.44/download?dummy=
---&amp;gt;  Attempting to fetch colored-2.0.0.crate from https://crates.io/api/v1/crates/colored/2.0.0/download?dummy=
---&amp;gt;  Attempting to fetch combine-4.6.3.crate from https://crates.io/api/v1/crates/combine/4.6.3/download?dummy=
---&amp;gt;  Attempting to fetch commoncrypto-0.2.0.crate from https://crates.io/api/v1/crates/commoncrypto/0.2.0/download?dummy=
---&amp;gt;  Attempting to fetch commoncrypto-sys-0.2.0.crate from https://crates.io/api/v1/crates/commoncrypto-sys/0.2.0/download?dummy=
---&amp;gt;  Attempting to fetch compiler_builtins-0.1.70.crate from https://crates.io/api/v1/crates/compiler_builtins/0.1.70/download?dummy=
---&amp;gt;  Attempting to fetch compiletest_rs-0.7.1.crate from https://crates.io/api/v1/crates/compiletest_rs/0.7.1/download?dummy=
---&amp;gt;  Attempting to fetch core-foundation-0.9.0.crate from https://crates.io/api/v1/crates/core-foundation/0.9.0/download?dummy=
---&amp;gt;  Attempting to fetch core-foundation-sys-0.8.0.crate from https://crates.io/api/v1/crates/core-foundation-sys/0.8.0/download?dummy=
---&amp;gt;  Attempting to fetch cpufeatures-0.2.1.crate from https://crates.io/api/v1/crates/cpufeatures/0.2.1/download?dummy=
---&amp;gt;  Attempting to fetch crc32fast-1.2.0.crate from https://crates.io/api/v1/crates/crc32fast/1.2.0/download?dummy=
---&amp;gt;  Attempting to fetch crossbeam-channel-0.5.2.crate from https://crates.io/api/v1/crates/crossbeam-channel/0.5.2/download?dummy=
---&amp;gt;  Attempting to fetch crossbeam-deque-0.8.1.crate from https://crates.io/api/v1/crates/crossbeam-deque/0.8.1/download?dummy=
---&amp;gt;  Attempting to fetch crossbeam-epoch-0.9.6.crate from https://crates.io/api/v1/crates/crossbeam-epoch/0.9.6/download?dummy=
---&amp;gt;  Attempting to fetch crossbeam-utils-0.8.6.crate from https://crates.io/api/v1/crates/crossbeam-utils/0.8.6/download?dummy=
---&amp;gt;  Attempting to fetch crypto-common-0.1.2.crate from https://crates.io/api/v1/crates/crypto-common/0.1.2/download?dummy=
---&amp;gt;  Attempting to fetch crypto-hash-0.3.4.crate from https://crates.io/api/v1/crates/crypto-hash/0.3.4/download?dummy=
---&amp;gt;  Attempting to fetch cstr-0.2.8.crate from https://crates.io/api/v1/crates/cstr/0.2.8/download?dummy=
---&amp;gt;  Attempting to fetch ctor-0.1.15.crate from https://crates.io/api/v1/crates/ctor/0.1.15/download?dummy=
---&amp;gt;  Attempting to fetch curl-0.4.41.crate from https://crates.io/api/v1/crates/curl/0.4.41/download?dummy=
---&amp;gt;  Attempting to fetch curl-sys-0.4.51+curl-7.80.0.crate from https://crates.io/api/v1/crates/curl-sys/0.4.51+curl-7.80.0/download?dummy=
---&amp;gt;  Attempting to fetch datafrog-2.0.1.crate from https://crates.io/api/v1/crates/datafrog/2.0.1/download?dummy=
---&amp;gt;  Attempting to fetch derive-new-0.5.8.crate from https://crates.io/api/v1/crates/derive-new/0.5.8/download?dummy=
---&amp;gt;  Attempting to fetch derive_more-0.99.9.crate from https://crates.io/api/v1/crates/derive_more/0.99.9/download?dummy=
---&amp;gt;  Attempting to fetch diff-0.1.12.crate from https://crates.io/api/v1/crates/diff/0.1.12/download?dummy=
---&amp;gt;  Attempting to fetch difference-2.0.0.crate from https://crates.io/api/v1/crates/difference/2.0.0/download?dummy=
---&amp;gt;  Attempting to fetch digest-0.8.1.crate from https://crates.io/api/v1/crates/digest/0.8.1/download?dummy=
---&amp;gt;  Attempting to fetch digest-0.10.2.crate from https://crates.io/api/v1/crates/digest/0.10.2/download?dummy=
---&amp;gt;  Attempting to fetch directories-3.0.2.crate from https://crates.io/api/v1/crates/directories/3.0.2/download?dummy=
---&amp;gt;  Attempting to fetch dirs-2.0.2.crate from https://crates.io/api/v1/crates/dirs/2.0.2/download?dummy=
---&amp;gt;  Attempting to fetch dirs-next-2.0.0.crate from https://crates.io/api/v1/crates/dirs-next/2.0.0/download?dummy=
---&amp;gt;  Attempting to fetch dirs-sys-0.3.6.crate from https://crates.io/api/v1/crates/dirs-sys/0.3.6/download?dummy=
---&amp;gt;  Attempting to fetch dirs-sys-next-0.1.2.crate from https://crates.io/api/v1/crates/dirs-sys-next/0.1.2/download?dummy=
---&amp;gt;  Attempting to fetch dlmalloc-0.2.3.crate from https://crates.io/api/v1/crates/dlmalloc/0.2.3/download?dummy=
---&amp;gt;  Attempting to fetch either-1.6.0.crate from https://crates.io/api/v1/crates/either/1.6.0/download?dummy=
---&amp;gt;  Attempting to fetch elasticlunr-rs-2.3.9.crate from https://crates.io/api/v1/crates/elasticlunr-rs/2.3.9/download?dummy=
---&amp;gt;  Attempting to fetch ena-0.14.0.crate from https://crates.io/api/v1/crates/ena/0.14.0/download?dummy=
---&amp;gt;  Attempting to fetch enum-iterator-0.6.0.crate from https://crates.io/api/v1/crates/enum-iterator/0.6.0/download?dummy=
---&amp;gt;  Attempting to fetch enum-iterator-derive-0.6.0.crate from https://crates.io/api/v1/crates/enum-iterator-derive/0.6.0/download?dummy=
---&amp;gt;  Attempting to fetch env_logger-0.7.1.crate from https://crates.io/api/v1/crates/env_logger/0.7.1/download?dummy=
---&amp;gt;  Attempting to fetch env_logger-0.8.4.crate from https://crates.io/api/v1/crates/env_logger/0.8.4/download?dummy=
---&amp;gt;  Attempting to fetch env_logger-0.9.0.crate from https://crates.io/api/v1/crates/env_logger/0.9.0/download?dummy=
---&amp;gt;  Attempting to fetch expect-test-1.0.1.crate from https://crates.io/api/v1/crates/expect-test/1.0.1/download?dummy=
---&amp;gt;  Attempting to fetch fake-simd-0.1.2.crate from https://crates.io/api/v1/crates/fake-simd/0.1.2/download?dummy=
---&amp;gt;  Attempting to fetch fallible-iterator-0.2.0.crate from https://crates.io/api/v1/crates/fallible-iterator/0.2.0/download?dummy=
---&amp;gt;  Attempting to fetch filetime-0.2.14.crate from https://crates.io/api/v1/crates/filetime/0.2.14/download?dummy=
---&amp;gt;  Attempting to fetch fixedbitset-0.2.0.crate from https://crates.io/api/v1/crates/fixedbitset/0.2.0/download?dummy=
---&amp;gt;  Attempting to fetch flate2-1.0.16.crate from https://crates.io/api/v1/crates/flate2/1.0.16/download?dummy=
---&amp;gt;  Attempting to fetch fnv-1.0.7.crate from https://crates.io/api/v1/crates/fnv/1.0.7/download?dummy=
---&amp;gt;  Attempting to fetch foreign-types-0.3.2.crate from https://crates.io/api/v1/crates/foreign-types/0.3.2/download?dummy=
---&amp;gt;  Attempting to fetch foreign-types-shared-0.1.1.crate from https://crates.io/api/v1/crates/foreign-types-shared/0.1.1/download?dummy=
---&amp;gt;  Attempting to fetch form_urlencoded-1.0.1.crate from https://crates.io/api/v1/crates/form_urlencoded/1.0.1/download?dummy=
---&amp;gt;  Attempting to fetch fortanix-sgx-abi-0.3.3.crate from https://crates.io/api/v1/crates/fortanix-sgx-abi/0.3.3/download?dummy=
---&amp;gt;  Attempting to fetch fs-err-2.5.0.crate from https://crates.io/api/v1/crates/fs-err/2.5.0/download?dummy=
---&amp;gt;  Attempting to fetch fs_extra-1.1.0.crate from https://crates.io/api/v1/crates/fs_extra/1.1.0/download?dummy=
---&amp;gt;  Attempting to fetch fst-0.4.5.crate from https://crates.io/api/v1/crates/fst/0.4.5/download?dummy=
---&amp;gt;  Attempting to fetch futf-0.1.4.crate from https://crates.io/api/v1/crates/futf/0.1.4/download?dummy=
---&amp;gt;  Attempting to fetch futures-0.1.31.crate from https://crates.io/api/v1/crates/futures/0.1.31/download?dummy=
---&amp;gt;  Attempting to fetch futures-0.3.19.crate from https://crates.io/api/v1/crates/futures/0.3.19/download?dummy=
---&amp;gt;  Attempting to fetch futures-channel-0.3.19.crate from https://crates.io/api/v1/crates/futures-channel/0.3.19/download?dummy=
---&amp;gt;  Attempting to fetch futures-core-0.3.19.crate from https://crates.io/api/v1/crates/futures-core/0.3.19/download?dummy=
---&amp;gt;  Attempting to fetch futures-executor-0.3.19.crate from https://crates.io/api/v1/crates/futures-executor/0.3.19/download?dummy=
---&amp;gt;  Attempting to fetch futures-io-0.3.19.crate from https://crates.io/api/v1/crates/futures-io/0.3.19/download?dummy=
---&amp;gt;  Attempting to fetch futures-macro-0.3.19.crate from https://crates.io/api/v1/crates/futures-macro/0.3.19/download?dummy=
---&amp;gt;  Attempting to fetch futures-sink-0.3.19.crate from https://crates.io/api/v1/crates/futures-sink/0.3.19/download?dummy=
---&amp;gt;  Attempting to fetch futures-task-0.3.19.crate from https://crates.io/api/v1/crates/futures-task/0.3.19/download?dummy=
---&amp;gt;  Attempting to fetch futures-util-0.3.19.crate from https://crates.io/api/v1/crates/futures-util/0.3.19/download?dummy=
---&amp;gt;  Attempting to fetch fwdansi-1.1.0.crate from https://crates.io/api/v1/crates/fwdansi/1.1.0/download?dummy=
---&amp;gt;  Attempting to fetch generic-array-0.12.4.crate from https://crates.io/api/v1/crates/generic-array/0.12.4/download?dummy=
---&amp;gt;  Attempting to fetch generic-array-0.14.4.crate from https://crates.io/api/v1/crates/generic-array/0.14.4/download?dummy=
---&amp;gt;  Attempting to fetch getopts-0.2.21.crate from https://crates.io/api/v1/crates/getopts/0.2.21/download?dummy=
---&amp;gt;  Attempting to fetch getrandom-0.1.14.crate from https://crates.io/api/v1/crates/getrandom/0.1.14/download?dummy=
---&amp;gt;  Attempting to fetch getrandom-0.2.0.crate from https://crates.io/api/v1/crates/getrandom/0.2.0/download?dummy=
---&amp;gt;  Attempting to fetch getset-0.1.1.crate from https://crates.io/api/v1/crates/getset/0.1.1/download?dummy=
---&amp;gt;  Attempting to fetch gimli-0.25.0.crate from https://crates.io/api/v1/crates/gimli/0.25.0/download?dummy=
---&amp;gt;  Attempting to fetch gimli-0.26.1.crate from https://crates.io/api/v1/crates/gimli/0.26.1/download?dummy=
---&amp;gt;  Attempting to fetch git2-0.14.2.crate from https://crates.io/api/v1/crates/git2/0.14.2/download?dummy=
---&amp;gt;  Attempting to fetch git2-curl-0.15.0.crate from https://crates.io/api/v1/crates/git2-curl/0.15.0/download?dummy=
---&amp;gt;  Attempting to fetch glob-0.3.0.crate from https://crates.io/api/v1/crates/glob/0.3.0/download?dummy=
---&amp;gt;  Attempting to fetch globset-0.4.5.crate from https://crates.io/api/v1/crates/globset/0.4.5/download?dummy=
---&amp;gt;  Attempting to fetch gsgdt-0.1.2.crate from https://crates.io/api/v1/crates/gsgdt/0.1.2/download?dummy=
---&amp;gt;  Attempting to fetch handlebars-4.1.0.crate from https://crates.io/api/v1/crates/handlebars/4.1.0/download?dummy=
---&amp;gt;  Attempting to fetch hashbrown-0.11.2.crate from https://crates.io/api/v1/crates/hashbrown/0.11.2/download?dummy=
---&amp;gt;  Attempting to fetch hashbrown-0.12.0.crate from https://crates.io/api/v1/crates/hashbrown/0.12.0/download?dummy=
---&amp;gt;  Attempting to fetch heck-0.3.1.crate from https://crates.io/api/v1/crates/heck/0.3.1/download?dummy=
---&amp;gt;  Attempting to fetch hermit-abi-0.1.19.crate from https://crates.io/api/v1/crates/hermit-abi/0.1.19/download?dummy=
---&amp;gt;  Attempting to fetch hermit-abi-0.2.0.crate from https://crates.io/api/v1/crates/hermit-abi/0.2.0/download?dummy=
---&amp;gt;  Attempting to fetch hex-0.3.2.crate from https://crates.io/api/v1/crates/hex/0.3.2/download?dummy=
---&amp;gt;  Attempting to fetch hex-0.4.2.crate from https://crates.io/api/v1/crates/hex/0.4.2/download?dummy=
---&amp;gt;  Attempting to fetch home-0.5.3.crate from https://crates.io/api/v1/crates/home/0.5.3/download?dummy=
---&amp;gt;  Attempting to fetch html5ever-0.25.1.crate from https://crates.io/api/v1/crates/html5ever/0.25.1/download?dummy=
---&amp;gt;  Attempting to fetch humantime-1.3.0.crate from https://crates.io/api/v1/crates/humantime/1.3.0/download?dummy=
---&amp;gt;  Attempting to fetch humantime-2.0.1.crate from https://crates.io/api/v1/crates/humantime/2.0.1/download?dummy=
---&amp;gt;  Attempting to fetch idna-0.1.5.crate from https://crates.io/api/v1/crates/idna/0.1.5/download?dummy=
---&amp;gt;  Attempting to fetch idna-0.2.0.crate from https://crates.io/api/v1/crates/idna/0.2.0/download?dummy=
---&amp;gt;  Attempting to fetch if_chain-1.0.0.crate from https://crates.io/api/v1/crates/if_chain/1.0.0/download?dummy=
---&amp;gt;  Attempting to fetch ignore-0.4.17.crate from https://crates.io/api/v1/crates/ignore/0.4.17/download?dummy=
---&amp;gt;  Attempting to fetch im-rc-15.0.0.crate from https://crates.io/api/v1/crates/im-rc/15.0.0/download?dummy=
---&amp;gt;  Attempting to fetch indexmap-1.8.0.crate from https://crates.io/api/v1/crates/indexmap/1.8.0/download?dummy=
---&amp;gt;  Attempting to fetch indoc-1.0.3.crate from https://crates.io/api/v1/crates/indoc/1.0.3/download?dummy=
---&amp;gt;  Attempting to fetch instant-0.1.12.crate from https://crates.io/api/v1/crates/instant/0.1.12/download?dummy=
---&amp;gt;  Attempting to fetch itertools-0.10.1.crate from https://crates.io/api/v1/crates/itertools/0.10.1/download?dummy=
---&amp;gt;  Attempting to fetch itoa-0.4.6.crate from https://crates.io/api/v1/crates/itoa/0.4.6/download?dummy=
---&amp;gt;  Attempting to fetch jobserver-0.1.24.crate from https://crates.io/api/v1/crates/jobserver/0.1.24/download?dummy=
---&amp;gt;  Attempting to fetch json-0.12.4.crate from https://crates.io/api/v1/crates/json/0.12.4/download?dummy=
---&amp;gt;  Attempting to fetch jsonpath_lib-0.2.6.crate from https://crates.io/api/v1/crates/jsonpath_lib/0.2.6/download?dummy=
---&amp;gt;  Attempting to fetch jsonrpc-client-transports-18.0.0.crate from https://crates.io/api/v1/crates/jsonrpc-client-transports/18.0.0/download?dummy=
---&amp;gt;  Attempting to fetch jsonrpc-core-18.0.0.crate from https://crates.io/api/v1/crates/jsonrpc-core/18.0.0/download?dummy=
---&amp;gt;  Attempting to fetch jsonrpc-core-client-18.0.0.crate from https://crates.io/api/v1/crates/jsonrpc-core-client/18.0.0/download?dummy=
---&amp;gt;  Attempting to fetch jsonrpc-derive-18.0.0.crate from https://crates.io/api/v1/crates/jsonrpc-derive/18.0.0/download?dummy=
---&amp;gt;  Attempting to fetch jsonrpc-ipc-server-18.0.0.crate from https://crates.io/api/v1/crates/jsonrpc-ipc-server/18.0.0/download?dummy=
---&amp;gt;  Attempting to fetch jsonrpc-pubsub-18.0.0.crate from https://crates.io/api/v1/crates/jsonrpc-pubsub/18.0.0/download?dummy=
---&amp;gt;  Attempting to fetch jsonrpc-server-utils-18.0.0.crate from https://crates.io/api/v1/crates/jsonrpc-server-utils/18.0.0/download?dummy=
---&amp;gt;  Attempting to fetch kstring-1.0.6.crate from https://crates.io/api/v1/crates/kstring/1.0.6/download?dummy=
---&amp;gt;  Attempting to fetch lazy_static-1.4.0.crate from https://crates.io/api/v1/crates/lazy_static/1.4.0/download?dummy=
---&amp;gt;  Attempting to fetch lazycell-1.3.0.crate from https://crates.io/api/v1/crates/lazycell/1.3.0/download?dummy=
---&amp;gt;  Attempting to fetch libc-0.2.116.crate from https://crates.io/api/v1/crates/libc/0.2.116/download?dummy=
---&amp;gt;  Attempting to fetch libgit2-sys-0.13.2+1.4.2.crate from https://crates.io/api/v1/crates/libgit2-sys/0.13.2+1.4.2/download?dummy=
---&amp;gt;  Attempting to fetch libloading-0.7.1.crate from https://crates.io/api/v1/crates/libloading/0.7.1/download?dummy=
---&amp;gt;  Attempting to fetch libm-0.1.4.crate from https://crates.io/api/v1/crates/libm/0.1.4/download?dummy=
---&amp;gt;  Attempting to fetch libnghttp2-sys-0.1.4+1.41.0.crate from https://crates.io/api/v1/crates/libnghttp2-sys/0.1.4+1.41.0/download?dummy=
---&amp;gt;  Attempting to fetch libssh2-sys-0.2.23.crate from https://crates.io/api/v1/crates/libssh2-sys/0.2.23/download?dummy=
---&amp;gt;  Attempting to fetch libz-sys-1.1.3.crate from https://crates.io/api/v1/crates/libz-sys/1.1.3/download?dummy=
---&amp;gt;  Attempting to fetch linked-hash-map-0.5.4.crate from https://crates.io/api/v1/crates/linked-hash-map/0.5.4/download?dummy=
---&amp;gt;  Attempting to fetch lock_api-0.4.5.crate from https://crates.io/api/v1/crates/lock_api/0.4.5/download?dummy=
---&amp;gt;  Attempting to fetch log-0.4.14.crate from https://crates.io/api/v1/crates/log/0.4.14/download?dummy=
---&amp;gt;  Attempting to fetch lsp-codec-0.3.0.crate from https://crates.io/api/v1/crates/lsp-codec/0.3.0/download?dummy=
---&amp;gt;  Attempting to fetch lsp-types-0.60.0.crate from https://crates.io/api/v1/crates/lsp-types/0.60.0/download?dummy=
---&amp;gt;  Attempting to fetch lzma-sys-0.1.16.crate from https://crates.io/api/v1/crates/lzma-sys/0.1.16/download?dummy=
---&amp;gt;  Attempting to fetch mac-0.1.1.crate from https://crates.io/api/v1/crates/mac/0.1.1/download?dummy=
---&amp;gt;  Attempting to fetch macro-utils-0.1.3.crate from https://crates.io/api/v1/crates/macro-utils/0.1.3/download?dummy=
---&amp;gt;  Attempting to fetch maplit-1.0.2.crate from https://crates.io/api/v1/crates/maplit/1.0.2/download?dummy=
---&amp;gt;  Attempting to fetch markup5ever-0.10.1.crate from https://crates.io/api/v1/crates/markup5ever/0.10.1/download?dummy=
---&amp;gt;  Attempting to fetch markup5ever_rcdom-0.1.0.crate from https://crates.io/api/v1/crates/markup5ever_rcdom/0.1.0/download?dummy=
---&amp;gt;  Attempting to fetch matchers-0.1.0.crate from https://crates.io/api/v1/crates/matchers/0.1.0/download?dummy=
---&amp;gt;  Attempting to fetch matches-0.1.8.crate from https://crates.io/api/v1/crates/matches/0.1.8/download?dummy=
---&amp;gt;  Attempting to fetch md-5-0.10.0.crate from https://crates.io/api/v1/crates/md-5/0.10.0/download?dummy=
---&amp;gt;  Attempting to fetch mdbook-0.4.15.crate from https://crates.io/api/v1/crates/mdbook/0.4.15/download?dummy=
---&amp;gt;  Attempting to fetch measureme-9.1.2.crate from https://crates.io/api/v1/crates/measureme/9.1.2/download?dummy=
---&amp;gt;  Attempting to fetch measureme-10.0.0.crate from https://crates.io/api/v1/crates/measureme/10.0.0/download?dummy=
---&amp;gt;  Attempting to fetch memchr-2.4.1.crate from https://crates.io/api/v1/crates/memchr/2.4.1/download?dummy=
---&amp;gt;  Attempting to fetch memmap2-0.2.1.crate from https://crates.io/api/v1/crates/memmap2/0.2.1/download?dummy=
---&amp;gt;  Attempting to fetch memoffset-0.6.5.crate from https://crates.io/api/v1/crates/memoffset/0.6.5/download?dummy=
---&amp;gt;  Attempting to fetch minifier-0.0.43.crate from https://crates.io/api/v1/crates/minifier/0.0.43/download?dummy=
---&amp;gt;  Attempting to fetch minimal-lexical-0.2.1.crate from https://crates.io/api/v1/crates/minimal-lexical/0.2.1/download?dummy=
---&amp;gt;  Attempting to fetch miniz_oxide-0.4.0.crate from https://crates.io/api/v1/crates/miniz_oxide/0.4.0/download?dummy=
---&amp;gt;  Attempting to fetch mio-0.7.14.crate from https://crates.io/api/v1/crates/mio/0.7.14/download?dummy=
---&amp;gt;  Attempting to fetch miow-0.3.7.crate from https://crates.io/api/v1/crates/miow/0.3.7/download?dummy=
---&amp;gt;  Attempting to fetch new_debug_unreachable-1.0.4.crate from https://crates.io/api/v1/crates/new_debug_unreachable/1.0.4/download?dummy=
---&amp;gt;  Attempting to fetch nom-7.1.0.crate from https://crates.io/api/v1/crates/nom/7.1.0/download?dummy=
---&amp;gt;  Attempting to fetch ntapi-0.3.6.crate from https://crates.io/api/v1/crates/ntapi/0.3.6/download?dummy=
---&amp;gt;  Attempting to fetch num-integer-0.1.43.crate from https://crates.io/api/v1/crates/num-integer/0.1.43/download?dummy=
---&amp;gt;  Attempting to fetch num-traits-0.2.12.crate from https://crates.io/api/v1/crates/num-traits/0.2.12/download?dummy=
---&amp;gt;  Attempting to fetch num_cpus-1.13.1.crate from https://crates.io/api/v1/crates/num_cpus/1.13.1/download?dummy=
---&amp;gt;  Attempting to fetch object-0.26.2.crate from https://crates.io/api/v1/crates/object/0.26.2/download?dummy=
---&amp;gt;  Attempting to fetch object-0.28.1.crate from https://crates.io/api/v1/crates/object/0.28.1/download?dummy=
---&amp;gt;  Attempting to fetch odht-0.3.1.crate from https://crates.io/api/v1/crates/odht/0.3.1/download?dummy=
---&amp;gt;  Attempting to fetch once_cell-1.7.2.crate from https://crates.io/api/v1/crates/once_cell/1.7.2/download?dummy=
---&amp;gt;  Attempting to fetch opaque-debug-0.2.3.crate from https://crates.io/api/v1/crates/opaque-debug/0.2.3/download?dummy=
---&amp;gt;  Attempting to fetch opener-0.5.0.crate from https://crates.io/api/v1/crates/opener/0.5.0/download?dummy=
---&amp;gt;  Attempting to fetch openssl-0.10.35.crate from https://crates.io/api/v1/crates/openssl/0.10.35/download?dummy=
---&amp;gt;  Attempting to fetch openssl-probe-0.1.2.crate from https://crates.io/api/v1/crates/openssl-probe/0.1.2/download?dummy=
---&amp;gt;  Attempting to fetch openssl-src-111.17.0+1.1.1m.crate from https://crates.io/api/v1/crates/openssl-src/111.17.0+1.1.1m/download?dummy=
---&amp;gt;  Attempting to fetch openssl-sys-0.9.65.crate from https://crates.io/api/v1/crates/openssl-sys/0.9.65/download?dummy=
---&amp;gt;  Attempting to fetch ordslice-0.3.0.crate from https://crates.io/api/v1/crates/ordslice/0.3.0/download?dummy=
---&amp;gt;  Attempting to fetch os_info-3.0.7.crate from https://crates.io/api/v1/crates/os_info/3.0.7/download?dummy=
---&amp;gt;  Attempting to fetch os_str_bytes-6.0.0.crate from https://crates.io/api/v1/crates/os_str_bytes/6.0.0/download?dummy=
---&amp;gt;  Attempting to fetch output_vt100-0.1.2.crate from https://crates.io/api/v1/crates/output_vt100/0.1.2/download?dummy=
---&amp;gt;  Attempting to fetch packed_simd_2-0.3.4.crate from https://crates.io/api/v1/crates/packed_simd_2/0.3.4/download?dummy=
---&amp;gt;  Attempting to fetch parity-tokio-ipc-0.9.0.crate from https://crates.io/api/v1/crates/parity-tokio-ipc/0.9.0/download?dummy=
---&amp;gt;  Attempting to fetch parking_lot-0.11.2.crate from https://crates.io/api/v1/crates/parking_lot/0.11.2/download?dummy=
---&amp;gt;  Attempting to fetch parking_lot_core-0.8.5.crate from https://crates.io/api/v1/crates/parking_lot_core/0.8.5/download?dummy=
---&amp;gt;  Attempting to fetch pathdiff-0.2.0.crate from https://crates.io/api/v1/crates/pathdiff/0.2.0/download?dummy=
---&amp;gt;  Attempting to fetch percent-encoding-1.0.1.crate from https://crates.io/api/v1/crates/percent-encoding/1.0.1/download?dummy=
---&amp;gt;  Attempting to fetch percent-encoding-2.1.0.crate from https://crates.io/api/v1/crates/percent-encoding/2.1.0/download?dummy=
---&amp;gt;  Attempting to fetch perf-event-open-sys-1.0.1.crate from https://crates.io/api/v1/crates/perf-event-open-sys/1.0.1/download?dummy=
---&amp;gt;  Attempting to fetch pest-2.1.3.crate from https://crates.io/api/v1/crates/pest/2.1.3/download?dummy=
---&amp;gt;  Attempting to fetch pest_derive-2.1.0.crate from https://crates.io/api/v1/crates/pest_derive/2.1.0/download?dummy=
---&amp;gt;  Attempting to fetch pest_generator-2.1.3.crate from https://crates.io/api/v1/crates/pest_generator/2.1.3/download?dummy=
---&amp;gt;  Attempting to fetch pest_meta-2.1.3.crate from https://crates.io/api/v1/crates/pest_meta/2.1.3/download?dummy=
---&amp;gt;  Attempting to fetch petgraph-0.5.1.crate from https://crates.io/api/v1/crates/petgraph/0.5.1/download?dummy=
---&amp;gt;  Attempting to fetch phf-0.8.0.crate from https://crates.io/api/v1/crates/phf/0.8.0/download?dummy=
---&amp;gt;  Attempting to fetch phf_codegen-0.8.0.crate from https://crates.io/api/v1/crates/phf_codegen/0.8.0/download?dummy=
---&amp;gt;  Attempting to fetch phf_generator-0.8.0.crate from https://crates.io/api/v1/crates/phf_generator/0.8.0/download?dummy=
---&amp;gt;  Attempting to fetch phf_shared-0.8.0.crate from https://crates.io/api/v1/crates/phf_shared/0.8.0/download?dummy=
---&amp;gt;  Attempting to fetch pin-project-lite-0.2.8.crate from https://crates.io/api/v1/crates/pin-project-lite/0.2.8/download?dummy=
---&amp;gt;  Attempting to fetch pin-utils-0.1.0.crate from https://crates.io/api/v1/crates/pin-utils/0.1.0/download?dummy=
---&amp;gt;  Attempting to fetch pkg-config-0.3.18.crate from https://crates.io/api/v1/crates/pkg-config/0.3.18/download?dummy=
---&amp;gt;  Attempting to fetch polonius-engine-0.13.0.crate from https://crates.io/api/v1/crates/polonius-engine/0.13.0/download?dummy=
---&amp;gt;  Attempting to fetch ppv-lite86-0.2.8.crate from https://crates.io/api/v1/crates/ppv-lite86/0.2.8/download?dummy=
---&amp;gt;  Attempting to fetch precomputed-hash-0.1.1.crate from https://crates.io/api/v1/crates/precomputed-hash/0.1.1/download?dummy=
---&amp;gt;  Attempting to fetch pretty_assertions-0.7.2.crate from https://crates.io/api/v1/crates/pretty_assertions/0.7.2/download?dummy=
---&amp;gt;  Attempting to fetch pretty_env_logger-0.4.0.crate from https://crates.io/api/v1/crates/pretty_env_logger/0.4.0/download?dummy=
---&amp;gt;  Attempting to fetch proc-macro-crate-0.1.5.crate from https://crates.io/api/v1/crates/proc-macro-crate/0.1.5/download?dummy=
---&amp;gt;  Attempting to fetch proc-macro-error-1.0.4.crate from https://crates.io/api/v1/crates/proc-macro-error/1.0.4/download?dummy=
---&amp;gt;  Attempting to fetch proc-macro-error-attr-1.0.4.crate from https://crates.io/api/v1/crates/proc-macro-error-attr/1.0.4/download?dummy=
---&amp;gt;  Attempting to fetch proc-macro2-1.0.30.crate from https://crates.io/api/v1/crates/proc-macro2/1.0.30/download?dummy=
---&amp;gt;  Attempting to fetch psm-0.1.16.crate from https://crates.io/api/v1/crates/psm/0.1.16/download?dummy=
---&amp;gt;  Attempting to fetch pulldown-cmark-0.9.1.crate from https://crates.io/api/v1/crates/pulldown-cmark/0.9.1/download?dummy=
---&amp;gt;  Attempting to fetch punycode-0.4.1.crate from https://crates.io/api/v1/crates/punycode/0.4.1/download?dummy=
---&amp;gt;  Attempting to fetch quick-error-1.2.3.crate from https://crates.io/api/v1/crates/quick-error/1.2.3/download?dummy=
---&amp;gt;  Attempting to fetch quick-error-2.0.0.crate from https://crates.io/api/v1/crates/quick-error/2.0.0/download?dummy=
---&amp;gt;  Attempting to fetch quine-mc_cluskey-0.2.4.crate from https://crates.io/api/v1/crates/quine-mc_cluskey/0.2.4/download?dummy=
---&amp;gt;  Attempting to fetch quote-1.0.7.crate from https://crates.io/api/v1/crates/quote/1.0.7/download?dummy=
---&amp;gt;  Attempting to fetch racer-2.2.1.crate from https://crates.io/api/v1/crates/racer/2.2.1/download?dummy=
---&amp;gt;  Attempting to fetch rand-0.7.3.crate from https://crates.io/api/v1/crates/rand/0.7.3/download?dummy=
---&amp;gt;  Attempting to fetch rand-0.8.4.crate from https://crates.io/api/v1/crates/rand/0.8.4/download?dummy=
---&amp;gt;  Attempting to fetch rand_chacha-0.2.2.crate from https://crates.io/api/v1/crates/rand_chacha/0.2.2/download?dummy=
---&amp;gt;  Attempting to fetch rand_chacha-0.3.0.crate from https://crates.io/api/v1/crates/rand_chacha/0.3.0/download?dummy=
---&amp;gt;  Attempting to fetch rand_core-0.5.1.crate from https://crates.io/api/v1/crates/rand_core/0.5.1/download?dummy=
---&amp;gt;  Attempting to fetch rand_core-0.6.2.crate from https://crates.io/api/v1/crates/rand_core/0.6.2/download?dummy=
---&amp;gt;  Attempting to fetch rand_hc-0.2.0.crate from https://crates.io/api/v1/crates/rand_hc/0.2.0/download?dummy=
---&amp;gt;  Attempting to fetch rand_hc-0.3.0.crate from https://crates.io/api/v1/crates/rand_hc/0.3.0/download?dummy=
---&amp;gt;  Attempting to fetch rand_pcg-0.2.1.crate from https://crates.io/api/v1/crates/rand_pcg/0.2.1/download?dummy=
---&amp;gt;  Attempting to fetch rand_xorshift-0.2.0.crate from https://crates.io/api/v1/crates/rand_xorshift/0.2.0/download?dummy=
---&amp;gt;  Attempting to fetch rand_xoshiro-0.4.0.crate from https://crates.io/api/v1/crates/rand_xoshiro/0.4.0/download?dummy=
---&amp;gt;  Attempting to fetch rand_xoshiro-0.6.0.crate from https://crates.io/api/v1/crates/rand_xoshiro/0.6.0/download?dummy=
---&amp;gt;  Attempting to fetch rayon-1.5.1.crate from https://crates.io/api/v1/crates/rayon/1.5.1/download?dummy=
---&amp;gt;  Attempting to fetch rayon-core-1.9.1.crate from https://crates.io/api/v1/crates/rayon-core/1.9.1/download?dummy=
---&amp;gt;  Attempting to fetch redox_syscall-0.2.10.crate from https://crates.io/api/v1/crates/redox_syscall/0.2.10/download?dummy=
---&amp;gt;  Attempting to fetch redox_users-0.4.0.crate from https://crates.io/api/v1/crates/redox_users/0.4.0/download?dummy=
---&amp;gt;  Attempting to fetch regex-1.5.4.crate from https://crates.io/api/v1/crates/regex/1.5.4/download?dummy=
---&amp;gt;  Attempting to fetch regex-automata-0.1.10.crate from https://crates.io/api/v1/crates/regex-automata/0.1.10/download?dummy=
---&amp;gt;  Attempting to fetch regex-syntax-0.6.25.crate from https://crates.io/api/v1/crates/regex-syntax/0.6.25/download?dummy=
---&amp;gt;  Attempting to fetch remove_dir_all-0.5.3.crate from https://crates.io/api/v1/crates/remove_dir_all/0.5.3/download?dummy=
---&amp;gt;  Attempting to fetch rls-data-0.19.1.crate from https://crates.io/api/v1/crates/rls-data/0.19.1/download?dummy=
---&amp;gt;  Attempting to fetch rls-span-0.5.3.crate from https://crates.io/api/v1/crates/rls-span/0.5.3/download?dummy=
---&amp;gt;  Attempting to fetch rls-vfs-0.8.0.crate from https://crates.io/api/v1/crates/rls-vfs/0.8.0/download?dummy=
---&amp;gt;  Attempting to fetch rustc-demangle-0.1.21.crate from https://crates.io/api/v1/crates/rustc-demangle/0.1.21/download?dummy=
---&amp;gt;  Attempting to fetch rustc-hash-1.1.0.crate from https://crates.io/api/v1/crates/rustc-hash/1.1.0/download?dummy=
---&amp;gt;  Attempting to fetch rustc-rayon-0.3.2.crate from https://crates.io/api/v1/crates/rustc-rayon/0.3.2/download?dummy=
---&amp;gt;  Attempting to fetch rustc-rayon-core-0.3.2.crate from https://crates.io/api/v1/crates/rustc-rayon-core/0.3.2/download?dummy=
---&amp;gt;  Attempting to fetch rustc-semver-1.1.0.crate from https://crates.io/api/v1/crates/rustc-semver/1.1.0/download?dummy=
---&amp;gt;  Attempting to fetch rustc_tools_util-0.2.0.crate from https://crates.io/api/v1/crates/rustc_tools_util/0.2.0/download?dummy=
---&amp;gt;  Attempting to fetch rustc_version-0.4.0.crate from https://crates.io/api/v1/crates/rustc_version/0.4.0/download?dummy=
---&amp;gt;  Attempting to fetch rustfix-0.5.1.crate from https://crates.io/api/v1/crates/rustfix/0.5.1/download?dummy=
---&amp;gt;  Attempting to fetch rustfix-0.6.0.crate from https://crates.io/api/v1/crates/rustfix/0.6.0/download?dummy=
---&amp;gt;  Attempting to fetch rustversion-1.0.5.crate from https://crates.io/api/v1/crates/rustversion/1.0.5/download?dummy=
---&amp;gt;  Attempting to fetch ryu-1.0.5.crate from https://crates.io/api/v1/crates/ryu/1.0.5/download?dummy=
---&amp;gt;  Attempting to fetch same-file-1.0.6.crate from https://crates.io/api/v1/crates/same-file/1.0.6/download?dummy=
---&amp;gt;  Attempting to fetch schannel-0.1.19.crate from https://crates.io/api/v1/crates/schannel/0.1.19/download?dummy=
---&amp;gt;  Attempting to fetch scoped-tls-1.0.0.crate from https://crates.io/api/v1/crates/scoped-tls/1.0.0/download?dummy=
---&amp;gt;  Attempting to fetch scopeguard-1.1.0.crate from https://crates.io/api/v1/crates/scopeguard/1.1.0/download?dummy=
---&amp;gt;  Attempting to fetch security-framework-2.0.0.crate from https://crates.io/api/v1/crates/security-framework/2.0.0/download?dummy=
---&amp;gt;  Attempting to fetch security-framework-sys-2.0.0.crate from https://crates.io/api/v1/crates/security-framework-sys/2.0.0/download?dummy=
---&amp;gt;  Attempting to fetch semver-1.0.3.crate from https://crates.io/api/v1/crates/semver/1.0.3/download?dummy=
---&amp;gt;  Attempting to fetch serde-1.0.125.crate from https://crates.io/api/v1/crates/serde/1.0.125/download?dummy=
---&amp;gt;  Attempting to fetch serde_derive-1.0.125.crate from https://crates.io/api/v1/crates/serde_derive/1.0.125/download?dummy=
---&amp;gt;  Attempting to fetch serde_ignored-0.1.2.crate from https://crates.io/api/v1/crates/serde_ignored/0.1.2/download?dummy=
---&amp;gt;  Attempting to fetch serde_json-1.0.59.crate from https://crates.io/api/v1/crates/serde_json/1.0.59/download?dummy=
---&amp;gt;  Attempting to fetch serde_repr-0.1.6.crate from https://crates.io/api/v1/crates/serde_repr/0.1.6/download?dummy=
---&amp;gt;  Attempting to fetch sha-1-0.8.2.crate from https://crates.io/api/v1/crates/sha-1/0.8.2/download?dummy=
---&amp;gt;  Attempting to fetch sha-1-0.10.0.crate from https://crates.io/api/v1/crates/sha-1/0.10.0/download?dummy=
---&amp;gt;  Attempting to fetch sha2-0.10.1.crate from https://crates.io/api/v1/crates/sha2/0.10.1/download?dummy=
---&amp;gt;  Attempting to fetch sharded-slab-0.1.1.crate from https://crates.io/api/v1/crates/sharded-slab/0.1.1/download?dummy=
---&amp;gt;  Attempting to fetch shell-escape-0.1.5.crate from https://crates.io/api/v1/crates/shell-escape/0.1.5/download?dummy=
---&amp;gt;  Attempting to fetch shlex-1.0.0.crate from https://crates.io/api/v1/crates/shlex/1.0.0/download?dummy=
---&amp;gt;  Attempting to fetch signal-hook-registry-1.2.2.crate from https://crates.io/api/v1/crates/signal-hook-registry/1.2.2/download?dummy=
---&amp;gt;  Attempting to fetch siphasher-0.3.3.crate from https://crates.io/api/v1/crates/siphasher/0.3.3/download?dummy=
---&amp;gt;  Attempting to fetch sized-chunks-0.6.4.crate from https://crates.io/api/v1/crates/sized-chunks/0.6.4/download?dummy=
---&amp;gt;  Attempting to fetch slab-0.4.2.crate from https://crates.io/api/v1/crates/slab/0.4.2/download?dummy=
---&amp;gt;  Attempting to fetch smallvec-1.7.0.crate from https://crates.io/api/v1/crates/smallvec/1.7.0/download?dummy=
---&amp;gt;  Attempting to fetch snap-1.0.1.crate from https://crates.io/api/v1/crates/snap/1.0.1/download?dummy=
---&amp;gt;  Attempting to fetch socket2-0.4.1.crate from https://crates.io/api/v1/crates/socket2/0.4.1/download?dummy=
---&amp;gt;  Attempting to fetch stable_deref_trait-1.2.0.crate from https://crates.io/api/v1/crates/stable_deref_trait/1.2.0/download?dummy=
---&amp;gt;  Attempting to fetch stacker-0.1.14.crate from https://crates.io/api/v1/crates/stacker/0.1.14/download?dummy=
---&amp;gt;  Attempting to fetch string_cache-0.8.0.crate from https://crates.io/api/v1/crates/string_cache/0.8.0/download?dummy=
---&amp;gt;  Attempting to fetch string_cache_codegen-0.5.1.crate from https://crates.io/api/v1/crates/string_cache_codegen/0.5.1/download?dummy=
---&amp;gt;  Attempting to fetch strip-ansi-escapes-0.1.0.crate from https://crates.io/api/v1/crates/strip-ansi-escapes/0.1.0/download?dummy=
---&amp;gt;  Attempting to fetch strsim-0.8.0.crate from https://crates.io/api/v1/crates/strsim/0.8.0/download?dummy=
---&amp;gt;  Attempting to fetch strsim-0.10.0.crate from https://crates.io/api/v1/crates/strsim/0.10.0/download?dummy=
---&amp;gt;  Attempting to fetch structopt-0.3.25.crate from https://crates.io/api/v1/crates/structopt/0.3.25/download?dummy=
---&amp;gt;  Attempting to fetch structopt-derive-0.4.18.crate from https://crates.io/api/v1/crates/structopt-derive/0.4.18/download?dummy=
---&amp;gt;  Attempting to fetch strum-0.18.0.crate from https://crates.io/api/v1/crates/strum/0.18.0/download?dummy=
---&amp;gt;  Attempting to fetch strum_macros-0.18.0.crate from https://crates.io/api/v1/crates/strum_macros/0.18.0/download?dummy=
---&amp;gt;  Attempting to fetch syn-1.0.80.crate from https://crates.io/api/v1/crates/syn/1.0.80/download?dummy=
---&amp;gt;  Attempting to fetch synstructure-0.12.6.crate from https://crates.io/api/v1/crates/synstructure/0.12.6/download?dummy=
---&amp;gt;  Attempting to fetch tar-0.4.37.crate from https://crates.io/api/v1/crates/tar/0.4.37/download?dummy=
---&amp;gt;  Attempting to fetch tempfile-3.2.0.crate from https://crates.io/api/v1/crates/tempfile/3.2.0/download?dummy=
---&amp;gt;  Attempting to fetch tendril-0.4.1.crate from https://crates.io/api/v1/crates/tendril/0.4.1/download?dummy=
---&amp;gt;  Attempting to fetch term-0.6.1.crate from https://crates.io/api/v1/crates/term/0.6.1/download?dummy=
---&amp;gt;  Attempting to fetch term-0.7.0.crate from https://crates.io/api/v1/crates/term/0.7.0/download?dummy=
---&amp;gt;  Attempting to fetch termcolor-1.1.2.crate from https://crates.io/api/v1/crates/termcolor/1.1.2/download?dummy=
---&amp;gt;  Attempting to fetch termize-0.1.1.crate from https://crates.io/api/v1/crates/termize/0.1.1/download?dummy=
---&amp;gt;  Attempting to fetch tester-0.9.0.crate from https://crates.io/api/v1/crates/tester/0.9.0/download?dummy=
---&amp;gt;  Attempting to fetch textwrap-0.11.0.crate from https://crates.io/api/v1/crates/textwrap/0.11.0/download?dummy=
---&amp;gt;  Attempting to fetch textwrap-0.14.2.crate from https://crates.io/api/v1/crates/textwrap/0.14.2/download?dummy=
---&amp;gt;  Attempting to fetch thiserror-1.0.30.crate from https://crates.io/api/v1/crates/thiserror/1.0.30/download?dummy=
---&amp;gt;  Attempting to fetch thiserror-impl-1.0.30.crate from https://crates.io/api/v1/crates/thiserror-impl/1.0.30/download?dummy=
---&amp;gt;  Attempting to fetch thorin-dwp-0.2.0.crate from https://crates.io/api/v1/crates/thorin-dwp/0.2.0/download?dummy=
---&amp;gt;  Attempting to fetch thread_local-1.1.4.crate from https://crates.io/api/v1/crates/thread_local/1.1.4/download?dummy=
---&amp;gt;  Attempting to fetch tikv-jemalloc-sys-0.4.1+5.2.1-patched.crate from https://crates.io/api/v1/crates/tikv-jemalloc-sys/0.4.1+5.2.1-patched/download?dummy=
---&amp;gt;  Attempting to fetch time-0.1.43.crate from https://crates.io/api/v1/crates/time/0.1.43/download?dummy=
---&amp;gt;  Attempting to fetch tinyvec-0.3.4.crate from https://crates.io/api/v1/crates/tinyvec/0.3.4/download?dummy=
---&amp;gt;  Attempting to fetch tokio-1.8.4.crate from https://crates.io/api/v1/crates/tokio/1.8.4/download?dummy=
---&amp;gt;  Attempting to fetch tokio-stream-0.1.7.crate from https://crates.io/api/v1/crates/tokio-stream/0.1.7/download?dummy=
---&amp;gt;  Attempting to fetch tokio-util-0.6.7.crate from https://crates.io/api/v1/crates/tokio-util/0.6.7/download?dummy=
---&amp;gt;  Attempting to fetch toml-0.5.7.crate from https://crates.io/api/v1/crates/toml/0.5.7/download?dummy=
---&amp;gt;  Attempting to fetch toml_edit-0.13.4.crate from https://crates.io/api/v1/crates/toml_edit/0.13.4/download?dummy=
---&amp;gt;  Attempting to fetch topological-sort-0.1.0.crate from https://crates.io/api/v1/crates/topological-sort/0.1.0/download?dummy=
---&amp;gt;  Attempting to fetch tower-service-0.3.1.crate from https://crates.io/api/v1/crates/tower-service/0.3.1/download?dummy=
---&amp;gt;  Attempting to fetch tracing-0.1.29.crate from https://crates.io/api/v1/crates/tracing/0.1.29/download?dummy=
---&amp;gt;  Attempting to fetch tracing-attributes-0.1.18.crate from https://crates.io/api/v1/crates/tracing-attributes/0.1.18/download?dummy=
---&amp;gt;  Attempting to fetch tracing-core-0.1.21.crate from https://crates.io/api/v1/crates/tracing-core/0.1.21/download?dummy=
---&amp;gt;  Attempting to fetch tracing-log-0.1.2.crate from https://crates.io/api/v1/crates/tracing-log/0.1.2/download?dummy=
---&amp;gt;  Attempting to fetch tracing-subscriber-0.3.3.crate from https://crates.io/api/v1/crates/tracing-subscriber/0.3.3/download?dummy=
---&amp;gt;  Attempting to fetch tracing-tree-0.2.0.crate from https://crates.io/api/v1/crates/tracing-tree/0.2.0/download?dummy=
---&amp;gt;  Attempting to fetch typenum-1.12.0.crate from https://crates.io/api/v1/crates/typenum/1.12.0/download?dummy=
---&amp;gt;  Attempting to fetch ucd-parse-0.1.8.crate from https://crates.io/api/v1/crates/ucd-parse/0.1.8/download?dummy=
---&amp;gt;  Attempting to fetch ucd-trie-0.1.3.crate from https://crates.io/api/v1/crates/ucd-trie/0.1.3/download?dummy=
---&amp;gt;  Attempting to fetch unic-char-property-0.9.0.crate from https://crates.io/api/v1/crates/unic-char-property/0.9.0/download?dummy=
---&amp;gt;  Attempting to fetch unic-char-range-0.9.0.crate from https://crates.io/api/v1/crates/unic-char-range/0.9.0/download?dummy=
---&amp;gt;  Attempting to fetch unic-common-0.9.0.crate from https://crates.io/api/v1/crates/unic-common/0.9.0/download?dummy=
---&amp;gt;  Attempting to fetch unic-emoji-char-0.9.0.crate from https://crates.io/api/v1/crates/unic-emoji-char/0.9.0/download?dummy=
---&amp;gt;  Attempting to fetch unic-ucd-version-0.9.0.crate from https://crates.io/api/v1/crates/unic-ucd-version/0.9.0/download?dummy=
---&amp;gt;  Attempting to fetch unicase-2.6.0.crate from https://crates.io/api/v1/crates/unicase/2.6.0/download?dummy=
---&amp;gt;  Attempting to fetch unicode-bidi-0.3.4.crate from https://crates.io/api/v1/crates/unicode-bidi/0.3.4/download?dummy=
---&amp;gt;  Attempting to fetch unicode-normalization-0.1.13.crate from https://crates.io/api/v1/crates/unicode-normalization/0.1.13/download?dummy=
---&amp;gt;  Attempting to fetch unicode-script-0.5.3.crate from https://crates.io/api/v1/crates/unicode-script/0.5.3/download?dummy=
---&amp;gt;  Attempting to fetch unicode-security-0.0.5.crate from https://crates.io/api/v1/crates/unicode-security/0.0.5/download?dummy=
---&amp;gt;  Attempting to fetch unicode-segmentation-1.6.0.crate from https://crates.io/api/v1/crates/unicode-segmentation/1.6.0/download?dummy=
---&amp;gt;  Attempting to fetch unicode-width-0.1.8.crate from https://crates.io/api/v1/crates/unicode-width/0.1.8/download?dummy=
---&amp;gt;  Attempting to fetch unicode-xid-0.2.2.crate from https://crates.io/api/v1/crates/unicode-xid/0.2.2/download?dummy=
---&amp;gt;  Attempting to fetch unicode_categories-0.1.1.crate from https://crates.io/api/v1/crates/unicode_categories/0.1.1/download?dummy=
---&amp;gt;  Attempting to fetch unified-diff-0.2.1.crate from https://crates.io/api/v1/crates/unified-diff/0.2.1/download?dummy=
---&amp;gt;  Attempting to fetch unindent-0.1.7.crate from https://crates.io/api/v1/crates/unindent/0.1.7/download?dummy=
---&amp;gt;  Attempting to fetch url-1.7.2.crate from https://crates.io/api/v1/crates/url/1.7.2/download?dummy=
---&amp;gt;  Attempting to fetch url-2.2.2.crate from https://crates.io/api/v1/crates/url/2.2.2/download?dummy=
---&amp;gt;  Attempting to fetch utf-8-0.7.5.crate from https://crates.io/api/v1/crates/utf-8/0.7.5/download?dummy=
---&amp;gt;  Attempting to fetch utf8parse-0.1.1.crate from https://crates.io/api/v1/crates/utf8parse/0.1.1/download?dummy=
---&amp;gt;  Attempting to fetch vcpkg-0.2.10.crate from https://crates.io/api/v1/crates/vcpkg/0.2.10/download?dummy=
---&amp;gt;  Attempting to fetch vec_map-0.8.2.crate from https://crates.io/api/v1/crates/vec_map/0.8.2/download?dummy=
---&amp;gt;  Attempting to fetch vergen-5.1.0.crate from https://crates.io/api/v1/crates/vergen/5.1.0/download?dummy=
---&amp;gt;  Attempting to fetch version_check-0.9.3.crate from https://crates.io/api/v1/crates/version_check/0.9.3/download?dummy=
---&amp;gt;  Attempting to fetch vte-0.3.3.crate from https://crates.io/api/v1/crates/vte/0.3.3/download?dummy=
---&amp;gt;  Attempting to fetch walkdir-2.3.1.crate from https://crates.io/api/v1/crates/walkdir/2.3.1/download?dummy=
---&amp;gt;  Attempting to fetch wasi-0.9.0+wasi-snapshot-preview1.crate from https://crates.io/api/v1/crates/wasi/0.9.0+wasi-snapshot-preview1/download?dummy=
---&amp;gt;  Attempting to fetch wasi-0.11.0+wasi-snapshot-preview1.crate from https://crates.io/api/v1/crates/wasi/0.11.0+wasi-snapshot-preview1/download?dummy=
---&amp;gt;  Attempting to fetch winapi-0.3.9.crate from https://crates.io/api/v1/crates/winapi/0.3.9/download?dummy=
---&amp;gt;  Attempting to fetch winapi-i686-pc-windows-gnu-0.4.0.crate from https://crates.io/api/v1/crates/winapi-i686-pc-windows-gnu/0.4.0/download?dummy=
---&amp;gt;  Attempting to fetch winapi-util-0.1.5.crate from https://crates.io/api/v1/crates/winapi-util/0.1.5/download?dummy=
---&amp;gt;  Attempting to fetch winapi-x86_64-pc-windows-gnu-0.4.0.crate from https://crates.io/api/v1/crates/winapi-x86_64-pc-windows-gnu/0.4.0/download?dummy=
---&amp;gt;  Attempting to fetch xattr-0.2.2.crate from https://crates.io/api/v1/crates/xattr/0.2.2/download?dummy=
---&amp;gt;  Attempting to fetch xml5ever-0.16.1.crate from https://crates.io/api/v1/crates/xml5ever/0.16.1/download?dummy=
---&amp;gt;  Attempting to fetch xz2-0.1.6.crate from https://crates.io/api/v1/crates/xz2/0.1.6/download?dummy=
---&amp;gt;  Attempting to fetch yaml-merge-keys-0.4.1.crate from https://crates.io/api/v1/crates/yaml-merge-keys/0.4.1/download?dummy=
---&amp;gt;  Attempting to fetch yaml-rust-0.3.5.crate from https://crates.io/api/v1/crates/yaml-rust/0.3.5/download?dummy=
---&amp;gt;  Attempting to fetch yaml-rust-0.4.4.crate from https://crates.io/api/v1/crates/yaml-rust/0.4.4/download?dummy=
---&amp;gt;  Attempting to fetch yansi-term-0.1.2.crate from https://crates.io/api/v1/crates/yansi-term/0.1.2/download?dummy=
---&amp;gt;  Verifying checksums for rust
---&amp;gt;  Extracting rust
---&amp;gt;  Applying patches to rust
---&amp;gt;  Configuring rust
---&amp;gt;  Building rust
      [                                        ]  18.3 %
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Wait, what?  Not only did Rust make it into the dependency chain here,
but whatever thing (or things) it's building needs (need) 393 Rust
packages to build?  Including things like
&lt;code&gt;winapi-x86_64-pc-windows-gnu-0&lt;/code&gt; (I am on &lt;code&gt;macOS-arm64&lt;/code&gt;) and two
different versions of a WebAssembly runtime?  What the fuck?!&lt;/p&gt;&lt;p&gt;Rust finishes building 35 minutes later, and now we're on to cargo
(the Rust package manager).  Progress!&lt;/p&gt;&lt;pre&gt;&lt;code&gt;---&amp;gt;  Staging rust into destroot
---&amp;gt;  Installing rust @1.61.0_2
---&amp;gt;  Activating rust @1.61.0_2
---&amp;gt;  Cleaning rust
---&amp;gt;  Computing dependencies for cargo
---&amp;gt;  Fetching archive for cargo
---&amp;gt;  Attempting to fetch cargo-0.62.0_2.darwin_22.arm64.tbz2 from https://packages.macports.org/cargo
---&amp;gt;  Attempting to fetch cargo-0.62.0_2.darwin_22.arm64.tbz2 from https://nue.de.packages.macports.org/cargo
---&amp;gt;  Attempting to fetch cargo-0.62.0_2.darwin_22.arm64.tbz2 from https://mse.uk.packages.macports.org/cargo
---&amp;gt;  Fetching distfiles for cargo
---&amp;gt;  Attempting to fetch cargo-0.62.0.tar.gz from https://distfiles.macports.org/cargo-crates
---&amp;gt;  Attempting to fetch cargo-1.60.0-aarch64-apple-darwin.tar.gz from https://static.rust-lang.org/dist
---&amp;gt;  Attempting to fetch anyhow-1.0.57.crate from https://crates.io/api/v1/crates/anyhow/1.0.57/download?dummy=
---&amp;gt;  Attempting to fetch arrayvec-0.5.2.crate from https://crates.io/api/v1/crates/arrayvec/0.5.2/download?dummy=
---&amp;gt;  Attempting to fetch bitmaps-2.1.0.crate from https://crates.io/api/v1/crates/bitmaps/2.1.0/download?dummy=
---&amp;gt;  Attempting to fetch bytesize-1.1.0.crate from https://crates.io/api/v1/crates/bytesize/1.1.0/download?dummy=
---&amp;gt;  Attempting to fetch cc-1.0.73.crate from https://crates.io/api/v1/crates/cc/1.0.73/download?dummy=
---&amp;gt;  Attempting to fetch clap-3.1.18.crate from https://crates.io/api/v1/crates/clap/3.1.18/download?dummy=
---&amp;gt;  Attempting to fetch clap_lex-0.2.0.crate from https://crates.io/api/v1/crates/clap_lex/0.2.0/download?dummy=
---&amp;gt;  Attempting to fetch combine-4.6.4.crate from https://crates.io/api/v1/crates/combine/4.6.4/download?dummy=
---&amp;gt;  Attempting to fetch commoncrypto-0.2.0.crate from https://crates.io/api/v1/crates/commoncrypto/0.2.0/download?dummy=
---&amp;gt;  Attempting to fetch commoncrypto-sys-0.2.0.crate from https://crates.io/api/v1/crates/commoncrypto-sys/0.2.0/download?dummy=
---&amp;gt;  Attempting to fetch core-foundation-0.9.3.crate from https://crates.io/api/v1/crates/core-foundation/0.9.3/download?dummy=
---&amp;gt;  Attempting to fetch core-foundation-sys-0.8.3.crate from https://crates.io/api/v1/crates/core-foundation-sys/0.8.3/download?dummy=
---&amp;gt;  Attempting to fetch crc32fast-1.3.2.crate from https://crates.io/api/v1/crates/crc32fast/1.3.2/download?dummy=
---&amp;gt;  Attempting to fetch crossbeam-utils-0.8.8.crate from https://crates.io/api/v1/crates/crossbeam-utils/0.8.8/download?dummy=
---&amp;gt;  Attempting to fetch crypto-hash-0.3.4.crate from https://crates.io/api/v1/crates/crypto-hash/0.3.4/download?dummy=
---&amp;gt;  Attempting to fetch curl-0.4.43.crate from https://crates.io/api/v1/crates/curl/0.4.43/download?dummy=
---&amp;gt;  Attempting to fetch curl-sys-0.4.55+curl-7.83.1.crate from https://crates.io/api/v1/crates/curl-sys/0.4.55+curl-7.83.1/download?dummy=
---&amp;gt;  Attempting to fetch env_logger-0.7.1.crate from https://crates.io/api/v1/crates/env_logger/0.7.1/download?dummy=
---&amp;gt;  Attempting to fetch env_logger-0.9.0.crate from https://crates.io/api/v1/crates/env_logger/0.9.0/download?dummy=
---&amp;gt;  Attempting to fetch fastrand-1.7.0.crate from https://crates.io/api/v1/crates/fastrand/1.7.0/download?dummy=
---&amp;gt;  Attempting to fetch filetime-0.2.16.crate from https://crates.io/api/v1/crates/filetime/0.2.16/download?dummy=
---&amp;gt;  Attempting to fetch flate2-1.0.23.crate from https://crates.io/api/v1/crates/flate2/1.0.23/download?dummy=
---&amp;gt;  Attempting to fetch fnv-1.0.7.crate from https://crates.io/api/v1/crates/fnv/1.0.7/download?dummy=
---&amp;gt;  Attempting to fetch foreign-types-0.3.2.crate from https://crates.io/api/v1/crates/foreign-types/0.3.2/download?dummy=
---&amp;gt;  Attempting to fetch foreign-types-shared-0.1.1.crate from https://crates.io/api/v1/crates/foreign-types-shared/0.1.1/download?dummy=
---&amp;gt;  Attempting to fetch form_urlencoded-1.0.1.crate from https://crates.io/api/v1/crates/form_urlencoded/1.0.1/download?dummy=
---&amp;gt;  Attempting to fetch fwdansi-1.1.0.crate from https://crates.io/api/v1/crates/fwdansi/1.1.0/download?dummy=
---&amp;gt;  Attempting to fetch git2-0.14.2.crate from https://crates.io/api/v1/crates/git2/0.14.2/download?dummy=
---&amp;gt;  Attempting to fetch git2-curl-0.15.0.crate from https://crates.io/api/v1/crates/git2-curl/0.15.0/download?dummy=
---&amp;gt;  Attempting to fetch globset-0.4.8.crate from https://crates.io/api/v1/crates/globset/0.4.8/download?dummy=
---&amp;gt;  Attempting to fetch hashbrown-0.11.2.crate from https://crates.io/api/v1/crates/hashbrown/0.11.2/download?dummy=
---&amp;gt;  Attempting to fetch hex-0.3.2.crate from https://crates.io/api/v1/crates/hex/0.3.2/download?dummy=
---&amp;gt;  Attempting to fetch hex-0.4.3.crate from https://crates.io/api/v1/crates/hex/0.4.3/download?dummy=
---&amp;gt;  Attempting to fetch home-0.5.3.crate from https://crates.io/api/v1/crates/home/0.5.3/download?dummy=
---&amp;gt;  Attempting to fetch humantime-1.3.0.crate from https://crates.io/api/v1/crates/humantime/1.3.0/download?dummy=
---&amp;gt;  Attempting to fetch idna-0.2.3.crate from https://crates.io/api/v1/crates/idna/0.2.3/download?dummy=
---&amp;gt;  Attempting to fetch ignore-0.4.18.crate from https://crates.io/api/v1/crates/ignore/0.4.18/download?dummy=
---&amp;gt;  Attempting to fetch im-rc-15.1.0.crate from https://crates.io/api/v1/crates/im-rc/15.1.0/download?dummy=
---&amp;gt;  Attempting to fetch indexmap-1.8.1.crate from https://crates.io/api/v1/crates/indexmap/1.8.1/download?dummy=
---&amp;gt;  Attempting to fetch itertools-0.10.3.crate from https://crates.io/api/v1/crates/itertools/0.10.3/download?dummy=
---&amp;gt;  Attempting to fetch itoa-1.0.2.crate from https://crates.io/api/v1/crates/itoa/1.0.2/download?dummy=
---&amp;gt;  Attempting to fetch kstring-1.0.6.crate from https://crates.io/api/v1/crates/kstring/1.0.6/download?dummy=
---&amp;gt;  Attempting to fetch libc-0.2.126.crate from https://crates.io/api/v1/crates/libc/0.2.126/download?dummy=
---&amp;gt;  Attempting to fetch libgit2-sys-0.13.2+1.4.2.crate from https://crates.io/api/v1/crates/libgit2-sys/0.13.2+1.4.2/download?dummy=
---&amp;gt;  Attempting to fetch libnghttp2-sys-0.1.7+1.45.0.crate from https://crates.io/api/v1/crates/libnghttp2-sys/0.1.7+1.45.0/download?dummy=
---&amp;gt;  Attempting to fetch libssh2-sys-0.2.23.crate from https://crates.io/api/v1/crates/libssh2-sys/0.2.23/download?dummy=
---&amp;gt;  Attempting to fetch libz-sys-1.1.6.crate from https://crates.io/api/v1/crates/libz-sys/1.1.6/download?dummy=
---&amp;gt;  Attempting to fetch log-0.4.17.crate from https://crates.io/api/v1/crates/log/0.4.17/download?dummy=
---&amp;gt;  Attempting to fetch matches-0.1.9.crate from https://crates.io/api/v1/crates/matches/0.1.9/download?dummy=
---&amp;gt;  Attempting to fetch memchr-2.5.0.crate from https://crates.io/api/v1/crates/memchr/2.5.0/download?dummy=
---&amp;gt;  Attempting to fetch miniz_oxide-0.5.1.crate from https://crates.io/api/v1/crates/miniz_oxide/0.5.1/download?dummy=
---&amp;gt;  Attempting to fetch once_cell-1.11.0.crate from https://crates.io/api/v1/crates/once_cell/1.11.0/download?dummy=
---&amp;gt;  Attempting to fetch opener-0.5.0.crate from https://crates.io/api/v1/crates/opener/0.5.0/download?dummy=
---&amp;gt;  Attempting to fetch openssl-0.10.40.crate from https://crates.io/api/v1/crates/openssl/0.10.40/download?dummy=
---&amp;gt;  Attempting to fetch openssl-macros-0.1.0.crate from https://crates.io/api/v1/crates/openssl-macros/0.1.0/download?dummy=
---&amp;gt;  Attempting to fetch openssl-probe-0.1.5.crate from https://crates.io/api/v1/crates/openssl-probe/0.1.5/download?dummy=
---&amp;gt;  Attempting to fetch openssl-src-111.20.0+1.1.1o.crate from https://crates.io/api/v1/crates/openssl-src/111.20.0+1.1.1o/download?dummy=
---&amp;gt;  Attempting to fetch openssl-sys-0.9.73.crate from https://crates.io/api/v1/crates/openssl-sys/0.9.73/download?dummy=
---&amp;gt;  Attempting to fetch os_info-3.4.0.crate from https://crates.io/api/v1/crates/os_info/3.4.0/download?dummy=
---&amp;gt;  Attempting to fetch os_str_bytes-6.0.1.crate from https://crates.io/api/v1/crates/os_str_bytes/6.0.1/download?dummy=
---&amp;gt;  Attempting to fetch percent-encoding-2.1.0.crate from https://crates.io/api/v1/crates/percent-encoding/2.1.0/download?dummy=
---&amp;gt;  Attempting to fetch pkg-config-0.3.25.crate from https://crates.io/api/v1/crates/pkg-config/0.3.25/download?dummy=
---&amp;gt;  Attempting to fetch pretty_env_logger-0.4.0.crate from https://crates.io/api/v1/crates/pretty_env_logger/0.4.0/download?dummy=
---&amp;gt;  Attempting to fetch proc-macro2-1.0.39.crate from https://crates.io/api/v1/crates/proc-macro2/1.0.39/download?dummy=
---&amp;gt;  Attempting to fetch quick-error-1.2.3.crate from https://crates.io/api/v1/crates/quick-error/1.2.3/download?dummy=
---&amp;gt;  Attempting to fetch rand_xoshiro-0.6.0.crate from https://crates.io/api/v1/crates/rand_xoshiro/0.6.0/download?dummy=
---&amp;gt;  Attempting to fetch regex-1.5.6.crate from https://crates.io/api/v1/crates/regex/1.5.6/download?dummy=
---&amp;gt;  Attempting to fetch regex-syntax-0.6.26.crate from https://crates.io/api/v1/crates/regex-syntax/0.6.26/download?dummy=
---&amp;gt;  Attempting to fetch remove_dir_all-0.5.3.crate from https://crates.io/api/v1/crates/remove_dir_all/0.5.3/download?dummy=
---&amp;gt;  Attempting to fetch rustc-workspace-hack-1.0.0.crate from https://crates.io/api/v1/crates/rustc-workspace-hack/1.0.0/download?dummy=
---&amp;gt;  Attempting to fetch rustfix-0.6.0.crate from https://crates.io/api/v1/crates/rustfix/0.6.0/download?dummy=
---&amp;gt;  Attempting to fetch ryu-1.0.10.crate from https://crates.io/api/v1/crates/ryu/1.0.10/download?dummy=
---&amp;gt;  Attempting to fetch schannel-0.1.20.crate from https://crates.io/api/v1/crates/schannel/0.1.20/download?dummy=
---&amp;gt;  Attempting to fetch semver-1.0.9.crate from https://crates.io/api/v1/crates/semver/1.0.9/download?dummy=
---&amp;gt;  Attempting to fetch serde-1.0.137.crate from https://crates.io/api/v1/crates/serde/1.0.137/download?dummy=
---&amp;gt;  Attempting to fetch serde_derive-1.0.137.crate from https://crates.io/api/v1/crates/serde_derive/1.0.137/download?dummy=
---&amp;gt;  Attempting to fetch serde_ignored-0.1.3.crate from https://crates.io/api/v1/crates/serde_ignored/0.1.3/download?dummy=
---&amp;gt;  Attempting to fetch serde_json-1.0.81.crate from https://crates.io/api/v1/crates/serde_json/1.0.81/download?dummy=
---&amp;gt;  Attempting to fetch shell-escape-0.1.5.crate from https://crates.io/api/v1/crates/shell-escape/0.1.5/download?dummy=
---&amp;gt;  Attempting to fetch sized-chunks-0.6.5.crate from https://crates.io/api/v1/crates/sized-chunks/0.6.5/download?dummy=
---&amp;gt;  Attempting to fetch socket2-0.4.4.crate from https://crates.io/api/v1/crates/socket2/0.4.4/download?dummy=
---&amp;gt;  Attempting to fetch strip-ansi-escapes-0.1.1.crate from https://crates.io/api/v1/crates/strip-ansi-escapes/0.1.1/download?dummy=
---&amp;gt;  Attempting to fetch strsim-0.10.0.crate from https://crates.io/api/v1/crates/strsim/0.10.0/download?dummy=
---&amp;gt;  Attempting to fetch syn-1.0.95.crate from https://crates.io/api/v1/crates/syn/1.0.95/download?dummy=
---&amp;gt;  Attempting to fetch tar-0.4.38.crate from https://crates.io/api/v1/crates/tar/0.4.38/download?dummy=
---&amp;gt;  Attempting to fetch tempfile-3.3.0.crate from https://crates.io/api/v1/crates/tempfile/3.3.0/download?dummy=
---&amp;gt;  Attempting to fetch termcolor-1.1.3.crate from https://crates.io/api/v1/crates/termcolor/1.1.3/download?dummy=
---&amp;gt;  Attempting to fetch textwrap-0.15.0.crate from https://crates.io/api/v1/crates/textwrap/0.15.0/download?dummy=
---&amp;gt;  Attempting to fetch thread_local-1.1.4.crate from https://crates.io/api/v1/crates/thread_local/1.1.4/download?dummy=
---&amp;gt;  Attempting to fetch tinyvec-1.6.0.crate from https://crates.io/api/v1/crates/tinyvec/1.6.0/download?dummy=
---&amp;gt;  Attempting to fetch tinyvec_macros-0.1.0.crate from https://crates.io/api/v1/crates/tinyvec_macros/0.1.0/download?dummy=
---&amp;gt;  Attempting to fetch toml_edit-0.13.4.crate from https://crates.io/api/v1/crates/toml_edit/0.13.4/download?dummy=
---&amp;gt;  Attempting to fetch typenum-1.15.0.crate from https://crates.io/api/v1/crates/typenum/1.15.0/download?dummy=
---&amp;gt;  Attempting to fetch unicode-bidi-0.3.8.crate from https://crates.io/api/v1/crates/unicode-bidi/0.3.8/download?dummy=
---&amp;gt;  Attempting to fetch unicode-ident-1.0.0.crate from https://crates.io/api/v1/crates/unicode-ident/1.0.0/download?dummy=
---&amp;gt;  Attempting to fetch unicode-normalization-0.1.19.crate from https://crates.io/api/v1/crates/unicode-normalization/0.1.19/download?dummy=
---&amp;gt;  Attempting to fetch unicode-xid-0.2.3.crate from https://crates.io/api/v1/crates/unicode-xid/0.2.3/download?dummy=
---&amp;gt;  Attempting to fetch url-2.2.2.crate from https://crates.io/api/v1/crates/url/2.2.2/download?dummy=
---&amp;gt;  Attempting to fetch utf8parse-0.2.0.crate from https://crates.io/api/v1/crates/utf8parse/0.2.0/download?dummy=
---&amp;gt;  Attempting to fetch vcpkg-0.2.15.crate from https://crates.io/api/v1/crates/vcpkg/0.2.15/download?dummy=
---&amp;gt;  Attempting to fetch vte-0.10.1.crate from https://crates.io/api/v1/crates/vte/0.10.1/download?dummy=
---&amp;gt;  Attempting to fetch vte_generate_state_changes-0.1.1.crate from https://crates.io/api/v1/crates/vte_generate_state_changes/0.1.1/download?dummy=
---&amp;gt;  Attempting to fetch windows-sys-0.36.1.crate from https://crates.io/api/v1/crates/windows-sys/0.36.1/download?dummy=
---&amp;gt;  Attempting to fetch windows_aarch64_msvc-0.36.1.crate from https://crates.io/api/v1/crates/windows_aarch64_msvc/0.36.1/download?dummy=
---&amp;gt;  Attempting to fetch windows_i686_gnu-0.36.1.crate from https://crates.io/api/v1/crates/windows_i686_gnu/0.36.1/download?dummy=
---&amp;gt;  Attempting to fetch windows_i686_msvc-0.36.1.crate from https://crates.io/api/v1/crates/windows_i686_msvc/0.36.1/download?dummy=
---&amp;gt;  Attempting to fetch windows_x86_64_gnu-0.36.1.crate from https://crates.io/api/v1/crates/windows_x86_64_gnu/0.36.1/download?dummy=
---&amp;gt;  Attempting to fetch windows_x86_64_msvc-0.36.1.crate from https://crates.io/api/v1/crates/windows_x86_64_msvc/0.36.1/download?dummy=
---&amp;gt;  Verifying checksums for cargo
---&amp;gt;  Extracting cargo
---&amp;gt;  Configuring cargo
---&amp;gt;  Building cargo
      [  ...  ]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Thankfully, cargo is relatively faster to build, and now we're ready
to make progress on other things.  Shout-out to the other Windows
packages that were just brought in.  Now we also get a hint into what
exactly brought Rust in:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;---&amp;gt;  Staging cargo into destroot
---&amp;gt;  Installing cargo @0.62.0_2
---&amp;gt;  Activating cargo @0.62.0_2
---&amp;gt;  Cleaning cargo
---&amp;gt;  Computing dependencies for py310-setuptools-rust
---&amp;gt;  Fetching archive for py310-setuptools-rust
---&amp;gt;  Attempting to fetch py310-setuptools-rust-1.5.2_0.darwin_any.noarch.tbz2 from https://packages.macports.org/py310-setuptools-rust
---&amp;gt;  Attempting to fetch py310-setuptools-rust-1.5.2_0.darwin_any.noarch.tbz2.rmd160 from https://packages.macports.org/py310-setuptools-rust
---&amp;gt;  Installing py310-setuptools-rust @1.5.2_0
---&amp;gt;  Activating py310-setuptools-rust @1.5.2_0
---&amp;gt;  Cleaning py310-setuptools-rust
---&amp;gt;  Computing dependencies for py310-packaging
---&amp;gt;  Fetching archive for py310-packaging
---&amp;gt;  Attempting to fetch py310-packaging-22.0_0.darwin_any.noarch.tbz2 from https://packages.macports.org/py310-packaging
---&amp;gt;  Attempting to fetch py310-packaging-22.0_0.darwin_any.noarch.tbz2.rmd160 from https://packages.macports.org/py310-packaging
---&amp;gt;  Installing py310-packaging @22.0_0
---&amp;gt;  Cleaning py310-packaging
---&amp;gt;  Computing dependencies for py310-packaging
---&amp;gt;  Deactivating py310-packaging @21.3_0
---&amp;gt;  Cleaning py310-packaging
---&amp;gt;  Activating py310-packaging @22.0_0
---&amp;gt;  Cleaning py310-packaging
---&amp;gt;  Computing dependencies for py310-pep517
---&amp;gt;  Fetching archive for py310-pep517
---&amp;gt;  Attempting to fetch py310-pep517-0.13.0_0.darwin_any.noarch.tbz2 from https://packages.macports.org/py310-pep517
---&amp;gt;  Attempting to fetch py310-pep517-0.13.0_0.darwin_any.noarch.tbz2.rmd160 from https://packages.macports.org/py310-pep517
---&amp;gt;  Installing py310-pep517 @0.13.0_0
---&amp;gt;  Activating py310-pep517 @0.13.0_0
---&amp;gt;  Cleaning py310-pep517
---&amp;gt;  Computing dependencies for py310-build
---&amp;gt;  Fetching archive for py310-build
---&amp;gt;  Attempting to fetch py310-build-0.8.0_0.darwin_any.noarch.tbz2 from https://packages.macports.org/py310-build
---&amp;gt;  Attempting to fetch py310-build-0.8.0_0.darwin_any.noarch.tbz2.rmd160 from https://packages.macports.org/py310-build
---&amp;gt;  Installing py310-build @0.8.0_0
---&amp;gt;  Activating py310-build @0.8.0_0
---&amp;gt;  Cleaning py310-build
---&amp;gt;  Computing dependencies for py310-installer
---&amp;gt;  Fetching archive for py310-installer
---&amp;gt;  Attempting to fetch py310-installer-0.6.0_0.darwin_any.noarch.tbz2 from https://packages.macports.org/py310-installer
---&amp;gt;  Attempting to fetch py310-installer-0.6.0_0.darwin_any.noarch.tbz2.rmd160 from https://packages.macports.org/py310-installer
---&amp;gt;  Installing py310-installer @0.6.0_0
---&amp;gt;  Activating py310-installer @0.6.0_0
---&amp;gt;  Cleaning py310-installer
---&amp;gt;  Computing dependencies for py310-wheel
---&amp;gt;  Fetching archive for py310-wheel
---&amp;gt;  Attempting to fetch py310-wheel-0.38.4_0.darwin_any.noarch.tbz2 from https://packages.macports.org/py310-wheel
---&amp;gt;  Attempting to fetch py310-wheel-0.38.4_0.darwin_any.noarch.tbz2.rmd160 from https://packages.macports.org/py310-wheel
---&amp;gt;  Installing py310-wheel @0.38.4_0
---&amp;gt;  Activating py310-wheel @0.38.4_0
---&amp;gt;  Cleaning py310-wheel
---&amp;gt;  Computing dependencies for py310-pycparser
---&amp;gt;  Fetching archive for py310-pycparser
---&amp;gt;  Attempting to fetch py310-pycparser-2.21_0.darwin_any.noarch.tbz2 from https://packages.macports.org/py310-pycparser
---&amp;gt;  Attempting to fetch py310-pycparser-2.21_0.darwin_any.noarch.tbz2.rmd160 from https://packages.macports.org/py310-pycparser
---&amp;gt;  Installing py310-pycparser @2.21_0
---&amp;gt;  Activating py310-pycparser @2.21_0
---&amp;gt;  Cleaning py310-pycparser
---&amp;gt;  Computing dependencies for py310-cffi
---&amp;gt;  Fetching archive for py310-cffi
---&amp;gt;  Attempting to fetch py310-cffi-1.15.1_0.darwin_22.arm64.tbz2 from https://packages.macports.org/py310-cffi
---&amp;gt;  Attempting to fetch py310-cffi-1.15.1_0.darwin_22.arm64.tbz2 from https://nue.de.packages.macports.org/py310-cffi
---&amp;gt;  Attempting to fetch py310-cffi-1.15.1_0.darwin_22.arm64.tbz2 from https://mse.uk.packages.macports.org/py310-cffi
---&amp;gt;  Fetching distfiles for py310-cffi
---&amp;gt;  Attempting to fetch cffi-1.15.1.tar.gz from https://distfiles.macports.org/py-cffi
---&amp;gt;  Verifying checksums for py310-cffi
---&amp;gt;  Extracting py310-cffi
---&amp;gt;  Applying patches to py310-cffi
---&amp;gt;  Configuring py310-cffi
---&amp;gt;  Building py310-cffi
---&amp;gt;  Staging py310-cffi into destroot
---&amp;gt;  Installing py310-cffi @1.15.1_0
---&amp;gt;  Activating py310-cffi @1.15.1_0
---&amp;gt;  Cleaning py310-cffi
---&amp;gt;  Computing dependencies for py310-cryptography
---&amp;gt;  Fetching archive for py310-cryptography
---&amp;gt;  Attempting to fetch py310-cryptography-38.0.3_0.darwin_22.arm64.tbz2 from https://packages.macports.org/py310-cryptography
---&amp;gt;  Attempting to fetch py310-cryptography-38.0.3_0.darwin_22.arm64.tbz2 from https://nue.de.packages.macports.org/py310-cryptography
---&amp;gt;  Attempting to fetch py310-cryptography-38.0.3_0.darwin_22.arm64.tbz2 from https://mse.uk.packages.macports.org/py310-cryptography
---&amp;gt;  Fetching distfiles for py310-cryptography
---&amp;gt;  Attempting to fetch cryptography-38.0.3.tar.gz from https://distfiles.macports.org/cargo-crates
---&amp;gt;  Attempting to fetch Inflector-0.11.4.crate from https://crates.io/api/v1/crates/Inflector/0.11.4/download?dummy=
---&amp;gt;  Attempting to fetch aliasable-0.1.3.crate from https://crates.io/api/v1/crates/aliasable/0.1.3/download?dummy=
---&amp;gt;  Attempting to fetch android_system_properties-0.1.5.crate from https://crates.io/api/v1/crates/android_system_properties/0.1.5/download?dummy=
---&amp;gt;  Attempting to fetch asn1-0.12.2.crate from https://crates.io/api/v1/crates/asn1/0.12.2/download?dummy=
---&amp;gt;  Attempting to fetch asn1_derive-0.12.2.crate from https://crates.io/api/v1/crates/asn1_derive/0.12.2/download?dummy=
---&amp;gt;  Attempting to fetch base64-0.13.0.crate from https://crates.io/api/v1/crates/base64/0.13.0/download?dummy=
---&amp;gt;  Attempting to fetch bumpalo-3.10.0.crate from https://crates.io/api/v1/crates/bumpalo/3.10.0/download?dummy=
---&amp;gt;  Attempting to fetch chrono-0.4.22.crate from https://crates.io/api/v1/crates/chrono/0.4.22/download?dummy=
---&amp;gt;  Attempting to fetch iana-time-zone-0.1.47.crate from https://crates.io/api/v1/crates/iana-time-zone/0.1.47/download?dummy=
---&amp;gt;  Attempting to fetch indoc-0.3.6.crate from https://crates.io/api/v1/crates/indoc/0.3.6/download?dummy=
---&amp;gt;  Attempting to fetch indoc-impl-0.3.6.crate from https://crates.io/api/v1/crates/indoc-impl/0.3.6/download?dummy=
---&amp;gt;  Attempting to fetch js-sys-0.3.59.crate from https://crates.io/api/v1/crates/js-sys/0.3.59/download?dummy=
---&amp;gt;  Attempting to fetch libc-0.2.132.crate from https://crates.io/api/v1/crates/libc/0.2.132/download?dummy=
---&amp;gt;  Attempting to fetch lock_api-0.4.8.crate from https://crates.io/api/v1/crates/lock_api/0.4.8/download?dummy=
---&amp;gt;  Attempting to fetch num-integer-0.1.45.crate from https://crates.io/api/v1/crates/num-integer/0.1.45/download?dummy=
---&amp;gt;  Attempting to fetch num-traits-0.2.15.crate from https://crates.io/api/v1/crates/num-traits/0.2.15/download?dummy=
---&amp;gt;  Attempting to fetch once_cell-1.14.0.crate from https://crates.io/api/v1/crates/once_cell/1.14.0/download?dummy=
---&amp;gt;  Attempting to fetch ouroboros-0.15.4.crate from https://crates.io/api/v1/crates/ouroboros/0.15.4/download?dummy=
---&amp;gt;  Attempting to fetch ouroboros_macro-0.15.4.crate from https://crates.io/api/v1/crates/ouroboros_macro/0.15.4/download?dummy=
---&amp;gt;  Attempting to fetch parking_lot-0.11.2.crate from https://crates.io/api/v1/crates/parking_lot/0.11.2/download?dummy=
---&amp;gt;  Attempting to fetch parking_lot_core-0.8.5.crate from https://crates.io/api/v1/crates/parking_lot_core/0.8.5/download?dummy=
---&amp;gt;  Attempting to fetch paste-0.1.18.crate from https://crates.io/api/v1/crates/paste/0.1.18/download?dummy=
---&amp;gt;  Attempting to fetch paste-impl-0.1.18.crate from https://crates.io/api/v1/crates/paste-impl/0.1.18/download?dummy=
---&amp;gt;  Attempting to fetch pem-1.1.0.crate from https://crates.io/api/v1/crates/pem/1.1.0/download?dummy=
---&amp;gt;  Attempting to fetch proc-macro-hack-0.5.19.crate from https://crates.io/api/v1/crates/proc-macro-hack/0.5.19/download?dummy=
---&amp;gt;  Attempting to fetch proc-macro2-1.0.43.crate from https://crates.io/api/v1/crates/proc-macro2/1.0.43/download?dummy=
---&amp;gt;  Attempting to fetch pyo3-0.15.2.crate from https://crates.io/api/v1/crates/pyo3/0.15.2/download?dummy=
---&amp;gt;  Attempting to fetch pyo3-build-config-0.15.2.crate from https://crates.io/api/v1/crates/pyo3-build-config/0.15.2/download?dummy=
---&amp;gt;  Attempting to fetch pyo3-macros-0.15.2.crate from https://crates.io/api/v1/crates/pyo3-macros/0.15.2/download?dummy=
---&amp;gt;  Attempting to fetch pyo3-macros-backend-0.15.2.crate from https://crates.io/api/v1/crates/pyo3-macros-backend/0.15.2/download?dummy=
---&amp;gt;  Attempting to fetch quote-1.0.21.crate from https://crates.io/api/v1/crates/quote/1.0.21/download?dummy=
---&amp;gt;  Attempting to fetch redox_syscall-0.2.16.crate from https://crates.io/api/v1/crates/redox_syscall/0.2.16/download?dummy=
---&amp;gt;  Attempting to fetch smallvec-1.9.0.crate from https://crates.io/api/v1/crates/smallvec/1.9.0/download?dummy=
---&amp;gt;  Attempting to fetch syn-1.0.99.crate from https://crates.io/api/v1/crates/syn/1.0.99/download?dummy=
---&amp;gt;  Attempting to fetch unicode-ident-1.0.3.crate from https://crates.io/api/v1/crates/unicode-ident/1.0.3/download?dummy=
---&amp;gt;  Attempting to fetch unindent-0.1.10.crate from https://crates.io/api/v1/crates/unindent/0.1.10/download?dummy=
---&amp;gt;  Attempting to fetch wasm-bindgen-0.2.82.crate from https://crates.io/api/v1/crates/wasm-bindgen/0.2.82/download?dummy=
---&amp;gt;  Attempting to fetch wasm-bindgen-backend-0.2.82.crate from https://crates.io/api/v1/crates/wasm-bindgen-backend/0.2.82/download?dummy=
---&amp;gt;  Attempting to fetch wasm-bindgen-macro-0.2.82.crate from https://crates.io/api/v1/crates/wasm-bindgen-macro/0.2.82/download?dummy=
---&amp;gt;  Attempting to fetch wasm-bindgen-macro-support-0.2.82.crate from https://crates.io/api/v1/crates/wasm-bindgen-macro-support/0.2.82/download?dummy=
---&amp;gt;  Attempting to fetch wasm-bindgen-shared-0.2.82.crate from https://crates.io/api/v1/crates/wasm-bindgen-shared/0.2.82/download?dummy=
---&amp;gt;  Verifying checksums for py310-cryptography
---&amp;gt;  Extracting py310-cryptography
---&amp;gt;  Configuring py310-cryptography
---&amp;gt;  Building py310-cryptography
---&amp;gt;  Staging py310-cryptography into destroot
---&amp;gt;  Installing py310-cryptography @38.0.3_0
---&amp;gt;  Activating py310-cryptography @38.0.3_0
---&amp;gt;  Cleaning py310-cryptography
---&amp;gt;  Computing dependencies for py310-fido2
---&amp;gt;  Fetching archive for py310-fido2
---&amp;gt;  Attempting to fetch py310-fido2-1.1.0_0.darwin_22.arm64.tbz2 from https://packages.macports.org/py310-fido2
---&amp;gt;  Attempting to fetch py310-fido2-1.1.0_0.darwin_22.arm64.tbz2 from https://nue.de.packages.macports.org/py310-fido2
---&amp;gt;  Attempting to fetch py310-fido2-1.1.0_0.darwin_22.arm64.tbz2 from https://mse.uk.packages.macports.org/py310-fido2
---&amp;gt;  Fetching distfiles for py310-fido2
---&amp;gt;  Attempting to fetch fido2-1.1.0.tar.gz from https://distfiles.macports.org/py-fido2
---&amp;gt;  Verifying checksums for py310-fido2
---&amp;gt;  Extracting py310-fido2
---&amp;gt;  Configuring py310-fido2
---&amp;gt;  Building py310-fido2
---&amp;gt;  Staging py310-fido2 into destroot
---&amp;gt;  Installing py310-fido2 @1.1.0_0
---&amp;gt;  Activating py310-fido2 @1.1.0_0
---&amp;gt;  Cleaning py310-fido2
---&amp;gt;  Computing dependencies for py310-openssl
---&amp;gt;  Fetching archive for py310-openssl
---&amp;gt;  Attempting to fetch py310-openssl-22.1.0_0.darwin_any.noarch.tbz2 from https://packages.macports.org/py310-openssl
---&amp;gt;  Attempting to fetch py310-openssl-22.1.0_0.darwin_any.noarch.tbz2.rmd160 from https://packages.macports.org/py310-openssl
---&amp;gt;  Installing py310-openssl @22.1.0_0
---&amp;gt;  Activating py310-openssl @22.1.0_0
---&amp;gt;  Cleaning py310-openssl
---&amp;gt;  Computing dependencies for gsed
---&amp;gt;  Fetching archive for gsed
---&amp;gt;  Attempting to fetch gsed-4.9_0.darwin_22.arm64.tbz2 from https://packages.macports.org/gsed
---&amp;gt;  Attempting to fetch gsed-4.9_0.darwin_22.arm64.tbz2 from https://nue.de.packages.macports.org/gsed
---&amp;gt;  Attempting to fetch gsed-4.9_0.darwin_22.arm64.tbz2 from https://mse.uk.packages.macports.org/gsed
---&amp;gt;  Fetching distfiles for gsed
---&amp;gt;  Attempting to fetch sed-4.9.tar.xz from https://distfiles.macports.org/gsed
---&amp;gt;  Verifying checksums for gsed
---&amp;gt;  Extracting gsed
---&amp;gt;  Configuring gsed
Warning: Configuration logfiles contain indications of -Wimplicit-function-declaration; check that features were not accidentally disabled:
  alignof: found in sed-4.9/config.log
  re_search: found in sed-4.9/config.log
  re_compile_pattern: found in sed-4.9/config.log
  re_set_syntax: found in sed-4.9/config.log
  MIN: found in sed-4.9/config.log
  __fpending: found in sed-4.9/config.log
  strchr: found in sed-4.9/config.log
  free: found in sed-4.9/config.log
---&amp;gt;  Building gsed
---&amp;gt;  Staging gsed into destroot
---&amp;gt;  Installing gsed @4.9_0
---&amp;gt;  Activating gsed @4.9_0
---&amp;gt;  Cleaning gsed
---&amp;gt;  Computing dependencies for swig
---&amp;gt;  Fetching archive for swig
---&amp;gt;  Attempting to fetch swig-4.1.1_0.darwin_22.arm64.tbz2 from https://packages.macports.org/swig
---&amp;gt;  Attempting to fetch swig-4.1.1_0.darwin_22.arm64.tbz2 from https://nue.de.packages.macports.org/swig
---&amp;gt;  Attempting to fetch swig-4.1.1_0.darwin_22.arm64.tbz2 from https://mse.uk.packages.macports.org/swig
---&amp;gt;  Fetching distfiles for swig
---&amp;gt;  Attempting to fetch swig-4.1.1.tar.gz from https://distfiles.macports.org/swig
---&amp;gt;  Verifying checksums for swig
---&amp;gt;  Extracting swig
---&amp;gt;  Configuring swig
Warning: Configuration logfiles contain indications of -Wimplicit-function-declaration; check that features were not accidentally disabled:
  snprintf: found in swig-4.1.1/CCache/config.log
  strcmp: found in swig-4.1.1/CCache/config.log
  exit: found in swig-4.1.1/CCache/config.log
  vsnprintf: found in swig-4.1.1/CCache/config.log
---&amp;gt;  Building swig
---&amp;gt;  Staging swig into destroot
---&amp;gt;  Installing swig @4.1.1_0
---&amp;gt;  Cleaning swig
---&amp;gt;  Computing dependencies for swig
---&amp;gt;  Deactivating swig @4.0.2_2
---&amp;gt;  Cleaning swig
---&amp;gt;  Activating swig @4.1.1_0
---&amp;gt;  Cleaning swig
---&amp;gt;  Computing dependencies for swig-python
---&amp;gt;  Fetching archive for swig-python
---&amp;gt;  Attempting to fetch swig-python-4.1.1_0.darwin_22.arm64.tbz2 from https://packages.macports.org/swig-python
---&amp;gt;  Attempting to fetch swig-python-4.1.1_0.darwin_22.arm64.tbz2 from https://nue.de.packages.macports.org/swig-python
---&amp;gt;  Attempting to fetch swig-python-4.1.1_0.darwin_22.arm64.tbz2 from https://mse.uk.packages.macports.org/swig-python
---&amp;gt;  Fetching distfiles for swig-python
---&amp;gt;  Verifying checksums for swig-python
---&amp;gt;  Extracting swig-python
---&amp;gt;  Configuring swig-python
Warning: Configuration logfiles contain indications of -Wimplicit-function-declaration; check that features were not accidentally disabled:
  snprintf: found in swig-4.1.1/CCache/config.log
  strcmp: found in swig-4.1.1/CCache/config.log
  exit: found in swig-4.1.1/CCache/config.log
  vsnprintf: found in swig-4.1.1/CCache/config.log
---&amp;gt;  Building swig-python
---&amp;gt;  Staging swig-python into destroot
---&amp;gt;  Installing swig-python @4.1.1_0
---&amp;gt;  Cleaning swig-python
---&amp;gt;  Computing dependencies for swig-python
---&amp;gt;  Deactivating swig-python @4.0.2_2
---&amp;gt;  Cleaning swig-python
---&amp;gt;  Activating swig-python @4.1.1_0
---&amp;gt;  Cleaning swig-python
---&amp;gt;  Computing dependencies for py310-pyscard
---&amp;gt;  Fetching archive for py310-pyscard
---&amp;gt;  Attempting to fetch py310-pyscard-2.0.5_0.darwin_22.arm64.tbz2 from https://packages.macports.org/py310-pyscard
---&amp;gt;  Attempting to fetch py310-pyscard-2.0.5_0.darwin_22.arm64.tbz2 from https://nue.de.packages.macports.org/py310-pyscard
---&amp;gt;  Attempting to fetch py310-pyscard-2.0.5_0.darwin_22.arm64.tbz2 from https://mse.uk.packages.macports.org/py310-pyscard
---&amp;gt;  Fetching distfiles for py310-pyscard
---&amp;gt;  Attempting to fetch pyscard-2.0.5.tar.gz from https://distfiles.macports.org/py-pyscard
---&amp;gt;  Verifying checksums for py310-pyscard
---&amp;gt;  Extracting py310-pyscard
---&amp;gt;  Configuring py310-pyscard
---&amp;gt;  Building py310-pyscard
---&amp;gt;  Staging py310-pyscard into destroot
---&amp;gt;  Installing py310-pyscard @2.0.5_0
---&amp;gt;  Activating py310-pyscard @2.0.5_0
---&amp;gt;  Cleaning py310-pyscard
---&amp;gt;  Computing dependencies for py310-zipp
---&amp;gt;  Fetching archive for py310-zipp
---&amp;gt;  Attempting to fetch py310-zipp-3.11.0_0.darwin_any.noarch.tbz2 from https://packages.macports.org/py310-zipp
---&amp;gt;  Attempting to fetch py310-zipp-3.11.0_0.darwin_any.noarch.tbz2.rmd160 from https://packages.macports.org/py310-zipp
---&amp;gt;  Installing py310-zipp @3.11.0_0
---&amp;gt;  Activating py310-zipp @3.11.0_0
---&amp;gt;  Cleaning py310-zipp
---&amp;gt;  Computing dependencies for py310-importlib-metadata
---&amp;gt;  Fetching archive for py310-importlib-metadata
---&amp;gt;  Attempting to fetch py310-importlib-metadata-5.1.0_0.darwin_any.noarch.tbz2 from https://packages.macports.org/py310-importlib-metadata
---&amp;gt;  Attempting to fetch py310-importlib-metadata-5.1.0_0.darwin_any.noarch.tbz2.rmd160 from https://packages.macports.org/py310-importlib-metadata
---&amp;gt;  Installing py310-importlib-metadata @5.1.0_0
---&amp;gt;  Activating py310-importlib-metadata @5.1.0_0
---&amp;gt;  Cleaning py310-importlib-metadata
---&amp;gt;  Computing dependencies for py310-more-itertools
---&amp;gt;  Fetching archive for py310-more-itertools
---&amp;gt;  Attempting to fetch py310-more-itertools-9.0.0_0.darwin_any.noarch.tbz2 from https://packages.macports.org/py310-more-itertools
---&amp;gt;  Attempting to fetch py310-more-itertools-9.0.0_0.darwin_any.noarch.tbz2.rmd160 from https://packages.macports.org/py310-more-itertools
---&amp;gt;  Installing py310-more-itertools @9.0.0_0
---&amp;gt;  Cleaning py310-more-itertools
---&amp;gt;  Computing dependencies for py310-more-itertools
---&amp;gt;  Deactivating py310-more-itertools @8.14.0_0
---&amp;gt;  Cleaning py310-more-itertools
---&amp;gt;  Activating py310-more-itertools @9.0.0_0
---&amp;gt;  Cleaning py310-more-itertools
---&amp;gt;  Computing dependencies for py310-jaraco.classes
---&amp;gt;  Fetching archive for py310-jaraco.classes
---&amp;gt;  Attempting to fetch py310-jaraco.classes-3.2.3_0.darwin_any.noarch.tbz2 from https://packages.macports.org/py310-jaraco.classes
---&amp;gt;  Attempting to fetch py310-jaraco.classes-3.2.3_0.darwin_any.noarch.tbz2.rmd160 from https://packages.macports.org/py310-jaraco.classes
---&amp;gt;  Installing py310-jaraco.classes @3.2.3_0
---&amp;gt;  Activating py310-jaraco.classes @3.2.3_0
---&amp;gt;  Cleaning py310-jaraco.classes
---&amp;gt;  Computing dependencies for py310-keyring
---&amp;gt;  Fetching archive for py310-keyring
---&amp;gt;  Attempting to fetch py310-keyring-23.11.0_0.darwin_any.noarch.tbz2 from https://packages.macports.org/py310-keyring
---&amp;gt;  Attempting to fetch py310-keyring-23.11.0_0.darwin_any.noarch.tbz2.rmd160 from https://packages.macports.org/py310-keyring
---&amp;gt;  Installing py310-keyring @23.11.0_0
---&amp;gt;  Activating py310-keyring @23.11.0_0
---&amp;gt;  Cleaning py310-keyring
---&amp;gt;  Computing dependencies for yubikey-manager
---&amp;gt;  Fetching archive for yubikey-manager
---&amp;gt;  Attempting to fetch yubikey-manager-5.0.0_0.darwin_22.arm64.tbz2 from https://packages.macports.org/yubikey-manager
---&amp;gt;  Attempting to fetch yubikey-manager-5.0.0_0.darwin_22.arm64.tbz2 from https://nue.de.packages.macports.org/yubikey-manager
---&amp;gt;  Attempting to fetch yubikey-manager-5.0.0_0.darwin_22.arm64.tbz2 from https://mse.uk.packages.macports.org/yubikey-manager
---&amp;gt;  Fetching distfiles for yubikey-manager
---&amp;gt;  Attempting to fetch yubikey_manager-5.0.0.tar.gz from https://distfiles.macports.org/yubikey-manager
---&amp;gt;  Verifying checksums for yubikey-manager
---&amp;gt;  Extracting yubikey-manager
---&amp;gt;  Configuring yubikey-manager
---&amp;gt;  Building yubikey-manager
---&amp;gt;  Staging yubikey-manager into destroot
---&amp;gt;  Installing yubikey-manager @5.0.0_0
---&amp;gt;  Cleaning yubikey-manager
---&amp;gt;  Computing dependencies for yubikey-manager
---&amp;gt;  Deactivating yubikey-manager @3.1.2_0
---&amp;gt;  Cleaning yubikey-manager
---&amp;gt;  Activating yubikey-manager @5.0.0_0
---&amp;gt;  Cleaning yubikey-manager
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Several minutes later, and about an hour from when I ran that original
command, we're done installing everything.  Except, one of the things
I tried to upgrade failed:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;---&amp;gt;  Fetching archive for ykpers
---&amp;gt;  Attempting to fetch ykpers-1.20.0_1.darwin_22.arm64.tbz2 from https://packages.macports.org/ykpers
---&amp;gt;  Attempting to fetch ykpers-1.20.0_1.darwin_22.arm64.tbz2 from https://nue.de.packages.macports.org/ykpers
---&amp;gt;  Attempting to fetch ykpers-1.20.0_1.darwin_22.arm64.tbz2 from https://mse.uk.packages.macports.org/ykpers
---&amp;gt;  Computing dependencies for ykpers
---&amp;gt;  Fetching distfiles for ykpers
---&amp;gt;  Attempting to fetch ykpers-1.20.0.tar.gz from https://distfiles.macports.org/ykpers
---&amp;gt;  Verifying checksums for ykpers
---&amp;gt;  Extracting ykpers
---&amp;gt;  Applying patches to ykpers
---&amp;gt;  Configuring ykpers
---&amp;gt;  Building ykpers
---&amp;gt;  Staging ykpers into destroot
---&amp;gt;  Unable to uninstall ykpers @1.20.0_1, the following ports depend on it:
---&amp;gt;    yubikey-manager @3.1.2_0
Warning: Uninstall forced.  Proceeding despite dependencies.
---&amp;gt;  Deactivating ykpers @1.20.0_1
---&amp;gt;  Cleaning ykpers
---&amp;gt;  Uninstalling ykpers @1.20.0_1
---&amp;gt;  Cleaning ykpers
---&amp;gt;  Computing dependencies for ykpers
---&amp;gt;  Fetching archive for ykpers
---&amp;gt;  Attempting to fetch ykpers-1.20.0_1.darwin_22.arm64.tbz2 from https://packages.macports.org/ykpers
---&amp;gt;  Attempting to fetch ykpers-1.20.0_1.darwin_22.arm64.tbz2 from https://nue.de.packages.macports.org/ykpers
---&amp;gt;  Attempting to fetch ykpers-1.20.0_1.darwin_22.arm64.tbz2 from https://mse.uk.packages.macports.org/ykpers
---&amp;gt;  Installing ykpers @1.20.0_1
---&amp;gt;  Activating ykpers @1.20.0_1
---&amp;gt;  Cleaning ykpers
---&amp;gt;  Updating database of binaries
---&amp;gt;  Scanning binaries for linking errors
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You win some, you lose some.  At least now we get to find out exactly
why we needed Rust:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ port rdependents rust
The following ports are dependent on rust:
  cargo
    py310-setuptools-rust
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The graph ends unexpectedly.  Probably some bug in MacPorts that I'm
going to ignore for now.  Based on the earlier log, I can see
&lt;code&gt;py310-cryptography&lt;/code&gt; depends on &lt;code&gt;py310-setuptools-rust&lt;/code&gt;&lt;sup&gt;&lt;a href="#fn_1" id="fnref_1_1"&gt;1&lt;/a&gt;&lt;/sup&gt;, and
&lt;code&gt;py310-cryptography&lt;/code&gt; is, in turn, depended on by &lt;code&gt;yubikey-manager&lt;/code&gt;.  I
also notice it brings in several &lt;code&gt;wasm-bindgen-*&lt;/code&gt; packages, for what
I'm sure are totally reasonable reasons&lt;sup&gt;&lt;a href="#fn_2" id="fnref_2_1"&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ port info py310-cryptography
py310-cryptography @38.0.3 (python, devel)
...
Build Dependencies:   diffutils-for-muniversal, py310-setuptools-rust, py310-build, py310-installer, py310-setuptools, py310-wheel, rust, cargo
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There's no moral to this story, except maybe avoid upgrading your
software.  I think Rust is Good, but this sucks.&lt;/p&gt;&lt;section class="footnotes"&gt;&lt;ol&gt;&lt;li id="fn_1"&gt;&lt;p&gt;What we've got here is a cryptography package that transitively
depends on at least 393 other packages, every single one of which
is a potential attack vector.  Is this fine?  I don't know.
Maybe? &lt;a class="footnote-backref" href="#fnref_1_1" title="Jump to reference" aria-label="Jump to reference"&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li id="fn_2"&gt;&lt;p&gt;Vaguely, I remember something about some Rust packages that use
Wasm to perform macro-expansion safely during builds, or something
along those lines, so these may in fact be reasonable uses of
these Wasm packages, by some definition of reasonable.  It feels
like extreme bloat to me, though. &lt;a class="footnote-backref" href="#fnref_2_1" title="Jump to reference" aria-label="Jump to reference"&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/section&gt;&lt;/article&gt;</description></item><item><title>Announcing racket-{avro,iso-printf,lz4,messagepack}</title><link>https://defn.io/2022/12/05/ann-roundup-2212</link><guid>https://defn.io/2022/12/05/ann-roundup-2212</guid><pubDate>Mon, 5 Dec 2022 10:15:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;Some of the feedback I've received on &lt;a href="https://franz.defn.io"&gt;Franz&lt;/a&gt; so far has been that folks
need support for more compression and serialization formats. In that
vein, here are some Racket libraries I've released in the past couple of
weeks:&lt;/p&gt;&lt;ul class="tight"&gt;&lt;li&gt;&lt;code&gt;racket-avro&lt;/code&gt; (&lt;a href="https://docs.racket-lang.org/avro-manual/index.html"&gt;docs&lt;/a&gt;, &lt;a href="https://github.com/Bogdanp/racket-avro"&gt;src&lt;/a&gt;) -- an
implementation of the &lt;a href="https://avro.apache.org"&gt;Apache Avro&lt;/a&gt; serialization protocol.  This is
fairly complete, but it elides support for Avro RPC for now.&lt;/li&gt;&lt;li&gt;&lt;code&gt;racket-iso-printf&lt;/code&gt; (&lt;a href="https://docs.racket-lang.org/iso-printf-manual/index.html"&gt;docs&lt;/a&gt;, &lt;a href="https://github.com/Bogdanp/racket-iso-printf"&gt;src&lt;/a&gt;)
-- an implementation of the standard C family of &lt;code&gt;printf&lt;/code&gt; functions.
This is used in &lt;a href="https://github.com/Bogdanp/racket-lua"&gt;racket-lua&lt;/a&gt; to implement the &lt;code&gt;string.format&lt;/code&gt;
procedure.  Originally, this was going to just be an internal part
of &lt;code&gt;#lang lua&lt;/code&gt;, but I figured it might have some use beyond it. I've
certainly wanted C-style &lt;code&gt;printf&lt;/code&gt; in the past in Racket.&lt;/li&gt;&lt;li&gt;&lt;code&gt;racket-lz4&lt;/code&gt; (&lt;a href="https://docs.racket-lang.org/lz4-manual/index.html"&gt;docs&lt;/a&gt;, &lt;a href="https://github.com/Bogdanp/racket-lz4"&gt;src&lt;/a&gt;) -- a
pure-Racket &lt;a href="https://github.com/lz4/lz4"&gt;LZ4&lt;/a&gt; decompressor.  It doesn't yet support compression,
but I may add it if there is interest.&lt;/li&gt;&lt;li&gt;&lt;code&gt;racket-messagepack&lt;/code&gt; (&lt;a href="https://docs.racket-lang.org/messagepack-manual/index.html"&gt;docs&lt;/a&gt;,
&lt;a href="https://github.com/Bogdanp/racket-messagepack"&gt;src&lt;/a&gt;) -- an implementation of the &lt;a href="https://msgpack.org"&gt;MessagePack&lt;/a&gt;
serialization format.  There is an existing &lt;a href="https://docs.racket-lang.org/msgpack/index.html"&gt;msgpack&lt;/a&gt; package on the
Package Server, but it is GPL-licensed, so I wanted to avoid
distributing it with my app.&lt;/li&gt;&lt;/ul&gt;&lt;/article&gt;</description></item><item><title>Announcing Franz</title><link>https://defn.io/2022/11/20/ann-franz</link><guid>https://defn.io/2022/11/20/ann-franz</guid><pubDate>Sun, 20 Nov 2022 20:55:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;I've been using &lt;a href="https://kafka.apache.org"&gt;Apache Kafka&lt;/a&gt; for about a year now and I've wanted a
desktop client from the beginning. I couldn't find one I liked -- the
best I've found is Confluent's Cloud UI, but that's neither a desktop
app, nor is it a great experience&lt;sup&gt;&lt;a href="#fn_1" id="fnref_1_1"&gt;1&lt;/a&gt;&lt;/sup&gt; -- so, a couple months ago I
started building a native macOS GUI for Kafka in my spare time and,
today, I'm proud to announce the first beta release of &lt;a href="https://franz.defn.io"&gt;Franz&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;This release covers all the basic functionality you might expect: you
can manage topics and consumer groups, publish &amp;amp; consume records, and
even hook in and script part of the consumption process. So, if that
sounds appealing to you, please give it a try and let me know what you
think. I've had tons of fun working on this so far and I'd love to make
it even better.&lt;/p&gt;&lt;p&gt;In terms of tech, obviously, the sane approach would've been to make a
straight Swift app and embed &lt;a href="https://github.com/edenhill/librdkafka"&gt;librdkafka&lt;/a&gt; and I'd be off to the races.
But, that wouldn't be very fun. So, instead, Franz is a Swift app backed
by Racket (via the same approach described in &lt;a href="/2022/08/21/swiftui-plus-racket-screencast/"&gt;Screencast: SwiftUI +
Racket&lt;/a&gt;, though with many improvements to the process since
I recorded that video -- expect an updated screencast sometime soon!),
where the underlying Kafka client is the &lt;a href="/2022/03/12/ann-racket-kafka/"&gt;same one&lt;/a&gt;
I've been working on off-and-on since the beginning of the year. The
aforementioned scripting support is provided by &lt;a href="https://github.com/Bogdanp/racket-lua"&gt;racket-lua&lt;/a&gt;, which I
&lt;a href="/2022/11/12/ann-racket-lua/"&gt;announced&lt;/a&gt; last week.&lt;/p&gt;&lt;p&gt;I've found this approach&lt;sup&gt;&lt;a href="#fn_2" id="fnref_2_1"&gt;2&lt;/a&gt;&lt;/sup&gt; to building apps to be hugely productive
(and damn fun!). I get to alternate between quickly bashing out core
functionality in Racket, interacting with said code at the &lt;a href="https://github.com/greghendershott/racket-mode/"&gt;racket-mode&lt;/a&gt;
REPL, and switching to Swift where I can bang out whatever GUI idea
comes to mind pretty quickly at this point. It all makes for some very
productive moments, even when those moments are relatively few and
far-between (I have a dayjob so this was all done during my free time
and mostly during weekends).&lt;/p&gt;&lt;section class="footnotes"&gt;&lt;ol&gt;&lt;li id="fn_1"&gt;&lt;p&gt;Unless you love being unconditionally logged out once an hour for
some reason. &lt;a class="footnote-backref" href="#fnref_1_1" title="Jump to reference" aria-label="Jump to reference"&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li id="fn_2"&gt;&lt;p&gt;I've used a similar approach to ship an app on the Mac App Store
&lt;a href="/2020/01/02/ann-remember/"&gt;a couple years ago&lt;/a&gt;. &lt;a class="footnote-backref" href="#fnref_2_1" title="Jump to reference" aria-label="Jump to reference"&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/section&gt;&lt;/article&gt;</description></item><item><title>#lang lua</title><link>https://defn.io/2022/11/12/ann-racket-lua</link><guid>https://defn.io/2022/11/12/ann-racket-lua</guid><pubDate>Sat, 12 Nov 2022 20:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;I'm currently working on &lt;a href="/2022/11/20/ann-franz"&gt;a macOS app&lt;/a&gt; that's built with Racket and
allows the user to write small scripts to process and filter some data.
While Racket is definitely &lt;i&gt;my&lt;/i&gt; preferred language and I could easily
use it for these scripts, my target audience for this app would probably
appreciate a more familiar language. I decided to use &lt;a href="https://lua.org"&gt;Lua&lt;/a&gt;. So, last
weekend I was faced with a choice&lt;sup&gt;&lt;a href="#fn_1" id="fnref_1_1"&gt;1&lt;/a&gt;&lt;/sup&gt; between writing FFI bindings for
Lua or implementing a &lt;a href="https://docs.racket-lang.org/guide/Module_Syntax.html#%28part._hash-lang%29"&gt;#lang&lt;/a&gt; in Racket. Of course, I picked the harder
option. Partly because it seemed like the more fun option, and partly
because this way I don't have to worry about making two (three? did I
mention Swift's also in the mix?) very different runtimes cooperate.&lt;/p&gt;&lt;p&gt;It's pretty far along at this point, though some Lua standard library
functionality is still missing and a couple things are still missing
lexer support (long brackets, scientific notation for numbers). If you
want to play around with it, you can get it from the package server
by installing &lt;code&gt;lua-lang&lt;/code&gt; or &lt;code&gt;lua-lib&lt;/code&gt; if you don't want the docs.
You can find the code &lt;a href="https://github.com/Bogdanp/racket-lua"&gt;on GitHub&lt;/a&gt; and the docs &lt;a href="https://docs.racket-lang.org/lua-manual@lua-lang/index.html"&gt;on the package
server&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;img alt="DrRacket Displaying a Lua Module" src="/img/racket-lua.png"/&gt;&lt;/p&gt;&lt;section class="footnotes"&gt;&lt;ol&gt;&lt;li id="fn_1"&gt;&lt;p&gt;Before I started working on the #lang, I had checked the
documentation server to see if there was already an implementation.
I didn't find anything. Unfortunately, I forgot to check the package
server until I was ready to upload my own library. Long story short,
there are now (at least) two&lt;sup&gt;&lt;a href="#fn_2" id="fnref_2_1"&gt;2&lt;/a&gt;&lt;/sup&gt; independent implementations of Lua as a
#lang for Racket. Whoops. &lt;a class="footnote-backref" href="#fnref_1_1" title="Jump to reference" aria-label="Jump to reference"&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li id="fn_2"&gt;&lt;p&gt;The other one you can find &lt;a href="https://github.com/ShawSumma/lure"&gt;here&lt;/a&gt;. &lt;a class="footnote-backref" href="#fnref_2_1" title="Jump to reference" aria-label="Jump to reference"&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/section&gt;&lt;/article&gt;</description></item><item><title>Screencast: SwiftUI + Racket</title><link>https://defn.io/2022/08/21/swiftui-plus-racket-screencast</link><guid>https://defn.io/2022/08/21/swiftui-plus-racket-screencast</guid><pubDate>Sun, 21 Aug 2022 19:45:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;I've been playing with embedding Racket CS in desktop apps off and on
for a while and today I recorded a little screencast demoing some of the
stuff I've been working on. Here it is on &lt;a href="https://www.youtube.com/watch?v=aTvU0j4hBR0"&gt;YouTube&lt;/a&gt;:&lt;/p&gt;&lt;p&gt;&lt;center&gt;&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube-nocookie.com/embed/aTvU0j4hBR0" title="YouTube video player" width="560"&gt;&lt;/iframe&gt;&lt;/center&gt;&lt;/p&gt;&lt;p&gt;This is all pretty experimental and I'm just playing around, so nothing
is particularly stable, but if this stuff interests you, check out
&lt;a href="https://github.com/Bogdanp/Noise"&gt;Noise&lt;/a&gt; and &lt;a href="https://github.com/Bogdanp/NoiseBackendExample"&gt;NoiseBackendExample&lt;/a&gt;.&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Announcing racket-crontab</title><link>https://defn.io/2022/08/20/ann-racket-crontab</link><guid>https://defn.io/2022/08/20/ann-racket-crontab</guid><pubDate>Sat, 20 Aug 2022 12:00:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;Earlier this week, &lt;a href="https://jessealama.net/"&gt;Jesse Alama&lt;/a&gt; brought up the topic of scheduling
cron jobs with &lt;a href="https://docs.racket-lang.org/koyo/index.html"&gt;koyo&lt;/a&gt; and we both agreed that it would be nice if koyo
had built-in support for that sort of thing. So, I wrote &lt;a href="https://docs.racket-lang.org/crontab-manual/index.html"&gt;crontab&lt;/a&gt;,
a little library for parsing cron-style schedules and executing
code based on them. On top of that functionality, I've added a new
&lt;a href="https://docs.racket-lang.org/koyo/scheduling.html"&gt;&lt;code&gt;koyo/crontab&lt;/code&gt;&lt;/a&gt; module to koyo that integrates with the
component system.&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Announcing racket-kafka</title><link>https://defn.io/2022/03/12/ann-racket-kafka</link><guid>https://defn.io/2022/03/12/ann-racket-kafka</guid><pubDate>Sat, 12 Mar 2022 10:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;For the past month or so, I've been working on implementing a
pure-Racket client for &lt;a href="https://kafka.apache.org/"&gt;Apache Kafka&lt;/a&gt;. Yesterday, it reached a point
where it can do the bare minimum you would expect from it: produce data
and join consumer groups to consume data. Kafka has a fairly large
feature-set so there's a ton left to do, but I figure this is a good
time to announce the library and get feedback. If you're interested in
using Kafka with Racket, please give it a try and let me know what you
think.&lt;/p&gt;&lt;p&gt;You can find the source code on &lt;a href="https://github.com/Bogdanp/racket-kafka"&gt;GitHub&lt;/a&gt; and the docs on &lt;a href="https://docs.racket-lang.org/kafka/index.html"&gt;the
Racket Package server&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;PS: While writing the library, I needed an easy way to write lots of
little wire procotol parsers, so I stacked my yaks and wrote a binary
format parser generator called &lt;a href="https://github.com/Bogdanp/racket-binfmt"&gt;binfmt&lt;/a&gt; (&lt;a href="https://docs.racket-lang.org/binfmt-manual/index.html"&gt;docs&lt;/a&gt;).&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Parallelizing the Racket Web Server</title><link>https://defn.io/2021/12/30/parallel-racket-web-server</link><guid>https://defn.io/2021/12/30/parallel-racket-web-server</guid><pubDate>Thu, 30 Dec 2021 13:45:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;Racket provides support for concurrency via lightweight threads, which
the &lt;a href="https://docs.racket-lang.org/web-server/index.html"&gt;web server&lt;/a&gt; leverages to handle requests, spawning one such
thread per incoming request. At the runtime level, these threads run
concurrently but not in parallel (i.e., only one thread is active at any
one time). Parallelism is available in Racket via &lt;a href="https://docs.racket-lang.org/guide/parallelism.html#%28part._effective-places%29"&gt;Places&lt;/a&gt;: distinct
instances of the Racket runtime running in separate OS threads that
communicate via message passing.&lt;/p&gt;&lt;p&gt;The web server doesn't do anything with places, so, by default, all
Racket web servers run in a single OS thread. That isn't a big deal
since you can run one web server process per core and place a reverse
proxy like Nginx in front to load balance between the processes. But
what if you don't want to do that? Is there a way to use the web server
in conjunction with places despite the web server lacking explicit
support for them?&lt;/p&gt;&lt;p&gt;The answer is "yes." Otherwise, I wouldn't be writing this post!
Doing so can lead to a decent reduction in memory usage over the
multi-process approach since some resources (such as code, shared
libraries, allocation segments, etc.) are shared between places.&lt;/p&gt;&lt;p&gt;One approach to solving this problem might be to spawn multiple places,
each running a web server bound to the same port. Unfortunately, it's
not possible in Racket to re-use TCP ports (primarily because not
all platforms have an equivalent of Linux's &lt;code&gt;SO_REUSEPORT&lt;/code&gt; flag).
Thankfully, the web server's &lt;code&gt;serve&lt;/code&gt; function takes an optional &lt;code&gt;tcp@&lt;/code&gt;
argument. We can leverage that argument to provide the server with a
custom implementation of the &lt;a href="https://docs.racket-lang.org/net/tcp.html#%28form._%28%28lib._net%2Ftcp-sig..rkt%29._tcp~5e%29%29"&gt;&lt;code&gt;tcp^&lt;/code&gt; signature&lt;/a&gt;. So, our main place can
spawn one place for every parallel web server that we want to run, then
run a TCP server of its own, accept new connections on that server, and
send each connection to the web server places one by one.&lt;/p&gt;&lt;p&gt;Take this minimal application -- saved on my machine as &lt;code&gt;app.rkt&lt;/code&gt; -- for
example:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;#lang racket/base

(require web-server/dispatch
         web-server/http
         web-server/servlet-dispatch
         web-server/web-server)

(provide
 start)

(define-values (app _)
  (dispatch-rules
   [("")
    (λ (_req)
      (response/output
       (λ (out)
         (displayln "hello, world" out))))]
   [else
    (λ (_req)
      (response/output
       #:code 404
       (λ (out)
         (displayln "not found" out))))]))

(define (start host port)
  (serve
   #:dispatch (dispatch/servlet app)
   #:listen-ip host
   #:port port))

(module+ main
  (define stop (start "127.0.0.1" 8000))
  (with-handlers ([exn:break? (λ (_)
                                (stop))])
    (sync never-evt)))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Without modifying &lt;code&gt;app.rkt&lt;/code&gt;, we can create a second module, called
&lt;code&gt;main.rkt&lt;/code&gt;, that spawns multiple instances of the server, each bound to
different ports:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;#lang racket/base

(require racket/match
         racket/place
         "app.rkt")

(define (start-place)
  (place ch
    (let loop ([stop void])
      (match (sync ch)
        [`(init ,host ,port)
         (loop (start host port))]
        [`(stop)
         (stop)]))))

(module+ main
  (define places
    (for/list ([idx (in-range 4)])
      (define pch (start-place))
      (begin0 pch
        (place-channel-put pch `(init "127.0.0.1" ,(+ 8000 idx))))))

  (with-handlers ([exn:break? (λ (_)
                                (for ([pch (in-list places)])
                                  (place-channel-put pch '(stop)))
                                (for-each place-wait places))])
    (sync never-evt)))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next, we can define our custom &lt;code&gt;tcp@&lt;/code&gt; unit in &lt;code&gt;main.rkt&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-diff"&gt;  #lang racket/base

- (require racket/match
+ (require net/tcp-sig
+          racket/match
           racket/place
+          (prefix-in tcp: racket/tcp)
+          racket/unit
           "app.rkt")

+ (struct place-tcp-listener ())
+
+ (define (make-place-tcp@ accept-ch)
+   (unit
+     (import)
+     (export tcp^)
+
+     (define (tcp-addresses _p [port-numbers? #f])
+       (if port-numbers?
+           (values "127.0.0.1" 1 "127.0.0.1" 0)
+           (values "127.0.0.1" "127.0.0.1")))
+
+     (define (tcp-connect _hostname
+                          _port-no
+                          [_local-hostname #f]
+                          [_local-port-no #f])
+       (error 'tcp-connect "not supported"))
+
+     (define (tcp-connect/enable-break _hostname
+                                       _port-no
+                                       [_local-hostname #f]
+                                       [_local-port-no #f])
+       (error 'tcp-connect/enable-break "not supported"))
+
+     (define (tcp-abandon-port p)
+       (tcp:tcp-abandon-port p))
+
+     (define (tcp-listen _port-no
+                         [_backlog 4]
+                         [_reuse? #f]
+                         [_hostname #f])
+       (place-tcp-listener))
+
+     (define (tcp-listener? l)
+       (place-tcp-listener? l))
+
+     (define (tcp-close _l)
+       (void))
+
+     (define (tcp-accept _l)
+       (apply values (channel-get accept-ch)))
+
+     (define (tcp-accept/enable-break _l)
+       (apply values (sync/enable-break accept-ch)))
+
+     (define (tcp-accept-ready? _l)
+       (error 'tcp-accept-ready? "not supported"))))

  (define (start-place)
    (place ch
      (let loop ([stop void])
        (match (place-channel-get ch)
          [`(init ,host ,port)
           (loop (start host port))]
          [`(stop)
           (stop)]))))

  (module+ main
    (define places
      (for/list ([idx (in-range 4)])
        (define pch (start-place))
        (begin0 pch
          (place-channel-put pch `(init "127.0.0.1" ,(+ 8000 idx))))))

    (with-handlers ([exn:break? (λ (_)
                                  (for ([pch (in-list places)])
                                    (place-channel-put pch '(stop)))
                                  (for-each place-wait places))])
      (sync never-evt)))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It may look daunting at first glance, but &lt;code&gt;make-place-tcp@&lt;/code&gt; is
straightforward: it takes a channel of TCP connections as input and
produces an instance of a &lt;a href="https://docs.racket-lang.org/guide/units.html#%28tech._unit%29"&gt;unit&lt;/a&gt; that implements the &lt;code&gt;tcp^&lt;/code&gt; signature
that accepts new connections off of that channel. The web server doesn't
use the client-specific functions, so we don't need to bother with
their implementation. The &lt;code&gt;tcp-listen&lt;/code&gt; function returns new instances
of a stub struct, and &lt;code&gt;tcp-accept&lt;/code&gt; synchronizes on the input channel
to receive new connections (each a list of an input port and an output
port).&lt;/p&gt;&lt;p&gt;Next, let's change &lt;code&gt;start-place&lt;/code&gt; to instantiate the unit for each web
server place and to pass that unit along to the app:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-diff"&gt;  (define (start-place)
    (place ch
+     (define connections-ch (make-channel))
+     (define tcp@ (make-place-tcp@ connections-ch))
      (let loop ([stop void])
        (match (sync ch)
          [`(init ,host ,port)
-          (loop (start host port))]
+          (loop (start host port tcp@))]
          [`(stop)
           (stop)]))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now we need to change &lt;code&gt;app.rkt&lt;/code&gt;'s &lt;code&gt;start&lt;/code&gt; function to take the &lt;code&gt;tcp@&lt;/code&gt;
argument and pass it to &lt;code&gt;serve&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-diff"&gt;- (define (start host port)
+ (define (start host port tcp@)
    (serve
     #:dispatch (dispatch/servlet app)
     #:listen-ip host
-    #:port port))
+    #:port port
+    #:tcp@ tcp@))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next, we can change &lt;code&gt;start-place&lt;/code&gt; to accept new connections on its place
channel:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-diff"&gt;  (define (start-place)
    (place ch
      (define connections-ch (make-channel))
      (define tcp@ (make-place-tcp@ connections-ch))
      (let loop ([stop void])
        (match (sync ch)
          [`(init ,host ,port)
           (loop (start host port tcp@))]
+         [`(accept ,in ,out)
+          (channel-put connections-ch (list in out))
+          (loop stop)]
          [`(stop)
           (stop)]))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally, we have to change the main place to make it spawn a TCP server
to accept new connections and dispatch them to the server places:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-diff"&gt;  (module+ main
+   (require racket/tcp)
+
+   (define num-places 4)
    (define places
-     (for/list ([idx (in-range 4)])
+     (for/list ([_ (in-range num-places)])
        (define pch (start-place))
        (begin0 pch
-         (place-channel-put pch `(init "127.0.0.1" ,(+ 8000 idx))))))
+         (place-channel-put pch `(init "127.0.0.1" 8000)))))

+   (define listener
+     (tcp-listen 8000 4096 #t "127.0.0.1"))
+   (with-handlers ([exn:break? (λ (_)
+                                 (for ([pch (in-list places)])
+                                   (place-channel-put pch '(stop)))
-                                 (for-each place-wait places))])
+                                 (for-each place-wait places)
+                                 (tcp-close listener))])
-     (sync never-evt)))
+     (let loop ([idx 0])
+       (define pch (list-ref places idx))
+       (define-values (in out)
+         (tcp-accept listener))
+       (place-channel-put pch `(accept ,in ,out))
+       (tcp-abandon-port out)
+       (tcp-abandon-port in)
+       (loop (modulo (add1 idx) num-places))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now the main place spawns four other places, each running a web server
that accepts new connections via the custom TCP unit, then it launches
a TCP server on port 8000 and dispatches incoming connections to the
server places in round-robin order. I used this approach earlier
this week to improve the implementation of the &lt;a href="https://github.com/TechEmpower/FrameworkBenchmarks/pull/7003"&gt;Racket TechEmpower
benchmark&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;You can find the final version of the code in this post &lt;a href="https://gist.github.com/Bogdanp/730ee19345d4f89d97c8be73739b7659"&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;/article&gt;</description></item><item><title>LISP GUI Examples</title><link>https://defn.io/2021/11/16/lisp-gui-examples</link><guid>https://defn.io/2021/11/16/lisp-gui-examples</guid><pubDate>Tue, 16 Nov 2021 08:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;I recently ran across Matthew D. Miller's &lt;a href="https://blog.matthewdmiller.net/series/survey-of-the-state-of-gui-programming-in-lisp"&gt;"Survey of the State of
GUI Programming in Lisp"&lt;/a&gt; series on implementing a small
GUI application across various LISP implementations. The &lt;a href="https://blog.matthewdmiller.net/learn-racket-by-example-gui-programming"&gt;first
article&lt;/a&gt; in that series uses &lt;code&gt;racket/gui&lt;/code&gt;, so I figured I'd take
a stab at porting that implementation to &lt;a href="https://github.com/Bogdanp/racket-gui-easy"&gt;gui-easy&lt;/a&gt;. You can find my
port &lt;a href="https://github.com/Bogdanp/lisp-gui-examples/blob/dd61503c00ba384527a737ff35a19f4a40f88dd4/examples/racket/bleep2.rkt"&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Porting the code was straightforward, but it uncovered a common problem
with bidirectional &lt;a href="https://docs.racket-lang.org/gui-easy/index.html#%28def._%28%28lib._racket%2Fgui%2Feasy..rkt%29._input%29%29"&gt;&lt;code&gt;input&lt;/code&gt;&lt;/a&gt;s: updating the field's value observable
on every change meant that the text (and cursor position) changed
as the user typed because every change would trigger an update (and
thus a re-rendering of the text) to the underlying text field. To
work around those sorts of problems, I introduced the &lt;code&gt;#:value=?&lt;/code&gt; and
&lt;code&gt;#:value-&amp;gt;text&lt;/code&gt; arguments in commit &lt;a href="https://github.com/Bogdanp/racket-gui-easy/commit/ce190608"&gt;&lt;code&gt;ce190608&lt;/code&gt;&lt;/a&gt;. Input views with a
&lt;code&gt;#:value=?&lt;/code&gt; function only re-render the text field's contents when the
current value of the input observable is different (according to the
&lt;code&gt;#:value=?&lt;/code&gt; function) from the previous one. This means that you can use
that argument to control whether or not partial edits end up triggering
a re-rendering of the text, so instead of:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define/obs @n 42)
(render
 (window
  #:size '(200 #f)
  (input
   (@n . ~&amp;gt; . number-&amp;gt;string)
   (λ (_event text)
     (cond [(string-&amp;gt;number text) =&amp;gt; (λ:= @n)])))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can write:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define/obs @n 42)
(render
 (window
  #:size '(200 #f)
  (input
   @n
   #:value=? =
   #:value-&amp;gt;text number-&amp;gt;string
   (λ (_event text)
     (cond [(string-&amp;gt;number text) =&amp;gt; (λ:= @n)])))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In the first example, typing a &lt;code&gt;.&lt;/code&gt; after &lt;code&gt;42&lt;/code&gt; re-renders the text as
&lt;code&gt;42.0&lt;/code&gt; and places the cursor at the end. In the second, it doesn't
re-render the text at all since &lt;code&gt;42.0&lt;/code&gt; and &lt;code&gt;42&lt;/code&gt; are &lt;code&gt;=&lt;/code&gt;. Still, the
second example isn't perfect since &lt;code&gt;string-&amp;gt;number&lt;/code&gt; parses &lt;code&gt;42.&lt;/code&gt; to
&lt;code&gt;42.0&lt;/code&gt; so, if you type &lt;code&gt;42.5&lt;/code&gt; into the text field and then delete the
&lt;code&gt;5&lt;/code&gt;, it will re-render the value as &lt;code&gt;42.0&lt;/code&gt;. You can work around this
problem by avoiding partial updates in the input's action:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-diff"&gt;   (λ (_event text)
--   (cond [(string-&amp;gt;number text) =&amp;gt; (λ:= @n)]))
++   (unless (string-suffix? text ".")
++     (cond [(string-&amp;gt;number text) =&amp;gt; (λ:= @n)])))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Perhaps a better way to handle this would be to make the &lt;code&gt;#:value-&amp;gt;text&lt;/code&gt;
argument smarter and have it pass the current text to the rendering
function when it has an arity of two. That way the rendering function
can decide whether or not it needs to change the text. I'll have to
experiment with that.&lt;/p&gt;&lt;/article&gt;</description></item><item><title>(eleventh RacketCon) talk: Declarative GUIs</title><link>https://defn.io/2021/11/07/racketcon-talk-2021</link><guid>https://defn.io/2021/11/07/racketcon-talk-2021</guid><pubDate>Sun, 7 Nov 2021 16:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;Yesterday, I gave a talk about &lt;a href="https://github.com/Bogdanp/racket-gui-easy"&gt;gui-easy&lt;/a&gt; at the eleventh RacketCon!
You can find a recording of the talk on &lt;a href="https://www.youtube.com/watch?v=7uGJJmjcxzY"&gt;YouTube&lt;/a&gt; and a
transcript below.  Day two of the conference is starting in a little
under a couple of hours so &lt;a href="https://con.racket-lang.org/"&gt;join us&lt;/a&gt; if you like!&lt;/p&gt;&lt;h2&gt;Transcript&lt;/h2&gt;&lt;h3&gt;Declarative GUIs&lt;/h3&gt;&lt;p&gt;&lt;img alt="Slide 1." src="/img/racketcon2021-slides/slide-01.jpg"/&gt;&lt;/p&gt;&lt;p&gt;My name is Bogdan Popa, and today I will be talking about &lt;code&gt;gui-easy&lt;/code&gt;,
a library for declaratively building graphical user interfaces in
Racket.&lt;/p&gt;&lt;h3&gt;racket/gui&lt;/h3&gt;&lt;p&gt;&lt;img alt="Slide 2." src="/img/racketcon2021-slides/slide-02.jpg"/&gt;&lt;/p&gt;&lt;p&gt;Racket comes with &lt;code&gt;racket/gui&lt;/code&gt; as part of its main distribution. The
&lt;code&gt;racket/gui&lt;/code&gt; library is a toolkit for building cross-platform
graphical user interfaces.  It's powerful and flexible, having been
used to implement the DrRacket IDE.  One of the reasons for that
flexibility is that it's built on top of the &lt;code&gt;racket/class&lt;/code&gt; library.
The downside of that is that it exposes an imperative API.
Additionally, it is agnostic concerning state management, which means
it's up to you to decide how you're going to keep track of state
within your application and how you're going to keep the GUI and the
application's state in sync.&lt;/p&gt;&lt;h3&gt;gui-easy&lt;/h3&gt;&lt;p&gt;&lt;img alt="Slide 3." src="/img/racketcon2021-slides/slide-03.jpg"/&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;gui-easy&lt;/code&gt; is my attempt at adding a declarative layer on top of
&lt;code&gt;racket/gui&lt;/code&gt;.  It achieves this in two ways.  Firstly, by hiding the
details of the class system from the user so that regular function
calls form the view hierarchy.  Secondly, by providing an abstraction
for managing state and automatically propagating state changes to
views.  These two properties make it less flexible than &lt;code&gt;racket/gui&lt;/code&gt;.
In particular, you cannot opt out of its state management abstraction.&lt;/p&gt;&lt;h3&gt;Counter&lt;/h3&gt;&lt;p&gt;&lt;img alt="Slide 4." src="/img/racketcon2021-slides/slide-04.jpg"/&gt;&lt;/p&gt;&lt;p&gt;Here is an example application built with &lt;code&gt;racket/gui&lt;/code&gt; on the left and
&lt;code&gt;gui-easy&lt;/code&gt; on the right. I can run both, and both produce roughly the
same result.&lt;/p&gt;&lt;p&gt;The &lt;code&gt;racket/gui&lt;/code&gt; version constructs the UI hierarchy incrementally by
instantiating each widget individually and passing them around as
parents of other widgets.  The frame holds the panel, and the panel
contains the two buttons and the message.  In contrast, the &lt;code&gt;gui-easy&lt;/code&gt;
version has a closer correspondence between the final structure of the
UI and the structure of the code.  The window holds the panel, which
holds the other three views.&lt;/p&gt;&lt;p&gt;The application state is managed in the &lt;code&gt;racket/gui&lt;/code&gt; version using a
mutable variable and a function that mutates that variable.  In
addition to changing the counter's value, the &lt;code&gt;update-count!&lt;/code&gt; function
is in charge of updating the message to reflect the change.&lt;/p&gt;&lt;p&gt;In the &lt;code&gt;gui-easy&lt;/code&gt; version, an observable wraps the counter, and the
library takes care of propagating changes to the relevant views (in
this case, the text view).&lt;/p&gt;&lt;h3&gt;Views&lt;/h3&gt;&lt;p&gt;&lt;img alt="Slide 5." src="/img/racketcon2021-slides/slide-05.jpg"/&gt;&lt;/p&gt;&lt;p&gt;Views are regular Racket functions that combine to form the GUI
hierarchy.  They know how to respond to Observable changes in ways
that make sense for the respective widgets they represent.  For
example, text views change their text when their input changes.
Choice views change their current choice when their selection changes,
and canvas views call their draw functions when their data changes.&lt;/p&gt;&lt;h3&gt;Observables&lt;/h3&gt;&lt;p&gt;&lt;img alt="Slide 6." src="/img/racketcon2021-slides/slide-06.jpg"/&gt;&lt;/p&gt;&lt;p&gt;An observable is like a box that can broadcast changes to its contents
to observer functions.  We can define an observable value, then
subscribe a couple of functions to it.  When we push a change to the
observable, the two observers trigger.  In this case, both print the
new value of the observable to standard out.&lt;/p&gt;&lt;p&gt;&lt;img alt="Slide 7." src="/img/racketcon2021-slides/slide-07.jpg"/&gt;&lt;/p&gt;&lt;p&gt;The &lt;code&gt;obs-map&lt;/code&gt; function produces derived observables by applying a
function to the contents of an existing observable.  Just like regular
observables, we can observe derived ones.  If we push a change to the
original &lt;code&gt;@count&lt;/code&gt; now, we can see both its observers trigger and the
observer we added to the derived one.&lt;/p&gt;&lt;p&gt;&lt;img alt="Slide 8." src="/img/racketcon2021-slides/slide-08.jpg"/&gt;&lt;/p&gt;&lt;p&gt;While you can observe mapped observables, you cannot update
them.  Doing so results in a contract error.&lt;/p&gt;&lt;h3&gt;Custom Views&lt;/h3&gt;&lt;p&gt;&lt;img alt="Slide 9." src="/img/racketcon2021-slides/slide-09.jpg"/&gt;&lt;/p&gt;&lt;p&gt;Sometimes you may need to implement custom views. Doing this is
straightforward.  Views in &lt;code&gt;gui-easy&lt;/code&gt; implement the &lt;code&gt;view&amp;lt;%&amp;gt;&lt;/code&gt;
interface.  The interface is just four methods.  Every &lt;code&gt;view&amp;lt;%&amp;gt;&lt;/code&gt; must
be able to list its dependencies.  Its &lt;code&gt;create&lt;/code&gt; method must instantiate
the underlying &lt;code&gt;racket/gui&lt;/code&gt; widget.  It needs to know how to respond to
changes in its dependencies and alter the underlying &lt;code&gt;racket/gui&lt;/code&gt;
widget.  When it's no longer needed, it can perform any teardown
actions it needs to in its &lt;code&gt;destroy&lt;/code&gt; method.&lt;/p&gt;&lt;p&gt;&lt;img alt="Slide 10." src="/img/racketcon2021-slides/slide-10.jpg"/&gt;&lt;/p&gt;&lt;p&gt;Here is a custom text view.  It depends on an observable message.  To
create the underlying widget, it instantiates a &lt;code&gt;message%&lt;/code&gt;.  When the
&lt;code&gt;@msg&lt;/code&gt; observable changes, it updates the label on the &lt;code&gt;message%&lt;/code&gt;
widget, and it doesn't need to perform any teardown actions, so its
&lt;code&gt;destroy&lt;/code&gt; method is a no-op.  Once we have the view implementation, we
can declare a constructor function to hide away the class details from
users, and then we can use the new view just like we would any of the
views built into &lt;code&gt;gui-easy&lt;/code&gt;.&lt;/p&gt;&lt;h2&gt;Demo&lt;/h2&gt;&lt;p&gt;&lt;img alt="Slide 11." src="/img/racketcon2021-slides/slide-11.jpg"/&gt;&lt;/p&gt;&lt;p&gt;Next, I will live-code a small GUI to give you a feel for what it's
like to use the library in practice.&lt;/p&gt;&lt;p&gt;&lt;center&gt;&lt;p&gt;&lt;em&gt;[No transcript for the &lt;a href="https://www.youtube.com/watch?v=7uGJJmjcxzY#t=8m7s"&gt;demo portion&lt;/a&gt;, sorry!]&lt;/em&gt;&lt;/p&gt;&lt;/center&gt;&lt;/p&gt;&lt;h2&gt;Thanks&lt;/h2&gt;&lt;p&gt;&lt;img alt="Slide 12." src="/img/racketcon2021-slides/slide-12.jpg"/&gt;&lt;/p&gt;&lt;p&gt;Thank you for attending my talk.  The &lt;a href="https://pkgd.racket-lang.org/pkgn/package/gui-easy"&gt;library&lt;/a&gt; and its
&lt;a href="https://docs.racket-lang.org/gui-easy/index.html"&gt;documentation&lt;/a&gt; are available on the package server, and you can
find the source code on my website at &lt;a href="https://defn.io"&gt;defn.io&lt;/a&gt;.  Alongside the source
code, you will find several &lt;a href="https://github.com/Bogdanp/racket-gui-easy/tree/master/examples"&gt;example&lt;/a&gt; applications, so I
encourage you to check those out if &lt;code&gt;gui-easy&lt;/code&gt; appeals to you.
Thanks!&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Announcing dbg</title><link>https://defn.io/2021/10/04/ann-dbg</link><guid>https://defn.io/2021/10/04/ann-dbg</guid><pubDate>Mon, 4 Oct 2021 09:10:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;I recently started working on a remote debugging/monitoring tool for
Racket programs. It comes with a TCP server for exposing debugging
information, a client implementation, and a GUI that builds upon
those two things. You run the server as part of your application and
then connect to it via the UI to debug things. Currently, it provides
insights into GC pauses, current memory usage by data type, and a way to
run and visualize performance profiles.&lt;/p&gt;&lt;p&gt;Below, you can see a short demo of dbg in action. The library is
available on the package server and you can find the source code on
&lt;a href="https://github.com/Bogdanp/racket-dbg"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;center&gt;&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube-nocookie.com/embed/KqRq1t9Ey8k" title="YouTube video player" width="560"&gt;&lt;/iframe&gt;&lt;/center&gt;&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Announcing gui-easy</title><link>https://defn.io/2021/08/01/ann-gui-easy</link><guid>https://defn.io/2021/08/01/ann-gui-easy</guid><pubDate>Sun, 1 Aug 2021 10:25:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;These past couple of months, I've been working on a library for building
declarative GUIs in Racket. It's called &lt;a href="https://github.com/Bogdanp/racket-gui-easy"&gt;gui-easy&lt;/a&gt; and it works by
wrapping &lt;code&gt;racket/gui&lt;/code&gt;. See the &lt;a href="https://github.com/Bogdanp/racket-gui-easy/tree/master/examples"&gt;examples&lt;/a&gt; directory to get an idea about
how it's meant to be used, or watch the demo below:&lt;/p&gt;&lt;p&gt;&lt;center&gt;&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube-nocookie.com/embed/AXJ9tTVGDwU" title="YouTube video player" width="560"&gt;&lt;/iframe&gt;&lt;/center&gt;&lt;/p&gt;&lt;p&gt;The library is still a work in progress so expect some breaking changes.
If you use it, let me know what you think!&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Improvements in koyo 0.9</title><link>https://defn.io/2021/07/30/koyo-0.9-improvements</link><guid>https://defn.io/2021/07/30/koyo-0.9-improvements</guid><pubDate>Fri, 30 Jul 2021 10:40:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;Recently, &lt;a href="https://github.com/djholtby"&gt;Daniel Holtby&lt;/a&gt; &lt;a href="https://github.com/racket/racket/pull/3926"&gt;improved&lt;/a&gt; the implementation of
&lt;a href="https://docs.racket-lang.org/reference/interactive.html#%28mod-path._racket%2Frerequire%29"&gt;&lt;code&gt;racket/rerequire&lt;/code&gt;&lt;/a&gt;, which, in turn, inspired me to improve
koyo's own code-reloading implementation. Version 0.9 (released today)
no longer restarts the application process on every change and, instead,
uses &lt;code&gt;dynamic-rerequire&lt;/code&gt; to only reload the modules that change as well
as any modules that depend on them. On Matchacha, which is about 11k
lines of Racket code (excluding whitespace), this improves reload times
by 5 to 10x, depending on the modules being reloaded.&lt;/p&gt;&lt;p&gt;Additionally, I added support for augmenting existing REPL
sessions with the functionality of &lt;code&gt;raco koyo console&lt;/code&gt; via
&lt;a href="https://koyoweb.org/console/index.html#%28def._%28%28lib._koyo%2Fconsole..rkt%29._start-console-here%29%29"&gt;&lt;code&gt;start-console-here&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;You can see the two changes in action in &lt;a href="https://www.youtube.com/watch?v=wWj7OPvXGgA"&gt;this short
screencast&lt;/a&gt;.&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Screencast: Writing a Resource Pool Library for Racket</title><link>https://defn.io/2021/04/06/resource-pool-screencast</link><guid>https://defn.io/2021/04/06/resource-pool-screencast</guid><pubDate>Tue, 6 Apr 2021 07:42:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;After hacking on &lt;a href="https://github.com/bogdanp/racket-redis"&gt;redis-lib&lt;/a&gt; for a bit on Sunday, I decided to write
a general-purpose resource pooling library that I can re-use between
it and &lt;a href="https://github.com/bogdanp/racket-http-easy"&gt;http-easy&lt;/a&gt; and I recorded the process. You can check it out on
&lt;a href="https://www.youtube.com/watch?v=qzvZoiIxbmE"&gt;YouTube&lt;/a&gt;:&lt;/p&gt;&lt;p&gt;&lt;center&gt;&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube-nocookie.com/embed/qzvZoiIxbmE" title="YouTube video player" width="560"&gt;&lt;/iframe&gt;&lt;/center&gt;&lt;/p&gt;&lt;p&gt;You can find the library on &lt;a href="https://github.com/bogdanp/racket-resource-pool"&gt;GitHub&lt;/a&gt;. One particularly interesting
bit about the library, that I did not to record, is that &lt;a href="https://github.com/Bogdanp/racket-resource-pool/blob/c6e82f0cb610f32beeef700ce897f613cb732fb6/resource-pool/test/data/pool.rkt"&gt;the
tests&lt;/a&gt; are all property-based. I might do another screencast at
some point to talk about how they work and the bugs they found in my
original implementation (from the video).&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Screencast: Building a Redis Session Store for Koyo</title><link>https://defn.io/2021/04/04/koyo-sessions-redis-screencast</link><guid>https://defn.io/2021/04/04/koyo-sessions-redis-screencast</guid><pubDate>Sun, 4 Apr 2021 13:37:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;I decided to write &lt;a href="https://github.com/bogdanp/koyo-sessions-redis"&gt;a library&lt;/a&gt; for storing &lt;a href="https://koyoweb.org"&gt;koyo&lt;/a&gt; sessions in Redis
today and I recorded the process. If that sounds appealing, you can
check it out on &lt;a href="https://www.youtube.com/watch?v=zMBHmKn0Nwc"&gt;YouTube&lt;/a&gt;:&lt;/p&gt;&lt;p&gt;&lt;center&gt;&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" frameborder="0" height="315" src="https://www.youtube-nocookie.com/embed/zMBHmKn0Nwc" title="YouTube video player" width="560"&gt;&lt;/iframe&gt;&lt;/center&gt;&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Running Racket CS on iOS</title><link>https://defn.io/2021/01/19/racket-cs-on-ios</link><guid>https://defn.io/2021/01/19/racket-cs-on-ios</guid><pubDate>Tue, 19 Jan 2021 10:25:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;&lt;b&gt;As of iOS 14.4, non-debugged builds (i.e. ones run outside of XCode)
fail with a dynamic code signing error and there is no way to work
around this at the moment.&lt;/b&gt;&lt;/p&gt;&lt;p&gt;A couple of weeks ago, I started working on getting Racket CS to compile
and run on iOS and, with a lot of guidance from Matthew Flatt, I managed
to get it working (with some &lt;a href="https://github.com/racket/racket/blob/351c0047d6371e36cf422b4627e020d14e8853fe/racket/src/ChezScheme/c/segment.c#L578-L587"&gt;caveats&lt;/a&gt;). &lt;a href="https://github.com/racket/racket/pull/3607"&gt;Those changes&lt;/a&gt; have now
been merged, so I figured I'd write another &lt;a href="/2020/01/05/racket-on-ios/"&gt;one of these guides&lt;/a&gt;
while the information is still fresh in my head.&lt;/p&gt;&lt;h2&gt;Compile Racket for macOS and for iOS&lt;/h2&gt;&lt;p&gt;To build Racket for iOS, clone the &lt;a href="https://github.com/racket/racket"&gt;Racket repository&lt;/a&gt; and follow the
cross-compilation instructions under "racket/src/README.txt". The
easiest approach is to create a "build" directory under "racket/src",
then configure the build from within that directory by running&lt;/p&gt;&lt;pre&gt;&lt;code info="language-bash"&gt;../configure \
  --host=aarch64-apple-darwin \
  --enable-ios=iPhoneOS \
  --enable-racket=auto
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;and then&lt;/p&gt;&lt;pre&gt;&lt;code&gt;make &amp;amp;&amp;amp; make install
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;to compile Racket and set up a distribution. After running this series
of commands, you should end up with a cross-compiled Racket
distribution at "racket/" inside the source repository. Additionally,
under "racket/src/build/local/", you'll have a compiled version of
Racket CS for your host machine. You'll use that version of Racket to
cross-compile Racket sources for iOS.&lt;/p&gt;&lt;h2&gt;Cross-compile Racket modules for iOS&lt;/h2&gt;&lt;p&gt;I added a section on &lt;a href="https://www.cs.utah.edu/plt/snapshots/current/doc/inside/ios-cross-compilation.html?q=inside"&gt;how to cross-compile Racket
modules&lt;/a&gt; to the "Inside Racket" docs so refer to that.
In short, if you save the following module under "app.rkt" somewhere&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;#lang racket/base

(provide echo)

(define (echo m)
  (displayln m))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;then you can run&lt;/p&gt;&lt;pre&gt;&lt;code info="language-bash"&gt;/path/to/racket/src/build/local/cs/c/racketcs \
  --cross-compiler tarm64osx /path/to/racket/racket/lib \
  -MCR /path/to/racket/src/build/cs/c/compiled: \
  -G /path/to/racket/racket/etc \
  -X /path/to/racket/racket/collects \
  -l- \
  raco ctool --mods app.zo app.rkt
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;to produce "app.zo", a binary object containing the cross-compiled
code for that module and all of its dependencies.&lt;/p&gt;&lt;h2&gt;Set up your XCode project&lt;/h2&gt;&lt;p&gt;To link against and use Racket CS within an XCode project, copy
"racketcs.h", "racketcsboot.h" and "chezscheme.h" from "racket/include/"
into a sub-directory of your project, then add that sub-directory to the
"Header Search Paths" section under your project's "Build Settings" tab.&lt;/p&gt;&lt;p&gt;&lt;img src="/img/racket-cs-on-ios-headers.png" alt="Headers" title=""/&gt;&lt;/p&gt;&lt;p&gt;Then, disable Bitcode from the same section.&lt;/p&gt;&lt;p&gt;&lt;img src="/img/racket-cs-on-ios-bitcode.png" alt="Bitcode" title=""/&gt;&lt;/p&gt;&lt;p&gt;Next, copy "libracketcs.a", "petite.boot", "scheme.boot" and
"racket.boot" from "racket/lib" into a sub-directory of your project
called "vendor/" and drag-and-drop the "vendor/" directory into your
XCode project. Then, instruct XCode to link "libracketcs.a" and
"libiconv.tbd" with your code from the "Build Phases" tab. You'll
have to add "libracketcs.a" to your project using the "Add Other..."
sub-menu.&lt;/p&gt;&lt;p&gt;&lt;img src="/img/racket-cs-on-ios-link.png" alt="Link" title=""/&gt;&lt;/p&gt;&lt;p&gt;Next, add a new C source file called "vendor.c" and answer "yes" if
prompted to create a bridging header for Swift. I tend to re-name the
bridging header to plain "bridge.h" because I don't like the name that
XCode generates by default. If you do this, you'll have to update the
"Objective-C Bridging Header" setting in your "Build Settings" tab. From
"bridge.h", include "vendor.h" and inside "vendor.h" add definitions for
&lt;code&gt;racket_init&lt;/code&gt; and &lt;code&gt;echo&lt;/code&gt;&lt;/p&gt;&lt;pre&gt;&lt;code info="language-c"&gt;#ifndef vendor_h
#define vendor_h

#include &amp;lt;stdlib.h&amp;gt;

int racket_init(const char *, const char *, const char *, const char *);
void echo(const char *);

#endif
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;then, inside of &lt;code&gt;vendor.c&lt;/code&gt;, implement them&lt;/p&gt;&lt;pre&gt;&lt;code info="language-c"&gt;#include &amp;lt;string.h&amp;gt;

#include "chezscheme.h"
#include "racketcs.h"

#include "vendor.h"

int racket_init(const char *petite_path,
                const char *scheme_path,
                const char *racket_path,
                const char *app_path) {
    racket_boot_arguments_t ba;
    memset(&amp;amp;ba, 0, sizeof(ba));
    ba.boot1_path = petite_path;
    ba.boot2_path = scheme_path;
    ba.boot3_path = racket_path;
    ba.exec_file = "example";
    racket_boot(&amp;amp;ba);
    racket_embedded_load_file(app_path, 1);
    ptr mod = Scons(Sstring_to_symbol("quote"), Scons(Sstring_to_symbol("main"), Snil));
    racket_dynamic_require(mod, Sfalse);
    Sdeactivate_thread();
    return 0;
}

void echo(const char *message) {
    Sactivate_thread();
    ptr mod = Scons(Sstring_to_symbol("quote"), Scons(Sstring_to_symbol("main"), Snil));
    ptr echo_fn = Scar(racket_dynamic_require(mod, Sstring_to_symbol("echo")));
    racket_apply(fn, Scons(Sstring(message), Snil));
    Sdeactivate_thread();
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Take a look at the &lt;a href="https://www.cs.utah.edu/plt/snapshots/current/doc/inside/cs.html?q=inside"&gt;Inside Racket CS&lt;/a&gt; documentation for details on the
embedding interface of Racket CS.  The gist of &lt;code&gt;racket_init&lt;/code&gt; is that
it takes the paths to "petite.boot", "scheme.boot", "racket.boot" and
"app.zo" as arguments in order to initialize Racket and then load the
"app.zo" module, which you can do from the &lt;code&gt;AppDelegate&lt;/code&gt;'s
&lt;code&gt;application(_:didFinishLaunchingWithOptions:)&lt;/code&gt; method:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-swift"&gt;let vendorPath = Bundle.main.resourcePath!.appending("/vendor")
let ok = racket_init(
    vendorPath.appending("/petite.boot"),
    vendorPath.appending("/scheme.boot"),
    vendorPath.appending("/racket.boot"),
    vendorPath.appending("/app.zo"))
if ok != 0 {
    print("failed to initialize racket")
    exit(1)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Upon successful initialization, you should be able to call the Racket &lt;code&gt;echo&lt;/code&gt;
function from Swift:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-swift"&gt;echo("Hello from Racket!".cString(using: .utf8))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Compile and run the project on a device and you should see "Hello from
Racket!" get printed in your debug console.&lt;/p&gt;&lt;h3&gt;Some XCode gotchas&lt;/h3&gt;&lt;p&gt;If you copy "vendor/" into your project instead of creating "folder
references" when you drag-and-drop it, then code signing may fail with
an ambiguous error.&lt;/p&gt;&lt;p&gt;Avoid using symbolic links for any of your resources (like the stuff
in "vendor/").  Doing so makes copying the code over to the device
fail with a "security" error that doesn't mention the root problem at
all.&lt;/p&gt;&lt;/article&gt;</description></item><item><title>neko.app</title><link>https://defn.io/2021/01/02/neko-app</link><guid>https://defn.io/2021/01/02/neko-app</guid><pubDate>Sat, 2 Jan 2021 02:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;I was watching &lt;a href="https://www.youtube.com/channel/UCrW38UKhlPoApXiuKNghuig"&gt;Systems with JT&lt;/a&gt; the other day and he demoed a hobby
operating system called &lt;a href="https://github.com/skiftOS/skift"&gt;skiftOS&lt;/a&gt;. During the demo he ran one of the
built-in apps called "neko" which looks like a clone of an old Windows
"pet" program I remember from my childhood, also called "neko" (or
"neko32").&lt;/p&gt;&lt;p&gt;It's a really simple program: when you start it up, a cute little kitten
shows up on your screen and starts running around, trying to catch your
mouse cursor. I figured it would be fun to clone the program for macOS
and so I went ahead and spent most of yesterday doing it. &lt;a href="https://github.com/bogdanp/neko"&gt;Here's&lt;/a&gt;
the code and if you're feeling nostalgic and want to run it yourself,
you can grab a build from the releases tab.&lt;/p&gt;&lt;p&gt;&lt;center&gt;&lt;img alt="demo" src="/img/neko.gif"/&gt;&lt;/center&gt;&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Racketeering Gophers</title><link>https://defn.io/2020/11/17/racketeering-gophers</link><guid>https://defn.io/2020/11/17/racketeering-gophers</guid><pubDate>Tue, 17 Nov 2020 12:30:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;&lt;center&gt;&lt;img alt="rocketeering gopher" src="/img/rocketeering-gopher.svg"/&gt;&lt;br/&gt;&lt;p&gt;&lt;em&gt;Close enough.&lt;/em&gt;&lt;/p&gt;&lt;/center&gt;&lt;/p&gt;&lt;p&gt;I've been working on a &lt;a href="https://webassembly.org/"&gt;Wasm&lt;/a&gt; implementation &lt;a href="https://github.com/bogdanp/racket-wasm"&gt;in Racket&lt;/a&gt;
for the past couple of weeks and have recently reached a neat
milestone.&lt;/p&gt;&lt;p&gt;I can take this Go program,&lt;/p&gt;&lt;pre&gt;&lt;code info="language-go"&gt;package main

import (
        "log"
        "net/http"
)

func main() {
        log.Println("GETing https://defn.io...")
        resp, err := http.Get("https://defn.io")
        if err != nil {
                log.Fatal(err)
        }
        defer resp.Body.Close()
        log.Println(resp.Status)
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;compile it to Wasm&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ env GOARCH=wasm GOOS=js go build -o http.wasm http.go
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;and end up with a 7MiB Wasm file&lt;/p&gt;&lt;pre&gt;&lt;code&gt;-rwxr-xr-x  1 bogdan  staff   7.1M Nov 17 12:43 http.wasm*
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;that this Racket program can run:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;#lang racket/base

(require wasm/private/binary
         wasm/private/validation
         wasm/private/vm
         "go-runtime.rkt")

;; Read the module.
(define m (call-with-input-file "http.wasm" read-wasm))

;; Typecheck.
(define-values (valid? error-message)
  (mod-valid? m))
(unless valid? (error error-message))

;; Create an interpreter.
(define v (make-vm m (hash "go" *go*)))

;; Grab the entrypoint and run it.
;; run(argc, argv)
(define run (vm-ref v "run" #f))
(parameterize ([current-vm v])
  (run 0 0))
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;$ racket run.rkt
2020/11/17 12:48:19 GETing https://defn.io...
2020/11/17 12:48:19 200 OK
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Tada!  The compiled Go code requires some runtime support that's
specific to it, which is where the &lt;a href="https://gist.github.com/Bogdanp/c4754c49dad09612a0bc3f84b342644b"&gt;&lt;code&gt;go-runtime.rkt&lt;/code&gt;&lt;/a&gt;
module above comes in.  I've only implemented the parts of the Go
runtime support that I needed to get the above program to work and
that code is pretty bad, but it gets the job done as a test for the
Wasm implementation.&lt;/p&gt;&lt;p&gt;There's still a lot to do until this is ready to be used by others
(note the lack of any sort of public API or documentation so far), but
I thought this was a cool little result worth sharing.&lt;/p&gt;&lt;p&gt;&lt;hr/&gt;&lt;/p&gt;&lt;p&gt;Credit: &lt;i&gt;Rocketeering Gopher&lt;/i&gt; by &lt;a href="https://github.com/egonelbre/gophers"&gt;Egon Elbre on GitHub&lt;/a&gt;.&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Racket Web Development with Koyo</title><link>https://defn.io/2020/10/18/web-dev-with-koyo</link><guid>https://defn.io/2020/10/18/web-dev-with-koyo</guid><pubDate>Sun, 18 Oct 2020 21:00:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;Inspired by &lt;a href="https://www.youtube.com/watch?v=bIi-tUzOwdw"&gt;Brian Adkins' RacketCon talk&lt;/a&gt; from yesterday, I
decided to record a screencast on what it's like to write a little
web application using my not-quite-a-web-framework, &lt;a href="https://github.com/Bogdanp/koyo"&gt;koyo&lt;/a&gt;. You can
watch it over on &lt;a href="https://www.youtube.com/watch?v=DS_0-lqiSVs"&gt;YouTube&lt;/a&gt; and you can find the resulting code
on &lt;a href="https://github.com/Bogdanp/koyo-shorty"&gt;GitHub&lt;/a&gt;. It's unscripted and I don't go too deep on how
everything works, but hopefully it's easy enough to follow and I've left
the various mistakes I've made in since it's usually helpful to watch
someone get out of a tricky situation so look forward to those if you
watch it!&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Deploying Racket Web Apps</title><link>https://defn.io/2020/06/28/racket-deployment</link><guid>https://defn.io/2020/06/28/racket-deployment</guid><pubDate>Sun, 28 Jun 2020 15:00:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;Someone recently asked about how to deploy Racket web apps on the
Racket Slack.  The most common answers were&lt;/p&gt;&lt;ol class="tight" start="1"&gt;&lt;li&gt;install Racket on the target machine, then ship your code there or&lt;/li&gt;&lt;li&gt;use Docker (basically a "portable" variant of option 1).&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;I wanted to take a few minutes today and write about my preferred way
of deploying Racket apps: build an executable with the application
code, libraries and assets embedded into it and ship that around.  I
prefer this approach because it means I don't have to worry about
installing a specific version of Racket on the target machine just to
run my code.  In fact, using this approach I can have different
versions of each application, each built with a different version of
Racket and easily switch between them.&lt;/p&gt;&lt;p&gt;&lt;a href="https://docs.racket-lang.org/raco/exe.html"&gt;raco exe&lt;/a&gt; embeds Racket modules along with the runtime into native
executables for the platform it's run on.  Take this program for
example:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;#lang racket/base

(require racket/async-channel
         web-server/http
         web-server/servlet-dispatch
         web-server/web-server)

(define ch (make-async-channel))
(define stop
  (serve
   #:dispatch (dispatch/servlet
               (lambda (_req)
                 (response/xexpr
                  '(h1 "Hello!"))))
   #:port 8000
   #:listen-ip "127.0.0.1"
   #:confirmation-channel ch))

(define ready-or-exn (sync ch))
(when (exn:fail? ready-or-exn)
  (raise ready-or-exn))

(with-handlers ([exn:break?
                 (lambda (_)
                   (stop))])
  (sync/enable-break never-evt))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If I save it to a file called &lt;code&gt;app.rkt&lt;/code&gt; and then call &lt;code&gt;raco exe -o app app.rkt&lt;/code&gt;, I'll end up with a self-contained executable called &lt;code&gt;app&lt;/code&gt; in
the current directory.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ file app
app: Mach-O 64-bit executable x86_64
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The resulting executable may still refer to dynamic libraries only
available on the current machine so it's not quite ready for
distribution at this stage.  That's where &lt;a href="https://docs.racket-lang.org/raco/exe-dist.html"&gt;raco distribute&lt;/a&gt; comes in.
It takes a stand-alone executable created by &lt;code&gt;raco exe&lt;/code&gt; and generates
a package containing the executable, dynamic libraries referenced by
it and any run-time files referenced by the app (more on this in a
sec).  The resulting package can then be copied over to other machines
running the same operating system.&lt;/p&gt;&lt;p&gt;Running &lt;code&gt;raco distribute dist app&lt;/code&gt; produces a directory with the
following contents:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ raco distribute dist app
$ tree dist/
dist/
├── bin
│   └── app
└── lib
    ├── Racket.framework
    │   └── Versions
    │       └── 7.7.0.9_CS
    │           ├── Racket
    │           └── boot
    │               ├── petite.boot
    │               ├── racket.boot
    │               └── scheme.boot
    └── plt
        └── app
            └── exts
                └── ert
                    ├── r0
                    │   └── error.css
                    ├── r1
                    │   ├── libcrypto.1.1.dylib
                    │   └── libssl.1.1.dylib
                    └── r2
                        └── bundles
                            ├── es
                            │   └── srfi-19
                            └── srfi-19

15 directories, 10 files
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I can take that directory, zip it up and ship it to any other machine
running the same version of macOS as I am and it will run unmodified.
The same would be true if I built the code on a Linux machine and then
shipped it to other Linux machines to run on and that's exactly what I
do when I distribute my web apps.  I have a CI job in every project
that builds and tests the code, then generates distributions that it
copies to the destination servers.&lt;/p&gt;&lt;p&gt;At this point you might be thinking "that's nice, but what about files
needed by the app at run-time?"  Let's modify the app so it reads a
file from disk then serves its contents on request:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;#lang racket/base

(require racket/async-channel
         racket/port
         web-server/http
         web-server/servlet-dispatch
         web-server/web-server)

(define text
  (call-with-input-file "example.txt" port-&amp;gt;string))

(define ch (make-async-channel))
(define stop
  (serve
   #:dispatch (dispatch/servlet
               (lambda (_req)
                 (response/xexpr
                  `(h1 ,text))))
   #:port 8000
   #:listen-ip "127.0.0.1"
   #:confirmation-channel ch))

(define ready-or-exn (sync ch))
(when (exn:fail? ready-or-exn)
  (raise ready-or-exn))

(with-handlers ([exn:break?
                 (lambda (_)
                   (stop))])
  (sync/enable-break never-evt))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If I just take this app, build an executable and then a distribution
then try to run it, I'll run into a problem:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ raco exe -o app app.rkt
$ raco distribute dist app
$ cd dist
$ ./bin/app
open-input-file: cannot open input file
  path: /Users/bogdan/tmp/dist/example.txt
  system error: No such file or directory; errno=2
  context...:
   raise-filesystem-error
   open-input-file
   call-with-input-file
   proc
   call-in-empty-metacontinuation-frame
   call-with-module-prompt
   body of '#%mzc:s
   temp35_0
   run-module-instance!
   perform-require!
   call-in-empty-metacontinuation-frame
   eval-one-top
   eval-compiled-parts
   embedded-load
   proc
   call-in-empty-metacontinuation-frame
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Had I not &lt;code&gt;cd&lt;/code&gt;'d into the &lt;code&gt;dist&lt;/code&gt; directory, this would've worked,
because &lt;code&gt;example.txt&lt;/code&gt; would've been in the working directory where the
application would have been run from.  The problem is we're passing a
path to &lt;code&gt;call-with-input-file&lt;/code&gt; that Racket doesn't know anything about
at compile time.&lt;/p&gt;&lt;p&gt;To ship the &lt;code&gt;example.txt&lt;/code&gt; file along with the application, we have to
use &lt;a href="https://docs.racket-lang.org/reference/Filesystem.html#%28form._%28%28lib._racket%2Fruntime-path..rkt%29._define-runtime-path%29%29"&gt;&lt;code&gt;define-runtime-path&lt;/code&gt;&lt;/a&gt; to tell Racket that it should embed the
file in the distribution and update the code so that it references the
embedded file's eventual path.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-diff"&gt; #lang racket/base

 (require racket/async-channel
          racket/port
+         racket/runtime-path
          web-server/http
          web-server/servlet-dispatch
          web-server/web-server)
+
+(define-runtime-path example-path "example.txt")

 (define text
-  (call-with-input-file "example.txt" port-&amp;gt;string))
+  (call-with-input-file example-path port-&amp;gt;string))

 (define ch (make-async-channel))
 (define stop
   (serve
    #:dispatch (dispatch/servlet
                (lambda (_req)
                  (response/xexpr
                   `(h1 ,text))))
    #:port 8000
    #:listen-ip "127.0.0.1"
    #:confirmation-channel ch))

 (define ready-or-exn (sync ch))
 (when (exn:fail? ready-or-exn)
   (raise ready-or-exn))

 (with-handlers ([exn:break?
                  (lambda (_)
                    (stop))])
   (sync/enable-break never-evt))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The use of &lt;code&gt;define-runtime-path&lt;/code&gt; in the above code tells &lt;code&gt;raco distribute&lt;/code&gt; to copy &lt;code&gt;example.txt&lt;/code&gt; into the distribution and makes it
so that the &lt;code&gt;example-path&lt;/code&gt; binding refers to the path that file will
eventually have.&lt;/p&gt;&lt;p&gt;If I build a distribution now and inspect its contents, I can see that
&lt;code&gt;example.txt&lt;/code&gt; is copied into it:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ raco exe -o app app.rkt
$ raco distribute dist app
$ tree dist
dist/
├── bin
│   └── app
└── lib
    ├── Racket.framework
    │   └── Versions
    │       └── 7.7.0.9_CS
    │           ├── Racket
    │           └── boot
    │               ├── petite.boot
    │               ├── racket.boot
    │               └── scheme.boot
    └── plt
        └── app
            └── exts
                └── ert
                    ├── r0
                    │   └── example.txt
                    ├── r1
                    │   └── error.css
                    ├── r2
                    │   ├── libcrypto.1.1.dylib
                    │   └── libssl.1.1.dylib
                    └── r3
                        └── bundles
                            ├── es
                            │   └── srfi-19
                            └── srfi-19

16 directories, 11 files
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you want more information about how this all works, the links I
gave for &lt;a href="https://docs.racket-lang.org/raco/exe.html"&gt;raco exe&lt;/a&gt;, &lt;a href="https://docs.racket-lang.org/raco/exe-dist.html"&gt;raco distribute&lt;/a&gt; and &lt;a href="https://docs.racket-lang.org/reference/Filesystem.html#%28form._%28%28lib._racket%2Fruntime-path..rkt%29._define-runtime-path%29%29"&gt;&lt;code&gt;define-runtime-path&lt;/code&gt;&lt;/a&gt;
should have you covered!&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Announcing http-easy</title><link>https://defn.io/2020/06/14/ann-http-easy</link><guid>https://defn.io/2020/06/14/ann-http-easy</guid><pubDate>Sun, 14 Jun 2020 18:00:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;Yesterday I released &lt;a href="https://github.com/bogdanp/racket-http-easy"&gt;http-easy&lt;/a&gt;, a high-level HTTP client for Racket.
I started working on it after getting annoyed at some of the code in
my &lt;a href="https://github.com/Bogdanp/racket-sentry/blob/9794b2da9c4f3ca8c8094d6bc78d5ca8bf9b133b/sentry-lib/sentry.rkt#L102-L147"&gt;racket-sentry&lt;/a&gt; package. The same day I wrote that code,
someone started a mailing list &lt;a href="https://groups.google.com/g/racket-users/c/sMZtC4G0bHw/m/RW_CN5EeAQAJ"&gt;thread&lt;/a&gt; asking for a "practical"
HTTP client so that served as additional motivation to spend some time
on this problem.&lt;/p&gt;&lt;p&gt;Here's a basic example:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(require net/http-easy)
(response-xexpr (get "https://example.com"))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It might not seem like much, but even just that gets you automatic
connection pooling. Want to stream response bodies instead of reading
them up front? Just pass in &lt;code&gt;#t&lt;/code&gt; for the &lt;code&gt;#:stream?&lt;/code&gt; argument:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define inp
  (response-output
   (get "https://example.com" #:stream? #t)))
(read-bytes 10 inp)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Want to &lt;code&gt;POST&lt;/code&gt; some &lt;code&gt;JSON&lt;/code&gt; somewhere? Use the &lt;code&gt;#:json&lt;/code&gt; keyword argument:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(post
 #:json (hasheq 'hello "world")
 "https://example.com")
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Need to upload a file? It's got you covered:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(post
 #:data (multipart-payload
         (file-part "f" (open-input-file "example-1.txt"))
         (file-part "f" (open-input-file "example-2.txt")))
 "https://example.com")
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can find these examples and more in the &lt;a href="https://docs.racket-lang.org/http-easy/index.html"&gt;documentation&lt;/a&gt;. The only
big feature that's currently missing is proxy support, but I plan to add
that soon. The library is pre-1.0 so, if you do start using it, keep in
mind that its API might change before it stabilizes.&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Continuations in Racket's Web Server</title><link>https://defn.io/2020/05/11/racket-web-server-internals</link><guid>https://defn.io/2020/05/11/racket-web-server-internals</guid><pubDate>Mon, 11 May 2020 11:55:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;In &lt;a href="/2020/02/12/racket-web-server-guide/"&gt;The Missing Guide to Racket's Web Server&lt;/a&gt;, I said that
&lt;code&gt;dispatch/servlet&lt;/code&gt; is equivalent to:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(lambda (start)
  (lambda (conn req)
    (output-response conn (start req))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That was an oversimplification.  It does apply its &lt;code&gt;start&lt;/code&gt; argument to
incoming requests and it does take care of writing the responses to
the appropriate connections, but it has another important job: to
handle responses returned from continuations and to dispatch incoming
requests to captured continuations.&lt;/p&gt;&lt;p&gt;With a number of details omitted, the essence of &lt;code&gt;dispatch/servlet&lt;/code&gt; is
actually the following:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define servlet-prompt
  (make-continuation-prompt 'servlet))

(define (dispatch/servlet start)
  (define servlet (make-servlet start))
  (lambda (conn req)
    (output-response conn (call-with-continuation-barrier
                           (lambda ()
                             (call-with-continuation-prompt
                              (lambda ()
                                ((servlet-handler servlet) req))
                              servlet-prompt))))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;First, it creates a &lt;code&gt;servlet&lt;/code&gt; value that wraps the request-handling
function that it is given. The servlet contains some internal state that
maps request URIs to captured continuations. The servlet's &lt;code&gt;handler&lt;/code&gt;
field is what decides which code to run when a request comes in: if the
request URI matches a known continuation, then that continuation is
resumed, otherwise the &lt;code&gt;start&lt;/code&gt; function is applied to the request.&lt;/p&gt;&lt;p&gt;After creating the servlet, it returns a dispatcher that applies the
servlet's handler to the request and writes the resulting response
to the connection. Before it applies the servlet handler, it sets
up a continuation barrier so that continuations captured within the
servlet cannot be resumed from outside of the request-response cycle,
guaranteeing that you can't resume such a continuation when the client
isn't prepared to receive a response. After installing the continuation
barrier, it installs a continuation prompt so that the various "web
interaction" functions can abort to it.&lt;/p&gt;&lt;p&gt;The simplest of the web interaction functions, &lt;code&gt;send/back&lt;/code&gt;, looks like
this:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (send/back resp)
  (abort-current-continuation servlet-prompt (lambda () resp)))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;With that in mind, consider the following request handler:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (hello req)
  (send/back (response/xexpr "sent"))
  (response/xexpr "ignored"))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When execution reaches the &lt;code&gt;send/back&lt;/code&gt; function call, it aborts to the
nearest&lt;sup&gt;&lt;a href="#fn_1" id="fnref_1_1"&gt;1&lt;/a&gt;&lt;/sup&gt; &lt;code&gt;servlet-prompt&lt;/code&gt; handler, which happens to be the one that
&lt;code&gt;dispatch/servlet&lt;/code&gt; installs with &lt;code&gt;call-with-continuation-prompt&lt;/code&gt;, so the
execution of the request handler short circuits and the response passed
to &lt;code&gt;send/back&lt;/code&gt; is immediately sent to the client.&lt;/p&gt;&lt;p&gt;The &lt;code&gt;send/suspend&lt;/code&gt; function, on the other hand, looks roughly&lt;sup&gt;&lt;a href="#fn_2" id="fnref_2_1"&gt;2&lt;/a&gt;&lt;/sup&gt; like
this:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (send/suspend f)
  (call-with-composable-continuation
   (lambda (k)
     (define k-url (store-continuation! k))
     (send/back (f k-url)))
   servlet-prompt))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Rather than immediately sending a response back to the client, it
captures the current continuation, associates it with a URL and then
passes that URL to a function, &lt;code&gt;f&lt;/code&gt;, that generates a response. The
resulting response is then sent back to the client.&lt;/p&gt;&lt;p&gt;Using &lt;code&gt;send/suspend&lt;/code&gt;, you can write request handlers that can be
suspended in the middle of execution and then resumed upon subsequent
requests:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (resumable req)
  (define req-2
    (send/suspend
     (lambda (k-url)
       (response/xexpr
        `(a ([href ,k-url]) "Resume")))))
  (response/xexpr "done"))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When &lt;code&gt;resumable&lt;/code&gt; is executed, the first response is generated and
returned to the client and when the client visits the anchor, the
continuation is resumed from where the first request left off, with
the new request bound to &lt;code&gt;req-2&lt;/code&gt;.&lt;/p&gt;&lt;section class="footnotes"&gt;&lt;ol&gt;&lt;li id="fn_1"&gt;&lt;p&gt;If you're wondering whether or not you can install your own
intermediary &lt;code&gt;servlet-prompt&lt;/code&gt; handlers, the answer is yes! &lt;a class="footnote-backref" href="#fnref_1_1" title="Jump to reference" aria-label="Jump to reference"&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;li id="fn_2"&gt;&lt;p&gt;For clarity, I'm omitting a number of implementation details
once again. &lt;a class="footnote-backref" href="#fnref_2_1" title="Jump to reference" aria-label="Jump to reference"&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/section&gt;&lt;/article&gt;</description></item><item><title>Using GitHub Actions to Test Racket Code (Revised)</title><link>https://defn.io/2020/05/05/github-actions-for-racket-revised</link><guid>https://defn.io/2020/05/05/github-actions-for-racket-revised</guid><pubDate>Tue, 5 May 2020 10:00:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;A little over a year ago, I &lt;a href="/2019/05/01/github-actions-for-racket"&gt;wrote&lt;/a&gt; about how you could use the
GitHub's new-at-the-time Actions feature to test Racket code. A lot
has changed since then, including the release of a completely revamped
version of GitHub Actions and so I thought it was time for an update.&lt;/p&gt;&lt;h2&gt;A Basic Package&lt;/h2&gt;&lt;p&gt;Let's say you're working on a Racket package for computing Fibonacci
sequences. Your &lt;code&gt;main.rkt&lt;/code&gt; module might look something like this:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;#lang racket/base

(require racket/stream)

(provide
 fibs)

(define (fibs)
  (stream*
   1
   1
   (let ([s (fibs)])
     (for/stream ([x (in-stream s)]
                  [y (in-stream (stream-rest s))])
       (+ x y)))))

(module+ test
  (require rackunit)
  (check-equal? (stream-&amp;gt;list (stream-take (fibs) 8))
                '(1 1 2 3 5 8 13 21)))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You'd like to make it so that every time you push a change to this
package to GitHub the test in this module gets run and you get notified
of any problems that occur. To do that, all you have to do is add a
workflow configuration file under &lt;code&gt;.github/workflows&lt;/code&gt;. The file can be
called anything you like as long as it ends with the &lt;code&gt;yml&lt;/code&gt; extension. In
this case you might call it &lt;code&gt;push.yml&lt;/code&gt;, because its contents will get
run whenever code is pushed to the repository. A basic workflow file
looks like this:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-yml"&gt;on:
  - push

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@master
      - name: Install Racket
        uses: Bogdanp/setup-racket@v1.6.1
        with:
          architecture: 'x64'
          distribution: 'full'
          version: '8.2'
      - name: Run Tests
        run: raco test main.rkt
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This workflow triggers whenever a &lt;code&gt;push&lt;/code&gt; event occurs within the
repository. When that happens, it'll run through its &lt;code&gt;jobs&lt;/code&gt; one-by-one.
The &lt;code&gt;test&lt;/code&gt; job in this workflow sets up a Ubuntu VM where it'll go
through each of its &lt;code&gt;steps&lt;/code&gt; in order.&lt;/p&gt;&lt;p&gt;The first step uses the &lt;a href="https://github.com/actions/checkout"&gt;actions/checkout&lt;/a&gt; action to clone the
repository inside the VM. Once checked out, the working directory for
all the subsequent actions will be in the root of the checked-out
repository, unless otherwise specified within a step.&lt;/p&gt;&lt;p&gt;The second step uses my own &lt;a href="https://github.com/Bogdanp/setup-racket"&gt;Bogdanp/setup-racket&lt;/a&gt; action to install
Racket CS version 8.2 in the VM. This adds the &lt;code&gt;racket&lt;/code&gt; and &lt;code&gt;raco&lt;/code&gt;
executables to the &lt;code&gt;PATH&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Finally, the last step runs the tests in the &lt;code&gt;main.rkt&lt;/code&gt; module.&lt;/p&gt;&lt;h2&gt;Installing Dependencies&lt;/h2&gt;&lt;p&gt;Say you're not that confident in that one test that you have for the
&lt;code&gt;fibs&lt;/code&gt; function and you'll like to throw some property-based testing
in the mix.  Your &lt;code&gt;test&lt;/code&gt; submodule becomes:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(module+ test
  (require rackcheck
           rackunit)

  (check-property
   (property ([n (gen:integer-in 3 100)])
     (define numbers (stream-&amp;gt;list (stream-take (fibs) n)))
     (for ([n (cddr numbers)]
           [y (cdr numbers)]
           [x numbers])
       (check-eqv? (+ x y) n)))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When you push this change, your action will fail because &lt;a href="https://github.com/Bogdanp/rackcheck"&gt;rackcheck&lt;/a&gt;
won't be installed on the VM.  To work around this, you can update the
&lt;code&gt;Install Racket&lt;/code&gt; step to tell it to install &lt;code&gt;rackcheck&lt;/code&gt; for you:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-diff"&gt;       - name: Install Racket
         uses: Bogdanp/setup-racket@v1.6.1
         with:
           architecture: 'x64'
           distribution: 'full'
           version: '8.2'
+          packages: 'rackcheck'
       - name: Run Tests
         run: raco test main.rkt
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A better solution, however, would be to add a &lt;code&gt;info.rkt&lt;/code&gt; file to your
repository to specify what dependencies your package has:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;#lang info

(define build-deps '("rackcheck" "rackunit-lib"))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then, between the &lt;code&gt;Install Racket&lt;/code&gt; and the &lt;code&gt;Run Tests&lt;/code&gt; steps, you can
add another step to install your package into the VM:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-diff"&gt;       - name: Install Racket
         uses: Bogdanp/setup-racket@v1.6.1
         with:
           architecture: 'x64'
           distribution: 'full'
           version: '8.2'
+      - name: Install Package and its Dependencies
+        run: raco pkg install --auto --batch
       - name: Run Tests
         run: raco test main.rkt
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This way, you won't have to worry about updating your workflow every
time you change your dependencies.&lt;/p&gt;&lt;h2&gt;Matrix Testing&lt;/h2&gt;&lt;p&gt;At this point you might be fairly confident that your implementation of
&lt;code&gt;fibs&lt;/code&gt; is correct, but you want to guarantee that it works not only on
Racket CS version 8.2, but also on Racket BC as well. To do this, you
can add a matrix &lt;a href="https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstrategy"&gt;strategy&lt;/a&gt; to your job, specifying that the job should
be parameterized over the &lt;code&gt;racket-variant&lt;/code&gt; values:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-diff"&gt; jobs:
   test:
     runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        racket-variant: ['BC', 'CS']
+    name: Test on ${{ matrix.racket-variant }} Racket
     steps:
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can then update the install step to be parameterized over the
variant:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-diff"&gt;       - name: Install Racket
         uses: Bogdanp/setup-racket@v1.6.1
         with:
           architecture: 'x64'
           distribution: 'full'
+          variant: ${{ matrix.racket-variant }}
           version: '8.2'
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can go one step further and also parameterize the versions of Racket
that you want your tests to run on:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-diff"&gt; jobs:
   test:
     runs-on: ubuntu-latest
     strategy:
       matrix:
         racket-variant: ['BC', 'CS']
+        racket-version: ['8.1', '8.2']
     name: Test on ${{ matrix.racket-variant }} Racket
     steps:
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And then plug that parameter into the install step, as before:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-diff"&gt;       - name: Install Racket
         uses: Bogdanp/setup-racket@v1.6.1
         with:
           architecture: 'x64'
           distribution: 'full'
           variant: ${{ matrix.racket-variant }}
+          version: ${{ matrix.racket-version }}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Following these steps will make it so that every change you push will
get tested against versions 8.1 and 8.2 of both variants of Racket.&lt;/p&gt;&lt;p&gt;This only scratches the surface of what you can do with GH Actions so,
if you're interested to learn more, I'd recommend reading through the
&lt;a href="https://help.github.com/en/actions/reference"&gt;docs&lt;/a&gt;. You can find a working example of everything I've mentioned in
this article in &lt;a href="https://github.com/Bogdanp/racket-actions-example"&gt;this repo&lt;/a&gt;.&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Announcing racksnaps</title><link>https://defn.io/2020/05/03/ann-racksnaps</link><guid>https://defn.io/2020/05/03/ann-racksnaps</guid><pubDate>Sun, 3 May 2020 14:00:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;Racket's package manager doesn't currently have the notion of locking
package sets to specific versions&lt;sup&gt;&lt;a href="#fn_1" id="fnref_1_1"&gt;1&lt;/a&gt;&lt;/sup&gt; per project so, as someone who
operates a couple production Racket applications, I've been concerned
about the possibility that new deployments could introduce bugs in
production due to changing dependencies.&lt;/p&gt;&lt;p&gt;To solve this problem, over the past weekend I've put together a service
that creates daily snapshots of the official package catalog. You can
find it at &lt;a href="https://racksnaps.defn.io"&gt;racksnaps.defn.io&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Every day at 12am UTC, the service queries all the packages on
&lt;a href="https://pkgs.racket-lang.org"&gt;pkgs.racket-lang.org&lt;/a&gt; for metadata and source locations. It then
creates a source package archive for each package whose sources are
still valid.&lt;/p&gt;&lt;p&gt;Once all the source package archives are created, "built" packages
(packages that contain source code, docs and compiled &lt;code&gt;.zo&lt;/code&gt; files) are
created from those archives. Each of these is compiled in isolation and
any packages that don't compile cleanly are excluded from the final
snapshot.&lt;/p&gt;&lt;p&gt;Snapshots are never modified once they succeed and a content addressing
scheme is used for the individual packages to avoid using up too much
disk space over time.&lt;/p&gt;&lt;p&gt;I plan to keep snapshots around indefinitely and I may add support
for paid features like custom package sets eventually -- if there is
interest -- to help support the hosting costs.&lt;/p&gt;&lt;h2&gt;An Example&lt;/h2&gt;&lt;p&gt;Say you've just started working on a new application. To develop against
the snapshot from May 2nd, 2020 using Racket 7.6, you might run the
following command:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;raco pkg config --set catalogs \
    https://download.racket-lang.org/releases/7.6/catalog/ \
    https://racksnaps.defn.io/snapshots/2020/05/02/catalog/ \
    https://pkgs.racket-lang.org \
    https://planet-compats.racket-lang.org
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will make it so that any packages you install will first look up
the 7.6 release catalog (for packages in the "main distribution", like
&lt;code&gt;rackunit&lt;/code&gt; and &lt;code&gt;typed-racket&lt;/code&gt;), then it'll look up packages in the
snapshot and fall back to the package catalog for any packages not in
the snapshot.&lt;/p&gt;&lt;p&gt;When building the application in CI you might limit the catalog list to
just the release catalog (for packages in the main distribution) and the
snapshot:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;raco pkg config --set catalogs \
    https://download.racket-lang.org/releases/7.6/catalog/ \
    https://racksnaps.defn.io/snapshots/2020/05/02/catalog/
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To speed up builds, you might layer in the built-snapshot for that day:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;raco pkg config --set catalogs \
    https://download.racket-lang.org/releases/7.6/catalog/ \
    https://racksnaps.defn.io/built-snapshots/2020/05/02/catalog/ \
    https://racksnaps.defn.io/snapshots/2020/05/02/catalog/
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Days/weeks/months/years later, when you're ready to deal with upgrading
your dependencies, you can update to a more recent snapshot and repeat
the cycle.&lt;/p&gt;&lt;p&gt;I'm receptive to feedback on how to improve the service so don't
hesitate to reach out if you think of anything!&lt;/p&gt;&lt;section class="footnotes"&gt;&lt;ol&gt;&lt;li id="fn_1"&gt;&lt;p&gt;Technically, it does support pinning a specific sha per package
when using git sources, but that is pretty cumbersome and it means
that packages always have to be installed from source, which increases
install times by 2 to 3x. &lt;a class="footnote-backref" href="#fnref_1_1" title="Jump to reference" aria-label="Jump to reference"&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/section&gt;&lt;/article&gt;</description></item><item><title>Converting byte arrays to UUIDs in Postgres</title><link>https://defn.io/2020/04/05/postgres-bytea-to-uuid</link><guid>https://defn.io/2020/04/05/postgres-bytea-to-uuid</guid><pubDate>Sun, 5 Apr 2020 19:00:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;For a project that I'm working on, I have a custom flake id &lt;a href="https://github.com/Bogdanp/racket-buid/blob/5806054cbea5e69fae66a0b6d622752ace690afd/README.md#spec"&gt;spec&lt;/a&gt; that
allows me to generate unique, sortable identifiers across computers
without any sort of synchronization. The ids themselves can be encoded
down to 16 bytes and I wanted to store them in Postgres. A good way
to do that is to leverage Postgres' &lt;code&gt;UUID&lt;/code&gt; data type, which lets you
efficiently store any 16 byte quantity in a way that can be indexed
reasonably well.&lt;/p&gt;&lt;p&gt;The problem I ran into was that my &lt;a href="https://github.com/racket/db/blob/3ce8e6b073cedc485011130d0d0c54475800c2a2/db-lib/db/util/postgresql.rkt#L110"&gt;DB library of choice&lt;/a&gt; only
supports inserting UUID values that follow the standard UUID format, so
queries like&lt;/p&gt;&lt;pre&gt;&lt;code info="language-sql"&gt;INSERT INTO the_table(uuid_column) VALUES ($1)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;would get rejected at runtime unless &lt;code&gt;$1&lt;/code&gt; actually looked like a UUID.&lt;/p&gt;&lt;p&gt;I considered converting the ids into the standard UUID format within
my application code but that didn't feel like the right thing to do.
Instead, I found that Postgres has a standard function called &lt;a href="https://www.postgresql.org/docs/12/functions-binarystring.html"&gt;&lt;code&gt;encode&lt;/code&gt;&lt;/a&gt;
that is able to take any byte array and encode it into a hex string so
all I had to do was change my query into&lt;/p&gt;&lt;pre&gt;&lt;code info="language-sql"&gt;INSERT INTO the_table(uuid_column) VALUES (CAST(ENCODE($1, 'hex') AS UUID))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;and that worked great!&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Testing a Web API using rackcheck</title><link>https://defn.io/2020/03/13/property-testing-api</link><guid>https://defn.io/2020/03/13/property-testing-api</guid><pubDate>Fri, 13 Mar 2020 11:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;Yesterday, I announced &lt;a href="https://docs.racket-lang.org/rackcheck/index.html"&gt;rackcheck&lt;/a&gt;, my new property-based testing
library for Racket and I wanted to do a quick dive into one of the
examples in the &lt;a href="https://github.com/bogdanp/rackcheck/"&gt;rackcheck repo&lt;/a&gt; where a simple web API is integration
tested using PBT.&lt;/p&gt;&lt;p&gt;You can find the full example &lt;a href="https://github.com/Bogdanp/rackcheck/blob/53d52fefe3a3606ad6e07cf4440d4e7eb99cd7e6/examples/web-api.rkt"&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;The app being tested is a simple leaderboard HTTP API with 3 endpoints:&lt;/p&gt;&lt;ul class="loose"&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;GET /players&lt;/code&gt;: lists all the registered players in order from
highest score to lowest.  Ties are broken by a secondary sort on the
names of the players in ascending order.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;POST /players&lt;/code&gt;: expects a JSON object containing a player name.  If
a player with that name does not exist, then it is created.  If it
does, then a 400 response is returned.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;POST /scores/{name}&lt;/code&gt;: increments the score of the player identified
by &lt;code&gt;{name}&lt;/code&gt;.  Does nothing if a player with that name cannot be
found.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The details of the API implementation don't matter much so I won't
cover it apart from pointing out that the code is intentionally tightly
coupled: all of the business logic is directly tied to the request
handling code.  One criticism I've seen of PBT is that it isn't usable
in contexts where the code you want to test isn't well-factored so I
wanted to show that this isn't true.&lt;/p&gt;&lt;h2&gt;The Less Interesting Bits&lt;/h2&gt;&lt;p&gt;Reading through the code from the &lt;a href="https://github.com/Bogdanp/rackcheck/blob/53d52fefe3a3606ad6e07cf4440d4e7eb99cd7e6/examples/web-api.rkt#L91"&gt;top&lt;/a&gt; of the &lt;code&gt;test&lt;/code&gt; submodule we have...&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (reset)
  (query-exec (current-conn) "DELETE FROM players"))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;reset&lt;/code&gt; ensures the database is in a clean slate.  It gets called
before every test so that the tests themselves don't interfere with
one another.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define (request path [data #f])
  (define-values (status _headers out)
    (http-sendrecv "127.0.0.1" path
                   #:port 9911
                   #:method (if data #"POST" #"GET")
                   #:headers (if data '(#"Content-type: application/json") null)
                   #:data (and data (jsexpr-&amp;gt;bytes data))))

  (match status
    [(regexp #rx#"^HTTP.... 200 ")
     (read-json out)]

    [(regexp #rx#"^HTTP.... 4.. ")
     (error 'client "bad request: ~s" (read-json out))]

    [_
     (error 'server (port-&amp;gt;string out))]))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;request&lt;/code&gt; is used to make requests to the API from within the tests.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(run-tests
 (test-suite
  "web-api"
  #:before
  (lambda ()
    (current-conn (sqlite3-connect #:database 'memory))
    (init-db)

    (define ch (make-async-channel))
    (set! stop (start ch))
    (sync ch))

   ...))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The test suite initializes the database then starts the web server on
a well-known port and waits for it to finish starting up.  The server
itself listens for connections a background thread.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;...

#:after
(lambda ()
  (stop))

...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After the tests are all done, the suite gracefully shuts down the
server.&lt;/p&gt;&lt;h2&gt;The Interesting Bits&lt;/h2&gt;&lt;p&gt;The approach I've taken to test the API is to come up with a simple
model for what the state of the API should be at any point and then
run arbitrary operations against the API, modifying both the API and
the model of its state at the same time.  After every operation, I
check that the state of the model matches that of the API.&lt;/p&gt;&lt;p&gt;To begin with, I define a struct for the model:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(struct model (scores-by-name)
  #:transparent)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The model is just a mapping from player names to their scores at some
point in time.&lt;/p&gt;&lt;p&gt;Next is a generator for player names:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define gen:player-name
  (gen:let ([given (gen:string gen:char-letter)]
            [family (gen:string gen:char-letter)])
    (format "~a ~a" given family)))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When sampled, it produces values like:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;web-api.rkt/test&amp;gt; (sample gen:player-name)
'(" "
  " "
  "vOVu "
  "FSIHd lly"
  "GvbsC JHdLeHmegT"
  "qWs sxsRXIxyZZGOtNVZwtdghwEY"
  "hKxIwwFZZDVoMirDig qpiGrJkbugmyodzXYxYnesIiS"
  "GikMSXKgMozVFWkDhWYduvyjTiSOJaTyNERaKhjPwTrerhoNM goHUhdziwTHzBnJeTrQUGcsLWKQYPGGqLSBntHWBtxw"
  "rylcoMnEtAMmdwsqvZiHqx ZgnOYbxJdeZ"
  "LGzrZIHZjnaZebCAvzPmzhvkbTL zxBzKdIbKumrXptYPEeQuPNqhAOiqczGb")
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next is the generator for operations:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define gen:ops
  (gen:let ([names (gen:no-shrink
                    (gen:resize (gen:filter (gen:list gen:player-name)
                                            (compose1 not null?))
                                10))]
            [ops (gen:list
                  (gen:choice
                   (gen:tuple (gen:const 'create) (gen:one-of names))
                   (gen:tuple (gen:const 'increase) (gen:one-of names))))])
    (cons '(init) ops)))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It pulls from the same set of up to 10 names generated by
&lt;code&gt;gen:player-name&lt;/code&gt; to generate lists of operations that always start
with &lt;code&gt;'(init)&lt;/code&gt; followed by zero or more randomly-selected &lt;code&gt;'(create ...)&lt;/code&gt; or &lt;code&gt;'(increase ...)&lt;/code&gt; operations.  Sampling it three times
produces:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;web-api.rkt/test&amp;gt; (sample gen:ops 3)
'(((init))
  ((init))
  ((init)
   (create "CveqBE K")
   (create "CveqBE K")
   (increase "ESXrkpSC uS")
   (create "sLvsTrsr ZKcVQr")))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next is the interpreter:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define/match (interpret s op)
  ...)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;interpret&lt;/code&gt; takes the current state and the operation it's supposed to
run, checks any pre-conditions, runs the operation, checks any
post-conditions and returns the new state.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;  ...

  [(_ (list 'init))
   (reset)
   (model (hash))]

  ...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When &lt;code&gt;interpret&lt;/code&gt; receives an &lt;code&gt;'(init)&lt;/code&gt; operation, it resets the
database and returns a fresh model.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;  ...

  [((model scores) (list 'create name))
   (define (create-player)
     (with-handlers ([exn:fail? void])
       (request "/players" (hasheq 'name name))))

   (define (player-names)
     (sort (for/list ([player (in-list (request "/players"))])
             (hash-ref player 'name))
           string&amp;lt;?))

   (define (scores-&amp;gt;names s)
     (sort (hash-keys s) string&amp;lt;?))

   (cond
     [(regexp-match-exact? " *" name)
      (begin0 s
        (create-player)
        (check-equal? (player-names) (scores-&amp;gt;names scores)))]

     [(hash-has-key? scores name)
      (begin0 s
        (create-player)
        (check-equal? (player-names) (scores-&amp;gt;names scores)))]

     [else
      (define scores* (hash-set scores name 0))
      (begin0 (model scores*)
        (create-player)
        (check-equal? (player-names) (scores-&amp;gt;names scores*)))])]

  ...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When &lt;code&gt;interpret&lt;/code&gt; receives a &lt;code&gt;'(create "player name")&lt;/code&gt; operation, it
sends a request to create the player to the API and then grabs all the
players in a subsequent request.  Finally, it makes sure they match
the updated model.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;  ...

  [((model scores) (list 'increase name))
   (define scores*
     (if (hash-has-key? scores name)
         (hash-update scores name add1)
         scores))

   (request (format "/scores/~a" name) (hasheq))
   (check-equal?
    (for/list ([player (in-list (request "/players"))])
      (cons (hash-ref player 'name)
            (hash-ref player 'score)))
    (sort (sort (hash-&amp;gt;list scores*) string&amp;lt;? #:key car) &amp;gt; #:key cdr))
   (model scores*)])
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When &lt;code&gt;interpret&lt;/code&gt; receives an &lt;code&gt;'(increase "player name")&lt;/code&gt; operation, it
sends a request to increase the player's score and then grabs the
leaderboard in a subsequent request to ensure it matches the model.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(check-property
 (make-config #:tests 30)
 (property ([ops gen:ops])
   (for/fold ([s #f])
             ([op (in-list ops)])
     (interpret s op))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally, I plug everything together by calling &lt;code&gt;check-property&lt;/code&gt; on a
&lt;code&gt;property&lt;/code&gt; whose inputs are operation lists generated using &lt;code&gt;gen:ops&lt;/code&gt;.
The property just &lt;code&gt;interpret&lt;/code&gt;s every command in sequence and interpret
will raise an exception if the application ends up in a bad state.&lt;/p&gt;&lt;p&gt;If I uncomment the check in the API that ensures no two players can
have the same name and then run the tests I get:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;; FAILURE
; /Users/bogdan/sandbox/rackcheck/examples/web-api.rkt:209:6
location:   web-api.rkt:209:6
name:       unnamed
seed:       1485163264
actual:     '(" UUVDlrhi" " UUVDlrhi")
expected:   '(" UUVDlrhi")

Failed after 4 tests:

  ops = ((init) (create "ZUNEQq k") (create "OPKmoJRUyl IYkkSON") (create "DrfMu pMLxwX") (increase "ZUNEQq k") (increase "OPKmoJRUyl IYkkSON") (create "tHsrGne IRVcaNpt") (create "ZUNEQq k"))

Shrunk:

  ops = ((init) (create " UUVDlrhi") (create " UUVDlrhi"))

--------------------
0 success(es) 1 failure(s) 0 error(s) 1 test(s) run
1
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Which is pretty great if you ask me!&lt;/p&gt;&lt;h2&gt;In Closing&lt;/h2&gt;&lt;p&gt;You might think this was a lot of work compared to just writing
example tests, but at the end of all this I have a straightforward
specification for my API by way of the &lt;code&gt;interpret&lt;/code&gt; function and the
tests that get thrown at the API are far more diverse than anything
I'd ever have taken the time to write by hand.&lt;/p&gt;&lt;p&gt;Extending the interpreter or adding new interpreters as the API grows
is also very easy once you get the hang of this pattern.&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Announcing rackcheck</title><link>https://defn.io/2020/03/12/ann-rackcheck</link><guid>https://defn.io/2020/03/12/ann-rackcheck</guid><pubDate>Thu, 12 Mar 2020 14:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;I've been playing around with property-based testing in Racket this past
week. I started by forking the existing &lt;a href="https://github.com/ifigueroap/racket-quickcheck"&gt;quickcheck&lt;/a&gt; library to try and
add support for shrinking, but I quickly realized that I'd have to make
a number of breaking changes to get it to work the way I wanted so, in
the end, I decided to start a new library from scratch.&lt;/p&gt;&lt;p&gt;The library is called &lt;a href="https://github.com/Bogdanp/rackcheck"&gt;rackcheck&lt;/a&gt; and you can grab it off of the package
server. The reference docs should show up on there soon and there are a
few examples in the repo. I'm pretty happy with the result so far, but
I may end up making some small API adjustments as I use it more so keep
that in mind!&lt;/p&gt;&lt;/article&gt;</description></item><item><title>The Missing Guide to Racket's Web Server</title><link>https://defn.io/2020/02/12/racket-web-server-guide</link><guid>https://defn.io/2020/02/12/racket-web-server-guide</guid><pubDate>Wed, 12 Feb 2020 12:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;Racket's built-in &lt;a href="https://docs.racket-lang.org/web-server/index.html?q=web-server"&gt;web-server&lt;/a&gt; package is great, but parts of it are
low-level enough that it can be confusing to people who are new to the
language.  In this post, I'm going to try to clear up some of that
confusion by providing some definitions and examples for things
beginners might wonder about.&lt;/p&gt;&lt;h2&gt;Servlets&lt;/h2&gt;&lt;p&gt;A servlet is a function from a &lt;a href="https://docs.racket-lang.org/web-server/http.html?q=request#%28def._%28%28lib._web-server%2Fhttp%2Frequest-structs..rkt%29._request%29%29"&gt;&lt;code&gt;request&lt;/code&gt;&lt;/a&gt; to a &lt;a href="https://docs.racket-lang.org/web-server/http.html?q=request#%28def._%28%28lib._web-server%2Fhttp%2Fresponse-structs..rkt%29._response%29%29"&gt;&lt;code&gt;response&lt;/code&gt;&lt;/a&gt;.  It has
the contract:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(-&amp;gt; request? can-be-response?)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here's a servlet that replies with "Hello, world!" regardless of what
the request looks like:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;#lang racket/base

(require web-server/http)

(define (hello req)
  (response/output
   (lambda (out)
     (displayln "Hello, world!" out))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And here's one that dynamically constructs a response based on the
request's query parameters:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;#lang racket/base

(require racket/match
         web-server/http)

(define (age req)
  (define binds (request-bindings/raw req))
  (define message
    (match (list (bindings-assq #"name" binds)
                 (bindings-assq #"age" binds))
      [(list #f #f)
       "Anonymous is unknown years old."]

      [(list #f (binding:form _ age))
       (format "Anonymous is ~a years old." age)]

      [(list (binding:form _ name) #f)
       (format "~a is unknown years old." name)]

      [(list (binding:form _ name)
             (binding:form _ age))
       (format "~a is ~a years old." name age)]))
  (response/output
   (lambda (out)
     (displayln message out))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href="https://docs.racket-lang.org/web-server/run.html?q=serve%2Fservlet#%28def._%28%28lib._web-server%2Fservlet-env..rkt%29._serve%2Fservlet%29%29"&gt;&lt;code&gt;serve/servlet&lt;/code&gt;&lt;/a&gt; is a convenience function that configures a server
to run whatever servlet you give it.&lt;/p&gt;&lt;p&gt;Here's how you'd run the &lt;code&gt;age&lt;/code&gt; servlet using &lt;code&gt;serve/servlet&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;#lang racket/base

(define age ...)

(serve/servlet
 age
 #:listen-ip "127.0.0.1"
 #:port 8000
 #:command-line? #t
 #:servlet-path ""
 #:servlet-regexp #rx"")
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;While very convenient for quick things, it obscures a lot of what's
going on under the hood from the caller.  An invocation of the
lower-level &lt;a href="https://docs.racket-lang.org/web-server-internal/web-server.html?q=serve#%28def._%28%28lib._web-server%2Fweb-server..rkt%29._serve%29%29"&gt;&lt;code&gt;serve&lt;/code&gt;&lt;/a&gt; function that achieves the same result would
look like:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;#lang racket/base

(require racket/match
         web-server/http
         web-server/servlet-dispatch
         web-server/web-server)

(define age ...)

(define stop
  (serve
   #:dispatch (dispatch/servlet age)
   #:listen-ip "127.0.0.1"
   #:port 8000))

(with-handlers ([exn:break? (lambda (e)
                              (stop))])
  (sync/enable-break never-evt))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This sets up a web server with a single dispatcher that runs a single
servlet, running in a background thread.  The return value of the
&lt;code&gt;serve&lt;/code&gt; function is a function that can be used to stop the server
and, since the server runs in a background thread, I need to do
something on the main thread to prevent it from terminating.  I've
chosen to wait on an event that never terminates and to capture breaks
(such as the &lt;code&gt;SIGINT&lt;/code&gt; and &lt;code&gt;SIGTERM&lt;/code&gt; signals (the former is sent when
you press Ctrl+C on a running process)).  When such a break is
received, the &lt;code&gt;stop&lt;/code&gt; function gets called and the server terminates
gracefully.&lt;/p&gt;&lt;h2&gt;Dispatchers&lt;/h2&gt;&lt;p&gt;You may have noticed that, unlike with &lt;code&gt;serve/servlet&lt;/code&gt;, I couldn't
just pass my &lt;code&gt;age&lt;/code&gt; servlet directly to &lt;code&gt;serve&lt;/code&gt;.  I had to turn it into
a dispatcher by calling &lt;code&gt;dispatch/servlet&lt;/code&gt;.  This is because a
dispatcher, not a servlet, sits at the root of every server.&lt;/p&gt;&lt;p&gt;A dispatcher is a function that takes a &lt;a href="https://docs.racket-lang.org/web-server-internal/connection-manager.html?q=connection%3F#%28def._%28%28lib._web-server%2Fprivate%2Fconnection-manager..rkt%29._connection~3f%29%29"&gt;&lt;code&gt;connection&lt;/code&gt;&lt;/a&gt; object and a
&lt;a href="https://docs.racket-lang.org/web-server/http.html?q=request#%28def._%28%28lib._web-server%2Fhttp%2Frequest-structs..rkt%29._request%29%29"&gt;&lt;code&gt;request&lt;/code&gt;&lt;/a&gt; and either services that request or calls
&lt;a href="https://docs.racket-lang.org/web-server-internal/dispatch.html?q=next-dispatcher#%28def._%28%28lib._web-server%2Fdispatchers%2Fdispatch..rkt%29._next-dispatcher%29%29"&gt;&lt;code&gt;next-dispatcher&lt;/code&gt;&lt;/a&gt;.  Its contract is:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(-&amp;gt; connection? request? any)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Dispatchers' return values &lt;i&gt;are ignored&lt;/i&gt;.  They operate directly on
the connection objects that they are given.  If I wanted to make my
own dispatcher to run the &lt;code&gt;age&lt;/code&gt; servlet instead of using
&lt;code&gt;dispatch/servlet&lt;/code&gt;, it'd look something like this:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;#lang racket/base

(require web-server/http/response)

(define (age-dispatcher conn req)
  (output-response conn (age req)))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;output-response&lt;/code&gt; takes a &lt;a href="https://docs.racket-lang.org/web-server-internal/connection-manager.html?q=connection%3F#%28def._%28%28lib._web-server%2Fprivate%2Fconnection-manager..rkt%29._connection~3f%29%29"&gt;&lt;code&gt;connection&lt;/code&gt;&lt;/a&gt; and a &lt;a href="https://docs.racket-lang.org/web-server/http.html?q=request#%28def._%28%28lib._web-server%2Fhttp%2Fresponse-structs..rkt%29._response%29%29"&gt;&lt;code&gt;response&lt;/code&gt;&lt;/a&gt; and
serializes the response over the connection to the client end.&lt;/p&gt;&lt;p&gt;This is equivalent&lt;sup&gt;&lt;a href="#fn_1" id="fnref_1_1"&gt;1&lt;/a&gt;&lt;/sup&gt; to:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;(define age-dispatcher (dispatch/servlet age))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There are a number of built-in dispatchers that you'd normally make
use of in a real world project.  The most important of which are:&lt;/p&gt;&lt;ul class="tight"&gt;&lt;li&gt;&lt;a href="https://docs.racket-lang.org/web-server-internal/dispatch-sequencer.html?q=dispatchers%2Ffile"&gt;&lt;code&gt;web-server/dispatchers/dispatch-sequencer&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://docs.racket-lang.org/web-server-internal/dispatch-filter.html?q=dispatchers%2Ffile"&gt;&lt;code&gt;web-server/dispatchers/dispatch-filter&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="https://docs.racket-lang.org/web-server-internal/dispatch-files.html?q=dispatchers%2Ffile"&gt;&lt;code&gt;web-server/dispatchers/dispatch-files&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;&lt;code&gt;dispatch-sequencer&lt;/code&gt;&lt;/h3&gt;&lt;p&gt;This dispatcher takes a list of dispatchers and runs through them in
order on every request, until it reaches the first one that doesn't
call &lt;code&gt;next-dispatcher&lt;/code&gt;.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;#lang racket/base

(require net/url
         racket/string
         web-server/dispatchers/dispatch
         (prefix-in sequencer: web-server/dispatchers/dispatch-sequencer)
         web-server/http
         web-server/http/response
         web-server/web-server)

(define (request-path-has-prefix? req p)
  (string-prefix? (path-&amp;gt;string (url-&amp;gt;path (request-uri req))) p))

(define (a-dispatcher conn req)
  (if (request-path-has-prefix? req "/a/")
      (output-response conn (response/output
                             (lambda (out)
                               (displayln "hello from a" out))))
      (next-dispatcher)))

(define (b-dispatcher conn req)
  (output-response conn
                   (response/output
                    (lambda (out)
                      (displayln "hello from b" out)))))

(define stop
  (serve
   #:dispatch (sequencer:make a-dispatcher
                              b-dispatcher)
   #:listen-ip "127.0.0.1"
   #:port 8000))

(with-handlers ([exn:break? (lambda (e)
                              (stop))])
  (sync/enable-break never-evt))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The above server runs the &lt;code&gt;a-dispatcher&lt;/code&gt; on every request.  If the
request path doesn't start with &lt;code&gt;"/a/"&lt;/code&gt;, then it moves on to the
&lt;code&gt;b-dispatcher&lt;/code&gt;.&lt;/p&gt;&lt;h3&gt;&lt;code&gt;dispatch-filter&lt;/code&gt;&lt;/h3&gt;&lt;p&gt;Filtering the request path like I did in the previous snippet is
pretty cumbersome so the web-server provides the filtering dispatcher
for this exact purpose.  The above code could be rewritten as:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;#lang racket/base

(require (prefix-in filter: web-server/dispatchers/dispatch-filter)
         (prefix-in sequencer: web-server/dispatchers/dispatch-sequencer)
         web-server/http
         web-server/http/response
         web-server/web-server)

(define (a-dispatcher conn req)
  (output-response conn
                   (response/output
                    (lambda (out)
                      (displayln "hello from a" out)))))

(define (b-dispatcher conn req)
  (output-response conn
                   (response/output
                    (lambda (out)
                      (displayln "hello from b" out)))))

(define stop
  (serve
   #:dispatch (sequencer:make (filter:make #rx"^/a/" a-dispatcher)
                              b-dispatcher)
   #:listen-ip "127.0.0.1"
   #:port 8000))

(with-handlers ([exn:break? (lambda (e)
                              (stop))])
  (sync/enable-break never-evt))
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;&lt;code&gt;dispatch-files&lt;/code&gt;&lt;/h3&gt;&lt;p&gt;This dispatcher can be used to serve files off of the filesystem.  You
can combine it with the other dispatchers to generate a server that
can either serve files off of the filesystem or fall back to a
servlet:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;#lang racket/base

(require net/url
         (prefix-in files: web-server/dispatchers/dispatch-files)
         (prefix-in filter: web-server/dispatchers/dispatch-filter)
         (prefix-in sequencer: web-server/dispatchers/dispatch-sequencer)
         web-server/dispatchers/filesystem-map
         web-server/http
         web-server/servlet-dispatch
         web-server/web-server)

(define (homepage req)
  (response/xexpr
   '(html
     (head
      (link ([href "/static/screen.css"] [rel "stylesheet"])))
     (body
      (h1 "Hello!")))))

(define url-&amp;gt;path/static
  (make-url-&amp;gt;path "static"))

(define static-dispatcher
  (files:make #:url-&amp;gt;path (lambda (u)
                            (url-&amp;gt;path/static
                             (struct-copy url u [path (cdr (url-path u))])))))

(define stop
  (serve
   #:dispatch (sequencer:make
               (filter:make #rx"^/static/" static-dispatcher)
               (dispatch/servlet homepage))
   #:listen-ip "127.0.0.1"
   #:port 8000))

(with-handlers ([exn:break? (lambda (e)
                              (stop))])
  (sync/enable-break never-evt))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This dispatcher needs to know how to map the current request URL to a
path on the filesystem.&lt;/p&gt;&lt;p&gt;First, I create a function that maps URLs to file paths within the
&lt;code&gt;static&lt;/code&gt; directory (a relative path from where the server happens to
be run (the current working directory)).  This function automatically
removes things like &lt;code&gt;..&lt;/code&gt; from the paths it is given, ensuring that no
request paths can "escape" out of the static directory.&lt;/p&gt;&lt;p&gt;Then, I pass &lt;code&gt;files:make&lt;/code&gt; a function that maps URLs to file paths.
Since I'm going to serve all static files from URLs that start with
&lt;code&gt;/static/&lt;/code&gt;, I need to drop that prefix from the URL before I pass it
to the &lt;code&gt;url-&amp;gt;path/static&lt;/code&gt; function because it expects a file path
relative to the &lt;code&gt;static&lt;/code&gt; directory.&lt;/p&gt;&lt;p&gt;Finally, I sequence the static dispatcher along with a servlet
dispatcher that serves the home page and the end result is a web
server that can serve static files from a directory and run dynamic
Racket code!&lt;/p&gt;&lt;h2&gt;Routing&lt;/h2&gt;&lt;p&gt;You could route requests by sequencing together multiple
&lt;code&gt;dispatch-filter&lt;/code&gt; dispatchers, but that wouldn't be very ergonomic.
The web server provides the &lt;a href="https://docs.racket-lang.org/web-server/dispatch.html?q=dispatch-rules#%28form._%28%28lib._web-server%2Fdispatch..rkt%29._dispatch-rules%29%29"&gt;&lt;code&gt;dispatch-rules&lt;/code&gt;&lt;/a&gt; macro as a convenient
way to declare &lt;i&gt;servlets&lt;/i&gt; -- not dispatchers! the overloading of terms
here can be a bit confusing -- that perform different actions based on
the request method and path.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;#lang racket/base

(require net/url
         web-server/dispatch
         (prefix-in files: web-server/dispatchers/dispatch-files)
         (prefix-in filter: web-server/dispatchers/dispatch-filter)
         (prefix-in sequencer: web-server/dispatchers/dispatch-sequencer)
         web-server/dispatchers/filesystem-map
         web-server/http
         web-server/servlet-dispatch
         web-server/web-server)

(define (response/template . content)
  (response/xexpr
   `(html
     (head
      (link ([href "/static/screen.css"] [rel "stylesheet"])))
     (body
      ,@content))))

(define (homepage req)
  (response/template '(h1 "Home")))

(define (blog req)
  (response/template '(h1 "Blog")))

(define-values (app reverse-uri)
  (dispatch-rules
   [("") homepage]
   [("blog") blog]))

(define url-&amp;gt;path/static (make-url-&amp;gt;path "static"))

(define static-dispatcher
  (files:make #:url-&amp;gt;path (lambda (u)
                            (url-&amp;gt;path/static
                             (struct-copy url u [path (cdr (url-path u))])))))

(define stop
  (serve
   #:dispatch (sequencer:make
               (filter:make #rx"^/static/" static-dispatcher)
               (dispatch/servlet app))
   #:listen-ip "127.0.0.1"
   #:port 8000))

(with-handlers ([exn:break? (lambda (e)
                              (stop))])
  (sync/enable-break never-evt))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Using &lt;code&gt;dispatch-rules&lt;/code&gt; as I've done above produces two values: a
servlet that maps requests made to &lt;code&gt;/&lt;/code&gt; to the &lt;code&gt;homepage&lt;/code&gt; servlet and
requests made to &lt;code&gt;/blog&lt;/code&gt; to the &lt;code&gt;blog&lt;/code&gt; servlet, and a function that
can produce reverse URIs when given either of those functions.&lt;/p&gt;&lt;p&gt;Plugging that in via &lt;code&gt;dispatch/servlet&lt;/code&gt; into the main servlet sequence
gets you a server that can serve files off of disk and also
dynamically dispatch requests to multiple servlets.&lt;/p&gt;&lt;p&gt;One final tweak we might want to make here is to plug another servlet
after the app servlet into the sequencer to handle requests to paths
that don't exist:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;#lang racket/base

(require net/url
         web-server/dispatch
         (prefix-in files: web-server/dispatchers/dispatch-files)
         (prefix-in filter: web-server/dispatchers/dispatch-filter)
         (prefix-in sequencer: web-server/dispatchers/dispatch-sequencer)
         web-server/dispatchers/filesystem-map
         web-server/http
         web-server/servlet-dispatch
         web-server/web-server)

(define (response/template . content)
  (response/xexpr
   `(html
     (head
      (link ([href "/static/screen.css"] [rel "stylesheet"])))
     (body
      ,@content))))

(define (homepage req)
  (response/template '(h1 "Home")))

(define (blog req)
  (response/template '(h1 "Blog")))

(define (not-found req)
  (response/template '(h1 "Not Found")))

(define-values (app reverse-uri)
  (dispatch-rules
   [("") homepage]
   [("blog") blog]))

(define url-&amp;gt;path/static (make-url-&amp;gt;path "static"))

(define static-dispatcher
  (files:make #:url-&amp;gt;path (lambda (u)
                            (url-&amp;gt;path/static
                             (struct-copy url u [path (cdr (url-path u))])))))

(define stop
  (serve
   #:dispatch (sequencer:make
               (filter:make #rx"^/static/" static-dispatcher)
               (dispatch/servlet app)
               (dispatch/servlet not-found))
   #:listen-ip "127.0.0.1"
   #:port 8000))

(with-handlers ([exn:break? (lambda (e)
                              (stop))])
  (sync/enable-break never-evt))
&lt;/code&gt;&lt;/pre&gt;&lt;section class="footnotes"&gt;&lt;ol&gt;&lt;li id="fn_1"&gt;&lt;p&gt;I am simplifying things here for the purposes of this guide.
The &lt;code&gt;dispatch/servlet&lt;/code&gt; function does some additional work to
support continuations.  See &lt;a href="/2020/05/11/racket-web-server-internals/"&gt;Continuations in Racket's Web
Server&lt;/a&gt; for details. &lt;a class="footnote-backref" href="#fnref_1_1" title="Jump to reference" aria-label="Jump to reference"&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/section&gt;&lt;/article&gt;</description></item><item><title>Announcing Try Racket</title><link>https://defn.io/2020/01/31/ann-try-racket</link><guid>https://defn.io/2020/01/31/ann-try-racket</guid><pubDate>Fri, 31 Jan 2020 12:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;I'd been meaning to play with Racket's built-in &lt;a href="https://docs.racket-lang.org/reference/Sandboxed_Evaluation.html?q=racket%2Fsandbox"&gt;sandboxing&lt;/a&gt;
capabilities for a while so yesterday I sat down and made &lt;a href="https://try-racket.defn.io"&gt;Try Racket&lt;/a&gt;.
It's a web app that lets you type in Racket code and run it. The code
you run is tied to your session and each session is allocated up to 60
seconds of run time per evaluation, with up to 128MB of memory used.
Filesystem and network access is not permitted and neither is access to
the FFI. The application itself runs inside a further-restricted Docker
container.&lt;/p&gt;&lt;p&gt;You can find the source code on &lt;a href="https://github.com/bogdanp/try-racket"&gt;GitHub&lt;/a&gt;. Contributions and improvements
are totally welcome!&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Running Racket BC on iOS</title><link>https://defn.io/2020/01/05/racket-on-ios</link><guid>https://defn.io/2020/01/05/racket-on-ios</guid><pubDate>Sun, 5 Jan 2020 20:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;&lt;b&gt;As of 2021-01-18, it is possible to &lt;a href="/2021/01/19/racket-cs-on-ios"&gt;run Racket CS on iOS&lt;/a&gt;.&lt;/b&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;/u/myfreeweb&lt;/code&gt; pointed out to me in a &lt;a href="https://lobste.rs/s/s4okil/native_applications_with_racket"&gt;lobste.rs&lt;/a&gt; thread yesterday
that Racket compiles just fine on &lt;code&gt;aarch64&lt;/code&gt; and that led me down a
rabbit hole trying to get Racket running inside an iOS application.  I
finally succeeded so I figured I'd write down my findings in hopes of
helping future Racketeers (myself included) going down this path!&lt;/p&gt;&lt;h2&gt;Compile Racket for macOS&lt;/h2&gt;&lt;p&gt;A recent-enough version of Racket is required in order to compile
Racket for iOS.  The best way to do that is to clone the &lt;a href="https://github.com/racket/racket"&gt;Racket repo&lt;/a&gt;
and follow the &lt;a href="https://github.com/racket/racket/blob/fc258725ba7e5bd7289f15a08843fb2f62af4e27/build.md"&gt;build instructions&lt;/a&gt; which should be as simple as
running &lt;code&gt;make&lt;/code&gt; in the repository root.&lt;/p&gt;&lt;p&gt;Assuming you're following along in a terminal session, run&lt;/p&gt;&lt;pre&gt;&lt;code info="language-bash"&gt;export RACKET_SRC=$(pwd)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You'll need to reference this directory in the following steps.&lt;/p&gt;&lt;h2&gt;Compile Racket for iOS&lt;/h2&gt;&lt;p&gt;Once you've successfully compiled Racket for macOS, clone the Racket
repository again, this time under a different directory.  I called
this directory &lt;code&gt;racket-ios&lt;/code&gt; to differentiate the two, but you can call
it whatever.  Make sure the same commit is checked out in both repos
and run through the following build steps starting at the repository
root:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-bash"&gt;mkdir racket/src/build \
  &amp;amp;&amp;amp; cd racket/src/build \
  &amp;amp;&amp;amp; ../configure \
        --host=aarch64-apple-darwin \
        --enable-ios=iPhoneOS \
        --enable-racket="$RACKET_SRC/racket/bin/racket"
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This will configure the build to create objects that can run on a
physical device.  To build Racket for the simulator instead, change
the &lt;code&gt;host&lt;/code&gt; to &lt;code&gt;x86_64-apple-darwin&lt;/code&gt; and &lt;code&gt;enable-ios&lt;/code&gt; from &lt;code&gt;iPhoneOS&lt;/code&gt;
to &lt;code&gt;iPhoneSimulator&lt;/code&gt;.  For details on these flags, see the &lt;a href="https://github.com/racket/racket/blob/fc258725ba7e5bd7289f15a08843fb2f62af4e27/racket/src/README.txt#L336"&gt;cross
compiling instructions&lt;/a&gt; in the Racket repo.&lt;/p&gt;&lt;p&gt;~~Although the instructions currently don't mention that &lt;code&gt;pthread&lt;/code&gt;
support is required when configuring the build, the code will fail to
compile without it.~~  Matthew Flatt pushed &lt;a href="https://github.com/racket/racket/commit/f0a63b59214d7885dc2d4872637e269eb38d5e49"&gt;a fix&lt;/a&gt; for this today!&lt;/p&gt;&lt;p&gt;Next, run &lt;code&gt;make cgc &amp;amp;&amp;amp; make install-cgc&lt;/code&gt; to compile the code and the
packages.  This builds the conservative GC variant of Racket.  I
started out trying to get everything running using the 3m variant of
Racket (with a precise GC), but I ran into a number of roadblocks,
including &lt;a href="https://bugs.llvm.org/show_bug.cgi?id=22868"&gt;an LLVM bug&lt;/a&gt; from 2015 so I eventually gave up and
switched to the CGC variant.&lt;/p&gt;&lt;h2&gt;Create the Xcode Project&lt;/h2&gt;&lt;p&gt;Create a new iOS-based project in Xcode.  Inside that project, add a
new group called "Frameworks" and then drag and drop &lt;code&gt;racket/libmzgc.a&lt;/code&gt;,
&lt;code&gt;racket/libracket.a&lt;/code&gt; and &lt;code&gt;rktio/librktio.a&lt;/code&gt; from the &lt;code&gt;racket/src/build&lt;/code&gt;
directory into the "Frameworks" group.  Make sure "Copy items if needed"
is toggled.&lt;/p&gt;&lt;p&gt;&lt;img src="/img/racket-on-ios-copy.png" alt="Drag n' Drop" title=""/&gt;&lt;/p&gt;&lt;p&gt;Open the project settings and, from the "Build Phases" -&amp;gt; "Link Binary
with Libraries" section, add &lt;code&gt;libiconv.tbd&lt;/code&gt;.  Racket depends on this
library.&lt;/p&gt;&lt;p&gt;&lt;img src="/img/racket-on-ios-link-iconv.png" alt="Link iconv" title=""/&gt;&lt;/p&gt;&lt;p&gt;Copy &lt;code&gt;racket/include&lt;/code&gt; into your project and then from the "Build
Settings" -&amp;gt; "Search Paths" -&amp;gt; "Header Search Paths" section add
&lt;code&gt;$(SRCROOT)/include&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;&lt;img src="/img/racket-on-ios-headers.png" alt="Headers" title=""/&gt;&lt;/p&gt;&lt;p&gt;Add a new header file called &lt;code&gt;BridgingHeader.h&lt;/code&gt; and then from the
"Build Settings" -&amp;gt; "Swift Compiler - General" -&amp;gt; "Objective-C
Bridging Header" add the path to the file.  Everything defined in this
file will be made available to the Swift code.  Inside the file add:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-c"&gt;#include "Interop.h"
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Create a new C file called &lt;code&gt;Interop.c&lt;/code&gt; and add&lt;/p&gt;&lt;pre&gt;&lt;code info="language-c"&gt;static int run(Scheme_Env *e, int argc, char *argv[]) {
    return 0;
}

void run_racket() {
    scheme_main_setup(1, run, 0, NULL);
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In its associated header file, &lt;code&gt;Interop.h&lt;/code&gt;, add&lt;/p&gt;&lt;pre&gt;&lt;code info="language-c"&gt;#include "scheme.h"

void run_racket(void);
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally, inside the &lt;code&gt;AppDelegate&lt;/code&gt;'s &lt;code&gt;application:didFinishLaunchingWithOptions&lt;/code&gt;
method, add a call to &lt;code&gt;run_racket&lt;/code&gt; and run your project on your device
to ensure everything compiles and runs properly.&lt;/p&gt;&lt;p&gt;If everything runs correctly, then pat yourself on the back, you've
just run Racket on iOS!  Of course, Racket's not really doing much
at this point.  Let's embed a Racket module into the project.&lt;/p&gt;&lt;p&gt;In the project source directory, create a new file called &lt;code&gt;hello.rkt&lt;/code&gt;
with the following contents:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-racket"&gt;#lang racket/base

(displayln "Hello, World!")
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;From the command line, compile &lt;code&gt;hello.rkt&lt;/code&gt; and all its transitive
dependencies into a C file:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-bash"&gt;"$RACKET_SRC/racket/bin/raco" ctool --c-mods hello.c hello.rkt
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next, update &lt;code&gt;Interop.c&lt;/code&gt; to include the resulting C file:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-c"&gt;#include "hello.c"
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And then update &lt;code&gt;run&lt;/code&gt; in that same file to initialize and run that
module:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-c"&gt;static int run(Scheme_Env *e, int argc, char *argv[]) {
    Scheme_Object *a[2];
    declare_modules(e);
    a[0] = scheme_make_pair(scheme_intern_symbol("quote"),
                            scheme_make_pair(scheme_intern_symbol("hello"),
                                             scheme_make_null()));
    a[1] = scheme_false;
    scheme_dynamic_require(2, a);
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note that the name of the module passed to &lt;code&gt;scheme_intern_symbol&lt;/code&gt; is
&lt;code&gt;"hello"&lt;/code&gt;, the same as the file name.&lt;/p&gt;&lt;p&gt;Run your project, and you should see "Hello, World!" in your console.&lt;/p&gt;&lt;p&gt;It took a little bit of work, but we got there!  From here, you should
be able to do more interesting stuff.  I'll get into some of that when
I start porting &lt;a href="https://gum.co/rememberapp"&gt;Remember&lt;/a&gt; to iOS.  In the mean time, you can read up
on this stuff &lt;a href="https://docs.racket-lang.org/inside/index.html?q=embed"&gt;over here&lt;/a&gt;!&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Native Applications with Racket</title><link>https://defn.io/2020/01/04/remember-internals</link><guid>https://defn.io/2020/01/04/remember-internals</guid><pubDate>Sat, 4 Jan 2020 13:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;A couple of days ago, I released a native macOS application called
&lt;a href="https://gumroad.com/l/rememberapp"&gt;Remember&lt;/a&gt;.  It is a small, keyboard-driven application for
stashing away notes/reminders for later.  One of the cool things about
it from a programming nerd perspective is that, while it is a
completely native Cocoa application whose frontend is built with
Swift, the core business logic is all in Racket!&lt;/p&gt;&lt;h2&gt;Why not use racket/gui?&lt;/h2&gt;&lt;p&gt;I started out with a &lt;a href="https://gist.github.com/Bogdanp/3fa6dec42a9bd7fa4422e0e0cd1cd23b"&gt;proof of concept&lt;/a&gt; that used Racket for the
GUI, but I realized that I'd have to write a bunch of Objective-C FFI
code to get the UI to look the way I wanted (a carbon copy of
Spotlight) and it seemed like it would be a pain to try and integrate
&lt;a href="https://github.com/davedelong/DDHotKey"&gt;DDHotKey&lt;/a&gt; and to add support for launching at login into a package
that is easy to distribute.  I was also unsure about how I could
notarize the distribution for macOS Catalina (more on this later).&lt;/p&gt;&lt;h2&gt;Why not do it all in Swift?&lt;/h2&gt;&lt;p&gt;I don't know Swift particularly well, nor do I like it very much.  I
find Apple's documentation lackluster and Xcode is surprisingly buggy
(renaming a class and its associated file fails to rename the file on
disk, which causes Xcode to fail silently, for example).  I wouldn't
mind the documentation being bad if the core cocoa code was open
source/source available; at least then I could look at the
implementation to try and understand what's going on.&lt;/p&gt;&lt;p&gt;More importantly, I plan to support Windows and Linux which means that
writing the core in a portable language is going to minimize the
amount of work I have to do as well as the differences between the
implementations on each platform.&lt;/p&gt;&lt;h2&gt;How it Works&lt;/h2&gt;&lt;p&gt;The &lt;a href="https://github.com/Bogdanp/remember/tree/bfe3c0c56b59602852155247c37ef4243866c6ba/core"&gt;Racket core&lt;/a&gt; runs a custom JSONRPC &lt;a href="https://github.com/Bogdanp/remember/blob/bfe3c0c56b59602852155247c37ef4243866c6ba/core/server.rkt#L15"&gt;server&lt;/a&gt; that listens
for commands on &lt;code&gt;stdin&lt;/code&gt; and sends responses on &lt;code&gt;stdout&lt;/code&gt;.  Using
Racket's &lt;a href="https://docs.racket-lang.org/raco/exe.html?q=raco%20exe"&gt;&lt;code&gt;raco exe&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://docs.racket-lang.org/raco/exe-dist.html?q=raco%20distribute"&gt;&lt;code&gt;raco distribute&lt;/code&gt;&lt;/a&gt; commands, that core gets
built into a native executable and copied into the Swift app's
resources folder.  The Swift application &lt;a href="https://github.com/Bogdanp/remember/blob/bfe3c0c56b59602852155247c37ef4243866c6ba/cocoa/remember/remember/ComsCenter.swift#L39"&gt;runs the core&lt;/a&gt; as a
subprocess on startup and communicates with it via pipes.&lt;/p&gt;&lt;p&gt;RPC commands are asynchronously handled by the core and the core may
also send asynchronous notifications to the frontend to let it know
when entries are due.&lt;/p&gt;&lt;h3&gt;Notarization&lt;/h3&gt;&lt;p&gt;It took me a couple of hours to figure out how to get everything
notarized.  I had to enable App Sandboxing for both the frontend and
the core application, figure out via trial and error which
entitlements were necessary, realize that I needed a separate set of
entitlements for the core application and that the
&lt;code&gt;com.apple.security.inherit&lt;/code&gt; entitlement, for whatever reason, doesn't
let the subprocess inherit its parents entitlements, meaning that I
had to also explicitly assign the core application the "Allow JIT" and
"Allow Unsigned Executable Memory" entitlements, else the process
would get killed with a &lt;code&gt;SIGINT&lt;/code&gt; and a red herring error message about
how the executable doesn't have a valid bundle identifier.&lt;/p&gt;&lt;h3&gt;Would I do this again?&lt;/h3&gt;&lt;p&gt;I'll have to see how porting to other platforms goes, but so far I'm
very happy with this approach.  The result is fast and now that I've
built the RPC infrastructure I can easily copy all that code into
other projects.  Writing the business logic in Racket means that I can
iterate very quickly and writing the GUI code using the native tools
for each platform is advantageous in terms of look and feel and
distribution.&lt;/p&gt;&lt;h3&gt;What about iOS?&lt;/h3&gt;&lt;p&gt;Unfortunately, the RPC approach breaks down on iOS where you're not
allowed to run subprocesses.  An approach that could work there is
building the app into a shared library, linking against it and doing
the RPC in-process.  I think that approach could work, but Racket
would have to be able to target &lt;code&gt;arm64&lt;/code&gt; for that to be feasible.
Fortunately, now that Racket is able to run on top of Chez Scheme,
which already has backends for many platforms, including &lt;code&gt;arm32le&lt;/code&gt;,
that might be a possibility in the future. &lt;sup&gt;&lt;a href="#fn_1" id="fnref_1_1"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;&lt;section class="footnotes"&gt;&lt;ol&gt;&lt;li id="fn_1"&gt;&lt;p&gt;&lt;code&gt;myfreeweb&lt;/code&gt; pointed out on &lt;a href="https://lobste.rs/s/s4okil/native_applications_with_racket#c_etvpxp"&gt;lobste.rs&lt;/a&gt; that this is already
supported by Racket BC! &lt;a class="footnote-backref" href="#fnref_1_1" title="Jump to reference" aria-label="Jump to reference"&gt;↩&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/section&gt;&lt;/article&gt;</description></item><item><title>Announcing Remember for macOS</title><link>https://defn.io/2020/01/02/ann-remember</link><guid>https://defn.io/2020/01/02/ann-remember</guid><pubDate>Thu, 2 Jan 2020 18:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;I've been using org-mode capture templates for years and I've always
wished I had something like that for the whole system. I took advantage
of the holiday break to build &lt;a href="https://gumroad.com/l/rememberapp"&gt;Remember&lt;/a&gt;, a little reminders application
with Spotlight-like UX. It's available on Gumroad and you can pay what
you want for it (inlcuding $0!) at the moment so I hope you'll give it a
go!&lt;/p&gt;&lt;p&gt;Although the app isn't Open Source, it source code is available &lt;a href="https://github.com/bogdanp/remember"&gt;on
GitHub&lt;/a&gt;. Feel free to build, modify and run it yourself, but
please don't redistribute it.&lt;/p&gt;&lt;p&gt;P.S. the app is currently &lt;a href="https://www.producthunt.com/posts/remember-6"&gt;on Product Hunt&lt;/a&gt; so if you have an
account there, feel free to leave a comment!&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Announcing setup-racket</title><link>https://defn.io/2019/11/03/ann-setup-racket</link><guid>https://defn.io/2019/11/03/ann-setup-racket</guid><pubDate>Sun, 3 Nov 2019 10:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;&lt;a href="https://github.com/features/actions"&gt;GitHub Actions&lt;/a&gt; are going to become generally-available next week so
I created an action for installing Racket. You can find it &lt;a href="https://github.com/marketplace/actions/setup-racket-environment"&gt;on the
marketplace&lt;/a&gt;. Here's what a minimal CI configuration for a
Racket package might look like:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-yaml"&gt;on: [push, pull_request]
name: CI
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@master
      - uses: Bogdanp/setup-racket@v0.1
        with:
          architecture: x64
          distribution: full
          variant: regular
          version: 7.4
      - run: raco test --drdr my-package-test/
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And a more involved one using a test matrix:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-yaml"&gt;on: [push, pull_request]
name: CI
jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        racket-version: ["7.3", "7.4"]
        racket-variant: ["regular", "CS"]
    name: "CI using Racket ${{ matrix.racket-version }} (${{ matrix.racket-variant }})"
    steps:
      - uses: actions/checkout@master
      - uses: Bogdanp/setup-racket@v0.1
        with:
          architecture: x64
          distribution: full
          variant: ${{ matrix.racket-variant }}
          version: ${{ matrix.racket-version }}
      - run: raco test --drdr my-package-test/
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The above configuration will cause the tests to be run against regular
Racket 7.3 and 7.4 as well as Racket-on-Chez 7.3 and 7.4.&lt;/p&gt;&lt;p&gt;One limitation right now is that the Action only supports installing
Racket on Linux targets, but I can update it to support Windows and
macOS if there is interest.&lt;/p&gt;&lt;p&gt;Check it out an let me know what you think!&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Announcing nemea</title><link>https://defn.io/2019/10/31/ann-nemea</link><guid>https://defn.io/2019/10/31/ann-nemea</guid><pubDate>Thu, 31 Oct 2019 11:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;I just open sourced one of the very first Racket code bases I've worked
on. The project is called &lt;a href="https://github.com/bogdanp/nemea"&gt;nemea&lt;/a&gt; and it's a tiny, privacy-preserving,
website analytics tracker. It doesn't do anything fancy, but it does
enough for my needs and, possibly, yours too so check it out!&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Announcing redis-rkt</title><link>https://defn.io/2019/10/06/ann-redis-rkt</link><guid>https://defn.io/2019/10/06/ann-redis-rkt</guid><pubDate>Sun, 6 Oct 2019 19:00:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;Another Racket thing! &lt;a href="https://github.com/bogdanp/racket-redis"&gt;redis-rkt&lt;/a&gt; is a new Redis client for Racket that
I've been working on these past few weeks. Compared to the existing
&lt;code&gt;redis&lt;/code&gt; and &lt;code&gt;rackdis&lt;/code&gt; packages, it:&lt;/p&gt;&lt;ul class="tight"&gt;&lt;li&gt;is fully documented,&lt;/li&gt;&lt;li&gt;is safer due to strict use of contracts,&lt;/li&gt;&lt;li&gt;is faster,&lt;/li&gt;&lt;li&gt;supports more commands and&lt;/li&gt;&lt;li&gt;its API tries to be idiomatic, rather than being just a thin wrapper
around Redis commands.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;a href="https://github.com/bogdanp/racket-redis"&gt;Check it out!&lt;/a&gt;&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Announcing chief</title><link>https://defn.io/2019/09/20/ann-chief</link><guid>https://defn.io/2019/09/20/ann-chief</guid><pubDate>Fri, 20 Sep 2019 20:00:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;I made a new Racket thing today! &lt;a href="https://github.com/bogdanp/racket-chief"&gt;chief&lt;/a&gt; is a port of a subset of
&lt;a href="http://ddollar.github.io/foreman/"&gt;foreman&lt;/a&gt;'s functionality to Racket. It lets you run sets of processes
together based on a &lt;code&gt;Procfile&lt;/code&gt;. If that sounds useful to you, do check
it out!&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Generators from Scratch</title><link>https://defn.io/2019/09/05/racket-generators</link><guid>https://defn.io/2019/09/05/racket-generators</guid><pubDate>Thu, 5 Sep 2019 17:00:00 +0300</pubDate><description>&lt;article&gt;&lt;h2&gt;Generators in Python&lt;/h2&gt;&lt;p&gt;One of the nice things about Python is that it comes with in-built
support for "generators", functions that can suspend themselves and be
resumed in the middle of processing.  Here's a generator that produces
the fibonacci series:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;def fib():
    x = 0
    y = 1
    while True:
        y, x = x + y, y
        yield x
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A generator is instantiated every time you call the &lt;code&gt;fib&lt;/code&gt; function.
Once you have a generator object, you can run it until the next time
it suspends itself by passing it to the &lt;code&gt;next&lt;/code&gt; function:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;&amp;gt;&amp;gt;&amp;gt; fibber = fib()
&amp;gt;&amp;gt;&amp;gt; next(fibber)
1
&amp;gt;&amp;gt;&amp;gt; next(fibber)
1
&amp;gt;&amp;gt;&amp;gt; next(fibber)
2
&amp;gt;&amp;gt;&amp;gt; next(fibber)
3
&amp;gt;&amp;gt;&amp;gt; next(fibber)
5
&amp;gt;&amp;gt;&amp;gt; next(fibber)
8
&amp;gt;&amp;gt;&amp;gt; next(fibber)
13
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The very first time we call &lt;code&gt;next&lt;/code&gt; on &lt;code&gt;fib&lt;/code&gt;, all of the code within it
runs and gets suspended at the &lt;code&gt;yield&lt;/code&gt;, returning the first value.
Every time we call &lt;code&gt;next&lt;/code&gt; on it after that, it resumes execution after
the &lt;code&gt;yield&lt;/code&gt;, which happens to be in a &lt;code&gt;while&lt;/code&gt; loop, so execution jumps
to the top of the loop.  &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; are modified and the function
&lt;code&gt;yield&lt;/code&gt;s again, suspending itself and returning the new value for &lt;code&gt;y&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;This can go on and on until the machine runs out of memory.&lt;/p&gt;&lt;p&gt;Another property of generators is that you can send values into them
every time they are resumed.  Here's a generator that computes the sum
of two numbers sent from the outside:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;def add():
    x = yield "expecting x"
    y = yield "expecting y"
    return x + y
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When you run this generator, execution first suspends before &lt;code&gt;x&lt;/code&gt; is
assigned and then it suspends again before &lt;code&gt;y&lt;/code&gt; is assigned.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;&amp;gt;&amp;gt;&amp;gt; adder = add()
&amp;gt;&amp;gt;&amp;gt; adder.send(None)
'expecting x'
&amp;gt;&amp;gt;&amp;gt; adder.send(1)
'expecting y'
&amp;gt;&amp;gt;&amp;gt; adder.send(2)
Traceback (most recent call last):
  File "&amp;lt;stdin&amp;gt;", line 1, in &amp;lt;module&amp;gt;
StopIteration: 3
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Think of the &lt;code&gt;send&lt;/code&gt; method as a lower level way of resuming a
generator than &lt;code&gt;next&lt;/code&gt;, with the added benefit that it allows you to
send values into the generator (whereas &lt;code&gt;next&lt;/code&gt; always sends &lt;code&gt;None&lt;/code&gt;).&lt;/p&gt;&lt;p&gt;Above, I first call &lt;code&gt;send&lt;/code&gt; with &lt;code&gt;None&lt;/code&gt;, which signals to the generator
that it can start executing.  Then I send it the value that should be
assigned to &lt;code&gt;x&lt;/code&gt;, followed by the value for &lt;code&gt;y&lt;/code&gt;.  Once it hits the
&lt;code&gt;return&lt;/code&gt; statement, the generator raises &lt;code&gt;StopIteration&lt;/code&gt; with the
returned value.&lt;/p&gt;&lt;h2&gt;Generators in Racket&lt;/h2&gt;&lt;p&gt;Racket's standard library also comes with built-in support for
generators via the &lt;a href="https://docs.racket-lang.org/reference/Generators.html"&gt;racket/generator&lt;/a&gt; module.  But what if it didn't?
Could we implement Python-like generators in the language itself?  The
answer, of course, is "yes."&lt;/p&gt;&lt;p&gt;Racket comes with an even more general mechanism for capturing and
resuming computation, called &lt;a href="https://en.wikipedia.org/wiki/Continuation"&gt;continuations&lt;/a&gt;.  Continuations let you
capture the execution of a program at a particular point as a
function.  When applied, that function will cause the program flow to
jump back to the point it refers to.  Here's an example:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-scheme"&gt;(+ 1
   (call/cc
    (lambda (k)
      (displayln "before k is applied")
      (k 2)
      (displayln "after k is applied")))
   3)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;call/cc&lt;/code&gt; captures the current continuation and passes it to another
function -- in this case the lambda we created -- and, as mentioned
above, when the continuation, &lt;code&gt;k&lt;/code&gt;, is applied the program will jump to
the spot that &lt;code&gt;call/cc&lt;/code&gt; was itself applied at, effectively "return"ing
from it with the value passed into the continuation.&lt;/p&gt;&lt;p&gt;So the above program:&lt;/p&gt;&lt;ol class="tight" start="1"&gt;&lt;li&gt;evaluates &lt;code&gt;1&lt;/code&gt;,&lt;/li&gt;&lt;li&gt;evaluates the &lt;code&gt;call/cc&lt;/code&gt;, reaching the line where &lt;code&gt;k&lt;/code&gt; is applied,&lt;/li&gt;&lt;li&gt;jumps back to the point where it started evaluating the &lt;code&gt;call/cc&lt;/code&gt;,&lt;/li&gt;&lt;li&gt;replaces the entire &lt;code&gt;call/cc&lt;/code&gt; block with the value passed to &lt;code&gt;k&lt;/code&gt;,&lt;/li&gt;&lt;li&gt;evaluates the &lt;code&gt;3&lt;/code&gt;, and, finally,&lt;/li&gt;&lt;li&gt;evaluates &lt;code&gt;(+ 1 2 3)&lt;/code&gt;.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The &lt;code&gt;(displayln "after k is applied")&lt;/code&gt; expression is never evaluated
by this program.&lt;/p&gt;&lt;p&gt;Here's another example, where we escape an infinite loop by way of a
continuation:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-scheme"&gt;(call/cc
 (lambda (k)
   (define (loop i)
     (when (= i 5)
       (k i))

     (loop (add1 i)))

   (loop 0)))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here we:&lt;/p&gt;&lt;ol class="tight" start="1"&gt;&lt;li&gt;evaluate the &lt;code&gt;call/cc&lt;/code&gt; and capture the current continuation, &lt;code&gt;k&lt;/code&gt;&lt;/li&gt;&lt;li&gt;recursively loop until &lt;code&gt;i&lt;/code&gt; is &lt;code&gt;5&lt;/code&gt;, when we apply &lt;code&gt;k&lt;/code&gt; and&lt;/li&gt;&lt;li&gt;jump back to the point where &lt;code&gt;call/cc&lt;/code&gt; was called and replace it
with the value passed to &lt;code&gt;k&lt;/code&gt;, which is &lt;code&gt;5&lt;/code&gt;.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Here's the same program with &lt;code&gt;k&lt;/code&gt; renamed to &lt;code&gt;return&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-scheme"&gt;(call/cc
 (lambda (return)
   (define (loop i)
     (when (= i 5)
       (return i))

     (loop (add1 i)))

   (loop 0)))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Does that look familiar?&lt;/p&gt;&lt;h3&gt;Delimited Continuations&lt;/h3&gt;&lt;p&gt;To implement our generators, we're going to use a more general type of
continuations called "&lt;a href="https://en.wikipedia.org/wiki/Delimited_continuation"&gt;delimited continuations&lt;/a&gt;."  These types of
continuations allow us to install "prompts", continuation frames
associated with specific prompt tags, and then later on in the
execution of the program, abort to the nearest enclosing prompt with a
particular tag without needing to have a reference to the captured
continuation.&lt;/p&gt;&lt;p&gt;Here's an example where I install a prompt and then abort to it:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-scheme"&gt;(define the-tag
  (make-continuation-prompt-tag))

(+ 1
   (call-with-continuation-prompt
    (lambda ()
      (displayln "before abort")
      (abort-current-continuation the-tag 2)
      (displayln "after abort"))
    the-tag
    (lambda (x)
      (displayln "received x")
      x))
   3)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When &lt;code&gt;abort-current-continuation&lt;/code&gt; is applied, execution jumps to the
abort handler (the third argument to &lt;code&gt;call-with-continuation-prompt&lt;/code&gt;)
and then returns from the point where &lt;code&gt;call-with-continuation-prompt&lt;/code&gt;
is applied.  So the above program prints&lt;/p&gt;&lt;pre&gt;&lt;code&gt;before abort
received x
6
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That shared prompt tag is how the abort function knows which
continuation prompt it should jump to.&lt;/p&gt;&lt;p&gt;With this in mind, we can define a prompt tag for our continuations:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-scheme"&gt;(define yield-tag
  (make-continuation-prompt-tag))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Followed by the &lt;code&gt;yield&lt;/code&gt; function:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-scheme"&gt;(define current-yielder
  (make-parameter
   (lambda _
     (error 'yield "may only be called within a generator"))))

(define (yield . args)
  (apply (current-yielder) args))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;yield&lt;/code&gt; looks up the value of the current yielder and then applies its
arguments to that function.  The default value of &lt;code&gt;current-yielder&lt;/code&gt;
raises an error so that &lt;code&gt;yield&lt;/code&gt; will fail if applied outside of a
generator.&lt;/p&gt;&lt;p&gt;Next, we implement the &lt;code&gt;generator&lt;/code&gt; function itself:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-scheme"&gt;(define (generator proc)
  (lambda _
    (define cont proc)

    (define (next . args)
      (call-with-continuation-prompt
       (lambda _
         (parameterize ([current-yielder yield])
           (begin0 (apply cont args)
             (set! cont (lambda _
                          (error 'generator "exhausted"))))))
       yield-tag))

    (define (yield . args)
      (call-with-current-continuation
       (lambda (k)
         (set! cont k)
         (abort-current-continuation yield-tag (lambda _
                                                 (apply values args))))
       yield-tag))

    next))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This function takes another function, &lt;code&gt;proc&lt;/code&gt;, as an argument and
returns a function that will create a generator "instance" (really,
it's just another function!) when applied.&lt;/p&gt;&lt;p&gt;Let's break down what happens inside the generator "factory" that is
created.  First, the original function is assigned to &lt;code&gt;cont&lt;/code&gt;.  Next,
an inner function called &lt;code&gt;next&lt;/code&gt; is defined.&lt;/p&gt;&lt;p&gt;When applied, &lt;code&gt;next&lt;/code&gt; installs a new continuation prompt with the
&lt;code&gt;yield-tag&lt;/code&gt;.  Inside the extent of the prompt, &lt;code&gt;next&lt;/code&gt; then sets
the current yielder to &lt;code&gt;yield&lt;/code&gt;, which we'll talk about in a second,
and returns the value of applying &lt;code&gt;cont&lt;/code&gt; to its arguments.&lt;/p&gt;&lt;p&gt;Next, the &lt;code&gt;yield&lt;/code&gt; function that &lt;code&gt;next&lt;/code&gt; references is defined.  When
applied, it captures its own continuation (the continuation of the
place that it itself was applied at), it then updates &lt;code&gt;cont&lt;/code&gt; to point
to that continuation and, finally, it aborts to the nearest prompt
with the &lt;code&gt;yield-tag&lt;/code&gt; (i.e. the place where the &lt;code&gt;next&lt;/code&gt; function was
applied), passing a function that returns &lt;code&gt;yield&lt;/code&gt;'s arguments to the
continuation prompt handler that &lt;code&gt;call-with-continuation-prompt&lt;/code&gt;
installed.  Because we didn't pass a handler function to
&lt;code&gt;call-with-continuation-prompt&lt;/code&gt;, the default handler, which applies
whatever argument it's given, is used.&lt;/p&gt;&lt;p&gt;Finally, the &lt;code&gt;next&lt;/code&gt; function is returned from the "factory."&lt;/p&gt;&lt;p&gt;And that's it!  That's pretty much all you need to do to get a working
implementation of generators.&lt;/p&gt;&lt;p&gt;Here's the definition of our &lt;code&gt;fib&lt;/code&gt; generator built using this function:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-scheme"&gt;(define fib
  (generator
   (lambda ()
     (let loop ([x 1]
                [y 1])
       (yield x)
       (loop y (+ x y))))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;and here it is in use:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-scheme"&gt;&amp;gt; (define fibber (fib))
&amp;gt; (fibber)
1
&amp;gt; (fibber)
1
&amp;gt; (fibber)
2
&amp;gt; (fibber)
3
&amp;gt; (fibber)
5
&amp;gt; (fibber)
8
&amp;gt; (fibber)
13
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And here's the &lt;code&gt;add&lt;/code&gt; generator:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-scheme"&gt;(define add
  (generator
   (lambda ()
     (+ (yield "expecting x")
        (yield "expecting y")))))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;in action:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-scheme"&gt;&amp;gt; (define adder (add))
&amp;gt; (adder)
"expecting x"
&amp;gt; (adder 1)
"expecting y"
&amp;gt; (adder 2)
3
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Pretty cool, eh?&lt;/p&gt;&lt;p&gt;Here's a video of me stepping through the &lt;code&gt;fib&lt;/code&gt; example with a debugger:&lt;/p&gt;&lt;p&gt;&lt;video controls="" src="https://media.defn.io/generators-screencast.mp4" width="100%"&gt;&lt;/video&gt;&lt;/p&gt;&lt;p&gt;P.S. This is actually pretty close to how the generator implementation
in Racket itself works.  Feel free to &lt;a href="https://github.com/racket/racket/blob/master/racket/collects/racket/generator.rkt"&gt;check that out&lt;/a&gt;,
the main differences between the two stem from some additional error
handling that the core implementation does.&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Racket for e-commerce</title><link>https://defn.io/2019/08/20/racket-ecommerce</link><guid>https://defn.io/2019/08/20/racket-ecommerce</guid><pubDate>Tue, 20 Aug 2019 21:00:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;&lt;i&gt;I had originally shared a version of this post with a small, private
mailing list, but then I figured there'd be no harm in sharing it with
a larger audience so here it is.&lt;/i&gt;&lt;/p&gt;&lt;p&gt;My girlfriend and I recently launched &lt;a href="https://www.matchacha.ro"&gt;matchacha.ro&lt;/a&gt;, a
small e-commerce site selling Japanese green tea here in Romania (the
site defaults to Romanian, but you can change the language by
scrolling to the bottom -- we don't ship outside of Romania, though,
sorry!) and I figured I'd write a little experience report for this
group.&lt;/p&gt;&lt;p&gt;I'll get the obvious stuff out of the way first: building the website
from scratch was &lt;i&gt;by far&lt;/i&gt; the easiest part.  Finding suppliers,
getting all the documentation right to be able to import the tea from
Japan -- nothing quite like Romanian bureaucracy to make you want to
give up on everything --, finding users and convincing them to buy the
product have all been significantly harder.&lt;/p&gt;&lt;p&gt;That said, as someone who wrote his first line of Racket almost
exactly a year ago, I have to say I found the whole process of
actually building the application delightful and only very rarely
frustrating (I'll get to that!).&lt;/p&gt;&lt;p&gt;My reasoning for building from scratch was threefold:&lt;/p&gt;&lt;ol class="tight" start="1"&gt;&lt;li&gt;I wanted to avoid being locked in to VC-backed SaaS so that excluded
most of the large players, like Shopify and BigCommerce,&lt;/li&gt;&lt;li&gt;I wanted to avoid anything written in PHP, and&lt;/li&gt;&lt;li&gt;I wanted to have some fun.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The app is a server-rendered "classic" web application with minimal JS
and it does most of the things you would expect from an e-commerce
app.  There are products, product variants, collections of products,
shopping carts, invoices that are generated on the fly, user accounts
(hidden right now), blog posts and dynamic pages, promo codes,
affiliate links, credit card payments, and a pretty powerful
administration panel.  It is backed by a Postgres database and it
comes in at a neat ~10k cloc of Racket code (including unit and
end-to-end tests):&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ cloc matchacha/ matchacha-tests/ migrations/ resources/css/ resources/js/
     295 text files.
     294 unique files.
      88 files ignored.

github.com/AlDanial/cloc v 1.82  T=0.18 s (1182.1 files/s, 102674.8 lines/s)
-------------------------------------------------------------------------------
Language                     files          blank        comment           code
-------------------------------------------------------------------------------
Racket                          87           1924            136          10374
Sass                            35            626              3           2928
JavaScript                      20            142             29            646
SQL                             65            145            454            573
-------------------------------------------------------------------------------
SUM:                           207           2837            622          14521
-------------------------------------------------------------------------------
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That number does not include all of the supporting libraries that I've
written, which come in at about another ~13k cloc including unit tests.&lt;/p&gt;&lt;p&gt;Speaking of libraries, here are all the ones the application currently
depends on:&lt;/p&gt;&lt;ul class="tight"&gt;&lt;li&gt;base&lt;/li&gt;&lt;li&gt;component-lib&lt;/li&gt;&lt;li&gt;crypto-lib&lt;/li&gt;&lt;li&gt;db-lib&lt;/li&gt;&lt;li&gt;deta-lib&lt;/li&gt;&lt;li&gt;forms-lib&lt;/li&gt;&lt;li&gt;gregor-lib&lt;/li&gt;&lt;li&gt;koyo-lib&lt;/li&gt;&lt;li&gt;koyo-sentry&lt;/li&gt;&lt;li&gt;marionette-lib&lt;/li&gt;&lt;li&gt;mobilpay&lt;/li&gt;&lt;li&gt;net-cookies-lib&lt;/li&gt;&lt;li&gt;postmark-client&lt;/li&gt;&lt;li&gt;rackunit-lib&lt;/li&gt;&lt;li&gt;sql&lt;/li&gt;&lt;li&gt;srfi-lite-lib&lt;/li&gt;&lt;li&gt;struct-define&lt;/li&gt;&lt;li&gt;struct-plus-plus&lt;/li&gt;&lt;li&gt;threading-lib&lt;/li&gt;&lt;li&gt;twilio&lt;/li&gt;&lt;li&gt;web-server-lib&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;That doesn't include transitive dependencies, because I don't know how
to list them all, but I want to thank everyone who has worked on any
of these and on any of these libraries' own dependencies.&lt;/p&gt;&lt;p&gt;The deployment process is fairly simple.  GitHub Actions picks up all
commits to master, runs their tests, and, on success, for commits that
are tagged a certain way, packages up a distribution then ships it up
to my dedicated server, unzips it, updates a symlink and restarts a
service.  Deployments are &lt;i&gt;not&lt;/i&gt; zero-downtime at this point and I'm
planning to switch to an AB-style deployment model once the site has
enough traffic to warrant it, but a second of downtime during each
deployment is fine for now.  Rolling back to an earlier version means
updating the aforementioned symlink and restarting the service.&lt;/p&gt;&lt;p&gt;The ability to package up the application so that it can be
distributed easily is one of my favorite things about Racket.  This is
a distinguishing feature that most of the dynamic languages that I
have used either lack entirely or it's not nearly as well integrated
as one would like.  With &lt;a href="https://koyo.defn.io"&gt;koyo&lt;/a&gt;, all it takes is&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ raco koyo dist
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;and all that really does under the hood is shell out to &lt;code&gt;raco exe&lt;/code&gt; and
then &lt;code&gt;raco dist&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Production runtime errors (of which there has been exactly one so far,
triggered by me, not one of my users) are sent to the SaaS version of
&lt;a href="https://sentry.io"&gt;Sentry&lt;/a&gt;.  The low runtime error rate is due to the amount of stuff
that Racket manages to catch at compile time compared to other dynamic
languages and to some fairly extensive automated testing.&lt;/p&gt;&lt;p&gt;I use &lt;a href="https://docs.racket-lang.org/rackunit/api.html?q=rackunit"&gt;rackunit&lt;/a&gt; for all my tests.  It gets the job done, but I find
it's not quite as good as, say, Python's &lt;a href="https://pytest.org/en/latest/"&gt;pytest&lt;/a&gt;.  My main complaint
is I like to organize tests in suites, but those aren't run
automatically, so in each of my test modules I have to call
&lt;code&gt;(run-tests some-suite)&lt;/code&gt; within a &lt;code&gt;test&lt;/code&gt; submodule, which splits up
the reporting that &lt;code&gt;raco test&lt;/code&gt; generates.  If I centralize things so
that I have a single &lt;code&gt;run-all-tests.rkt&lt;/code&gt; file, then I can't leverage
the &lt;code&gt;-j&lt;/code&gt; parameter to &lt;code&gt;raco test&lt;/code&gt;, meaning my tests run
&lt;i&gt;significantly&lt;/i&gt; slower.  Apart from that, I would like to be able to
run a particular test suite or test case from the command line, but I
haven't been able to figure out how to do that yet.  In addition, it
would be nice to be able to tell &lt;code&gt;raco test&lt;/code&gt; that I want to list the
&lt;code&gt;top n&lt;/code&gt; slowest tests along with their runtimes, which is something
that pytest can easily do.&lt;/p&gt;&lt;p&gt;For end-to-end and screenshot tests, I use my own library,
&lt;a href="https://docs.racket-lang.org/marionette/index.html?q=marionette"&gt;marionette&lt;/a&gt;, together with rackunit to control a live Firefox
instance and make assertions.  Some of those assertions generate
screenshots of a web page and compare them with old versions committed
to the repo by shelling out to &lt;a href="https://imagemagick.org/index.php"&gt;ImageMagick&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Error reporting during test runs or when I'm manually testing stuff is
the most frustrating part of working with Racket.  I run the tests as
well as the app (in local development mode) with &lt;code&gt;errortrace&lt;/code&gt; turned
on, but I still find myself often scratching my head trying to figure
out &lt;i&gt;where&lt;/i&gt; certain errors originate.  This is an area where Racket
really needs to improve.  It absolutely has to be able to show the
programmer where every error originated and that "where" can't be
"somewhere inside this 20-line-long for-loop in file x.rkt, good
luck!"&lt;/p&gt;&lt;p&gt;&lt;a href="https://racket-mode.com"&gt;racket-mode&lt;/a&gt; for Emacs is a joy.  I can easily spin up a REPL for any
module that I'm working on and tinker with it, with some pretty good
facilities for cleaning up imports, running tests and a macro stepper.&lt;/p&gt;&lt;p&gt;Continuations are fantastic for rapid development.  I use them for
small things like increasing/decreasing item quantities in the
shopping cart and preserving state between the steps of the checkout
process, but I will eventually be moving off them as the site grows
because, from the user's perspective, it's bad UX to have URLs expire
randomly just because a deployment happened to occur when you were
browsing the site.  At this stage, though, that's not a problem, and
continuation expiration is handled gracefully: the user is redirected
to the original page they were on before clicking the continuation
link (or submitting the form, etc.) and shown a flash message.&lt;/p&gt;&lt;p&gt;If you visited the website (and assuming you live in Europe), you may
have noticed how fast it is.  Part of that is because of how fast
Racket itself is, and part of it is me being judicious about what code
runs on each page.  &lt;a href="https://koyo.defn.io"&gt;koyo&lt;/a&gt; has an instrumenting profiler and, in
development mode, all pages render a little floating widget that I can
expand to show a trace of all the instrumented places (inspired by
&lt;a href="https://miniprofiler.com/"&gt;MiniProfiler&lt;/a&gt;).&lt;/p&gt;&lt;p&gt;I didn't find the lack of libraries to be a problem -- I just wrote
whatever it was I needed at the time -- and the documentation for the
stuff that does exist is pretty good.  Documentation for Racket itself
tends to be great, although a bit "stuffy".  That's a challenge for
someone who, like me, tends to skim a lot.  I would like to see more
code examples intended for people in a hurry.&lt;/p&gt;&lt;p&gt;One area where I didn't build my own thing was I18n/L10n.  For that,
the app relies on &lt;a href="https://srfi.schemers.org/srfi-29/srfi-29.html"&gt;SRFI-29&lt;/a&gt;.  I would have preferred to use the more
standard tooling, like &lt;a href="https://www.gnu.org/software/gettext/"&gt;gettext&lt;/a&gt;, but there's no library for that.
Yet.&lt;/p&gt;&lt;p&gt;The community is welcoming and thoughtful.  Although I don't
participate frequently, I enjoy reading racket-users, racket-dev, the
subreddit and the Slack channel.  I've learned a ton by reading blog
posts by Alexis King, Alex Harsanyi, Greg Hendershott, and others.  I
love it whenever a new issue of &lt;a href="https://racket-news.com/"&gt;Racket News&lt;/a&gt; comes out.&lt;/p&gt;&lt;p&gt;In terms of development time, this was all spread out across the past
year, but I estimate I only spent about a full "work" month on the
application itself and about another one working on all of those
supporting libraries I mentioned.  I think that's pretty good, given
all the things the application does.&lt;/p&gt;&lt;p&gt;Overall, I'm really happy with my choice to use Racket so far!&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Announcing deta</title><link>https://defn.io/2019/07/16/ann-deta</link><guid>https://defn.io/2019/07/16/ann-deta</guid><pubDate>Tue, 16 Jul 2019 10:00:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;I just released the first version of &lt;a href="https://deta.defn.io"&gt;deta&lt;/a&gt;, a Racket library for
mapping between database tables and structs.  A bit like Elixir's
Ecto.&lt;/p&gt;&lt;p&gt;You can install it from the package server with:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;raco pkg install deta
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Check it out!&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Announcing racket-sentry</title><link>https://defn.io/2019/07/02/ann-sentry</link><guid>https://defn.io/2019/07/02/ann-sentry</guid><pubDate>Tue, 2 Jul 2019 10:00:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;I just released the first version of &lt;a href="https://github.com/Bogdanp/racket-sentry"&gt;sentry&lt;/a&gt;, a Racket
library that lets you capture exceptions using the &lt;a href="https://sentry.io"&gt;Sentry&lt;/a&gt; API.&lt;/p&gt;&lt;p&gt;You can install it from the package server with:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;raco pkg install sentry
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And the docs should show up on the package server within the next couple
of days. In the mean time, you can run&lt;/p&gt;&lt;pre&gt;&lt;code&gt;raco docs sentry
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;after installing the package to read its docs locally.&lt;/p&gt;&lt;/article&gt;</description></item><item><title>racket/gui saves the day</title><link>https://defn.io/2019/06/17/racket-gui-saves</link><guid>https://defn.io/2019/06/17/racket-gui-saves</guid><pubDate>Mon, 17 Jun 2019 10:00:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;Yesterday, I bought an icon pack containing over 3,000 (!) SVG files
and macOS utterly failed me when I tried to search the unarchived
folder.&lt;/p&gt;&lt;p&gt;&lt;img alt="empty search screen" src="/img/racket-gui-saves-svg-search.png"/&gt;&lt;/p&gt;&lt;p&gt;So I did what any self-respecting Racketeer would do.  I used this as
an excuse to play around with Racket's built-in GUI library!&lt;/p&gt;&lt;p&gt;&lt;img alt="the final product" src="/img/racket-gui-saves-icon-viewer.png"/&gt;&lt;/p&gt;&lt;h2&gt;Diving in&lt;/h2&gt;&lt;p&gt;The Racket GUI toolkit is a retained mode style API for building user
interfaces, meaning that you work with it by instantiating objects
that represent the things that ought to be drawn on your screen and
then the system draws them for you and, in order to add custom
behavior when the user interacts with those objects, you register
callbacks that react to certain events.&lt;/p&gt;&lt;p&gt;To render a window on the screen, all you have to do is:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-scheme"&gt;#lang racket/gui

(require racket/class)

(define window
  (new frame%
       [label "Hello World!"]))

(send window show #t)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The above code instantiates a new frame object -- whose label (title)
is "Hello World!" -- and then tells it to make itself visible.  Easy
as that!&lt;/p&gt;&lt;h3&gt;&lt;code&gt;racket/class&lt;/code&gt; crash course&lt;/h3&gt;&lt;p&gt;The GUI library is built on top of Racket's class system.  All you
need to know to make use of it in this context is:&lt;/p&gt;&lt;ul class="tight"&gt;&lt;li&gt;class names conventionally have a &lt;code&gt;%&lt;/code&gt; suffix,&lt;/li&gt;&lt;li&gt;interface names conventionally have a &lt;code&gt;&amp;lt;%&amp;gt;&lt;/code&gt; suffix,&lt;/li&gt;&lt;li&gt;you instantiate a class by using the &lt;code&gt;new&lt;/code&gt; macro, giving it the name
of the class you want to instantiate followed by zero or more field
values and&lt;/li&gt;&lt;li&gt;you send objects messages using the &lt;code&gt;send&lt;/code&gt; macro.&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;Laying things out&lt;/h3&gt;&lt;p&gt;With the above in mind, we can go ahead and lay out our interface:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-scheme"&gt;#lang racket/gui

(require racket/class)

(define window
  (new frame%
       [label "Icon Viewer"]
       [width 800]
       [height 600]))

(define panel
  (new vertical-panel%
       [parent window]))

(define search-box
  (new text-field%
       [parent panel]
       [label #f]))

(define list-box
  (new list-box%
       [parent panel]
       [choices empty]
       [label #f]))

(define canvas
  (new canvas%
       [parent panel]))

(send window show #t)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Assigning a &lt;code&gt;parent&lt;/code&gt; to a widget ensures that said widget renders
within said object.  So, above, we have defined the following
hierarchy:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;window
└── panel
    ├── search-box
    ├── list-box
    └── canvas
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;panel&lt;/code&gt; lays out its children underneath one another in a single
column and its children should be fairly self-explanatory:&lt;/p&gt;&lt;ul class="tight"&gt;&lt;li&gt;&lt;code&gt;search-box&lt;/code&gt; is where we're going to type our search filters,&lt;/li&gt;&lt;li&gt;&lt;code&gt;list-box&lt;/code&gt; is where we're going to list the filtered files and&lt;/li&gt;&lt;li&gt;&lt;code&gt;canvas&lt;/code&gt; is where we're going to draw the selected file.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;If you run the above code, then you should get a UI that's nearly
identical to the one I showed at the beginning of this article.&lt;/p&gt;&lt;h3&gt;Adding behavior&lt;/h3&gt;&lt;p&gt;Despite looking like the screenshot, the code above doesn't implement
any of the behavior of the final product yet.  So let's add that!&lt;/p&gt;&lt;p&gt;You may have noticed that the &lt;code&gt;list-box%&lt;/code&gt; class takes a &lt;code&gt;choices&lt;/code&gt;
field.  Let's start there.&lt;/p&gt;&lt;p&gt;At the top of the file, we can gather all of the names of the SVG
files in the current directory into a list:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-scheme"&gt;(define folder-path
  (current-directory))

(define filenames
  (for/list ([filename (directory-list folder-path)]
             #:when (equal? (path-get-extension filename) #".svg"))
    (path-&amp;gt;string filename)))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And then we can pass that list to the &lt;code&gt;list-box&lt;/code&gt; when we instantiate
it via the aforementioned &lt;code&gt;choices&lt;/code&gt; field:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-scheme"&gt;(define list-box
  (new list-box%
       [parent panel]
       [choices filenames]
       [label #f]))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Run the code from a folder that contains SVG files and you should now
see those files being listed in the UI.&lt;/p&gt;&lt;p&gt;On to filtering.  When someone enters text into the &lt;code&gt;search-box&lt;/code&gt;, we
want the list of filenames to be narrowed down to only those filenames
that contain the search string.  To do this, we can give the
&lt;code&gt;search-box&lt;/code&gt; a callback that it should execute whenever its contents
change:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-scheme"&gt;(define search-box
  (new text-field%
       [parent panel]
       [label #f]
       [callback (lambda (sb e)
                   (define text (send sb get-value))

                   (send list-box clear)
                   (for ([filename filenames]
                         #:when (string-contains? filename text))
                     (send list-box append filename)))]))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The callback takes the &lt;code&gt;search-box&lt;/code&gt; object itself and an object
representing the event that occurred as arguments.  It then extracts
the text from the &lt;code&gt;search-box&lt;/code&gt;, empties out the &lt;code&gt;list-box&lt;/code&gt; and only
adds back in those filenames which contain the search string.&lt;/p&gt;&lt;p&gt;Finally, it's time to display the selected SVGs.  For that, we're
going to need the &lt;code&gt;rsvg&lt;/code&gt; library.  To install it, you can run:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;raco pkg install rsvg
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It relies on &lt;code&gt;librsvg&lt;/code&gt; so you're going to need to have that available
as well.  On macOS, you can install it using homebrew:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;brew install librsvg
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To hook things up, we require &lt;code&gt;rsvg&lt;/code&gt; then add a callback to the
&lt;code&gt;list-box&lt;/code&gt; so that, when an item is clicked, we can read the file from
disk and display it on the &lt;code&gt;canvas&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-scheme"&gt;
(require rsvg)

;; ...

(define list-box
  (new list-box%
       [parent panel]
       [label #f]
       [choices filenames]
       [callback (lambda (tb e)
                   (define selection (send tb get-string-selection))
                   (define filename (and selection (build-path folder-path selection)))
                   (when filename
                     (define svg (load-svg-from-file filename 3))
                     (define dc (send canvas get-dc))
                     (send dc clear)
                     (send dc draw-bitmap svg 0 0)))]))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And that's it!  At this point, you should have a working icon viewer.
Not bad for about 50 lines of code.&lt;/p&gt;&lt;p&gt;The &lt;a href="/code/icon-viewer.rkt"&gt;final version&lt;/a&gt; is a little longer because
I added support for debouncing and copying selected files elsewhere by
double clicking on them, but it still clocks in at only about 100
lines of code!&lt;/p&gt;&lt;p&gt;You can find the final version of the code &lt;a href="/code/icon-viewer.rkt"&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Announcing marionette</title><link>https://defn.io/2019/06/08/ann-marionette</link><guid>https://defn.io/2019/06/08/ann-marionette</guid><pubDate>Sat, 8 Jun 2019 15:45:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;I just released the first version of &lt;a href="https://github.com/Bogdanp/marionette"&gt;marionette&lt;/a&gt; (named
after &lt;a href="https://firefox-source-docs.mozilla.org/testing/marionette/marionette/Protocol.html"&gt;the protocol&lt;/a&gt; it implements), a Racket library that lets you
remotely control the Firefox web browser. Think "puppeteer", but for
Firefox.&lt;/p&gt;&lt;p&gt;You can install it from the package server with:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;raco pkg install marionette
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And the docs should show up on the package server within the next couple
of days. In the mean time, you can run&lt;/p&gt;&lt;pre&gt;&lt;code&gt;raco docs marionette
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;after installing the package to read its docs locally.&lt;/p&gt;&lt;p&gt;It's still early days, but the library already supports most of the
basic things you would expect it to. If you try it out, let me know what
you think!&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Using GitHub Actions to Test Racket Code</title><link>https://defn.io/2019/05/01/github-actions-for-racket</link><guid>https://defn.io/2019/05/01/github-actions-for-racket</guid><pubDate>Wed, 1 May 2019 17:00:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;&lt;b&gt;This article is outdated as of 2020/05/05 because it refers to the
previous implementation of GitHub Actions. I've put together a revised
version at &lt;a href="/2020/05/05/github-actions-for-racket-revised"&gt;Using GitHub Actions to Test Racket Code (Revised)&lt;/a&gt; so
you should read that instead.&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Like &lt;a href="https://alex-hhh.github.io/2019/04/build-racket-packages-with-azure-pipelines.html"&gt;Alex Harsányi&lt;/a&gt;, I've been looking for a good,
free-as-in-beer, alternative to Travis CI. For now, I've settled on
&lt;a href="https://github.com/features/actions"&gt;GitHub Actions&lt;/a&gt; because using them is straightforward and
because I saves me from creating yet another account with some other
company.&lt;/p&gt;&lt;p&gt;GitHub Actions revolves around the concept of "workflows" and "actions".
Actions execute arbitrary Docker containers on top of a checked-out
repository and workflows describe which actions need to be executed when
a particular &lt;a href="https://developer.github.com/actions/managing-workflows/workflow-configuration-options/#events-supported-in-workflow-files"&gt;event&lt;/a&gt; occurs. During the execution of a workflow, all
actions, including ones running in parallel, share the same physical
workspace. All of this stuff is declaratively specified using &lt;a href="https://github.com/hashicorp/hcl"&gt;HCL&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Here's an example workflow:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-hcl"&gt;workflow "demo" {
  on = "push"
  resolves = ["echo"]
}

action "make-a" {
  uses = "docker://alpine"
  runs = ["sh", "-c", "echo Hello &amp;gt; $GITHUB_WORKSPACE/a"]
}

action "make-b" {
  uses = "docker://alpine"
  runs = ["sh", "-c", "echo World &amp;gt; $GITHUB_WORKSPACE/b"]
}

action "echo" {
  needs = ["make-a", "make-b"]
  uses = "docker://alpine"
  runs = ["sh", "-c", "cat $GITHUB_WORKSPACE/a $GITHUB_WORKSPACE/b"]
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is saying that there is a workflow called "demo" that is executed
whenever anything is pushed to the repository. That workflow's goal
is to execute the "echo" action, which happens to depend on actions
"make-a" and "make-b". When the workflow is triggered, those two actions
are run first and the "echo" action only runs after they both succeed.
In this particular example, each of the actions runs a shell command
on top of the &lt;code&gt;alpine&lt;/code&gt; docker image, but I could've picked any other
image from Docker Hub or any existing GitHub Action repository. &lt;a href="https://developer.github.com/actions/managing-workflows/workflow-configuration-options/"&gt;This
page&lt;/a&gt; describes all of the various workflow configuration options.&lt;/p&gt;&lt;p&gt;If you save the above config in a file called &lt;code&gt;.github/main.workflow&lt;/code&gt; in
any GitHub repository and visit the "Actions" tab, then -- assuming you
have access to the GitHub Actions beta -- you should see the pipeline
execute almost immediately and output:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Hello
World
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We can leverage all of this to test a Racket package is by using Jack
Firth's &lt;a href="https://hub.docker.com/r/jackfirth/racket"&gt;racket&lt;/a&gt; image (or you could roll your own and host it on Docker
Hub yourself):&lt;/p&gt;&lt;pre&gt;&lt;code info="language-hcl"&gt;workflow "main" {
  on = "push"
  resolves = ["test"]
}

action "test" {
  uses = "docker://jackfirth/racket:7.2"
  runs = "/github/workspace/ci/test.sh"
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here are the contents of &lt;code&gt;ci/test.sh&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-bash"&gt;#!/usr/bin/env bash

set -euo pipefail

pushd "$GITHUB_WORKSPACE"
raco pkg install --batch --auto testpackage/
raco test testpackage/
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The script sets the working directory to &lt;code&gt;$GITHUB_WORKSPACE&lt;/code&gt;, installs
all of &lt;code&gt;testpackage&lt;/code&gt;'s (a hypothetical package for the purposes of this
article) dependencies and then runs its tests.&lt;/p&gt;&lt;p&gt;That's all there is to it! With about 12 LOC we've put together a basic
workflow that tests a package on every commit.&lt;/p&gt;&lt;h2&gt;Gotchas &amp;amp; Limitations&lt;/h2&gt;&lt;p&gt;Like I mentioned before, all actions in a workflow share the same
workspace so it's possible for actions to clash with one another when
they operate on the filesystem. That's something you have to keep in
mind when designing your workflows.&lt;/p&gt;&lt;p&gt;Workflow and action names share a namespace. If you call your workflow
"test" and your action "test", you'll get an error saying the workflow
is invalid, but it won't point out exactly why.&lt;/p&gt;&lt;p&gt;Finally, there's no built-in support for notifications. When builds
fail, you won't notice unless you visit the Actions tab. For one of my
non-OSS projects, I've set up a Telegram bot that I can use to notify a
particular channel whenever builds succeed or fail. That works, but it's
fairly ugly because there doesn't seem to be any way to conditionally
execute actions (i.e. things like "if action &lt;code&gt;a&lt;/code&gt; fails then run action
&lt;code&gt;b&lt;/code&gt;"), so I had to bundle the notification-handling code into each of my
action scripts.&lt;/p&gt;&lt;/article&gt;</description></item><item><title>The Problem with SSH Agent Forwarding</title><link>https://defn.io/2019/04/12/ssh-forwarding</link><guid>https://defn.io/2019/04/12/ssh-forwarding</guid><pubDate>Fri, 12 Apr 2019 14:00:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;After hacking the &lt;a href="https://matrix.org"&gt;matrix.org&lt;/a&gt; website today, the
attacker opened a series of GitHub issues mentioning the flaws he
discovered. In &lt;a href="https://github.com/matrix-org/matrix.org/issues/357"&gt;one of those issues&lt;/a&gt;, he mentions that "complete
compromise could have been avoided if developers were prohibited from
using [SSH agent forwarding]."&lt;/p&gt;&lt;p&gt;Here's what &lt;code&gt;man ssh_config&lt;/code&gt; has to say about &lt;code&gt;ForwardAgent&lt;/code&gt;:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;Agent forwarding should be enabled with caution.  Users with the
ability to bypass file permissions on the remote host (for the
agent's Unix-domain socket) can access the local agent through the
forwarded connection.  An attacker cannot obtain key material from
the agent, however they can perform operations on the keys that
enable them to authenticate using the identities loaded into the
agent.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Simply put: if your jump box is compromised and you use SSH agent
forwarding to connect to another machine through it, then you risk
also compromising the target machine!&lt;/p&gt;&lt;p&gt;Instead, you should use either &lt;code&gt;ProxyCommand&lt;/code&gt; or &lt;code&gt;ProxyJump&lt;/code&gt; (added in
OpenSSH 7.3).  That way, ssh will forward the TCP connection to the
target host via the jump box and the actual connection will be made on
your workstation.  If someone on the jump box tries to MITM your
connection, then you will be warned by ssh.&lt;/p&gt;&lt;p&gt;Here's what a config file that uses &lt;code&gt;ProxyCommand&lt;/code&gt; might look like:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-ssh-config"&gt;Host bastion
  HostName bastion.example.com

Host target
  HostName target.example.internal
  Port 2222
  ProxyCommand ssh -W %h:%p bastion
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And here's the equivalent config using &lt;code&gt;ProxyJump&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-ssh-config"&gt;Host bastion
  HostName bastion.example.com

Host target
  HostName target.example.internal
  Port 2222
  ProxyJump bastion
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Of course, you can also specify either option via the command line:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-bash"&gt;ssh -o 'ProxyJump=bastion.example.com' target.example.internal -p 2222
&lt;/code&gt;&lt;/pre&gt;&lt;/article&gt;</description></item><item><title>Continuations for Web Development</title><link>https://defn.io/2019/04/07/web-continuations</link><guid>https://defn.io/2019/04/07/web-continuations</guid><pubDate>Sun, 7 Apr 2019 17:00:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;One of the distinguishing features of Racket's built-in &lt;a href="https://docs.racket-lang.org/web-server/"&gt;web-server&lt;/a&gt;
is that it supports the use of &lt;a href="https://en.wikipedia.org/wiki/Continuation"&gt;continuation&lt;/a&gt;s in a web context.  This
is a feature I've only ever seen in Smalltalk's &lt;a href="https://github.com/SeasideSt/Seaside"&gt;Seaside&lt;/a&gt; before,
though Racket's version is more powerful.&lt;/p&gt;&lt;p&gt;I've been leveraging continuations in an e-commerce application I've
been building these past couple of months and I wanted to write down
my thoughts.  Let's dive right in.&lt;/p&gt;&lt;h2&gt;First, an Example&lt;/h2&gt;&lt;pre&gt;&lt;code info="language-scheme"&gt;#lang web-server/insta

(define (render-counter counter)
  (send/suspend/dispatch
   (lambda (embed/url)
     (response/xexpr
      `(div
        (h1 "Current count: " ,(number-&amp;gt;string counter))
        (a ((href ,(embed/url (lambda (req)
                                (render-counter (+ counter 1))))))
           "Increment"))))))

(define (start req)
  (render-counter 0))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;i&gt;Note: I'm using &lt;code&gt;#lang web-server/insta&lt;/code&gt; here, which I realize may be
off-putting to some readers (I know it rubbed me the wrong way when I
was first reading about web development in Racket).  There are more
familiar (less magical) ways to implement web servers in Racket than
this, but this is the most succinct.&lt;/i&gt;&lt;/p&gt;&lt;p&gt;The entry point for this server (the &lt;code&gt;start&lt;/code&gt; function) calls
&lt;code&gt;render-counter&lt;/code&gt; with a starting value of &lt;code&gt;0&lt;/code&gt; for every new request
that comes in.  &lt;code&gt;render-counter&lt;/code&gt; then demarcates the start of the
continuation with the call to &lt;a href="https://docs.racket-lang.org/web-server/servlet.html#%28def._%28%28lib._web-server%2Fservlet%2Fweb..rkt%29._send%2Fsuspend%2Fdispatch%29%29"&gt;send/suspend/dispatch&lt;/a&gt; and, finally, it
renders some HTML that displays the current value of the counter as
well as a link that, when clicked, will recursively &lt;code&gt;render-counter&lt;/code&gt;
with an incremented &lt;code&gt;counter&lt;/code&gt; value.&lt;/p&gt;&lt;p&gt;Notice how naturally this code flows and the fact that the
continuation (the anonymous function that is passed to &lt;code&gt;embed/url&lt;/code&gt;) is
able to reference bindings in the scope of its parent.&lt;/p&gt;&lt;p&gt;Here's what that short piece of code gets you:&lt;/p&gt;&lt;p&gt;&lt;video controls="" src="https://media.defn.io/continuations-demo.mp4" width="100%"&gt;&lt;/video&gt;&lt;/p&gt;&lt;p&gt;I think this is cool as hell.  In essence, continuations let you write
code that manipulates objects (a counter, a shopping a cart, &lt;a href="https://github.com/Bogdanp/racket-forms/blob/master/examples/blog-continuations.rkt#L161-L183"&gt;a form&lt;/a&gt;,
etc.)  local to the current web page without having to do duplicate
work -- image a shopping cart where you only retrieve a product from
the database when you render the product page, but you close over the
product when adding it to the cart -- and without having to give said
code an explicit route.  The latter is both a strength and a weakness,
as I argue below.&lt;/p&gt;&lt;h2&gt;Then, the Bad&lt;/h2&gt;&lt;p&gt;As with most things, continuations on the web come with a set of
trade-offs.&lt;/p&gt;&lt;p&gt;Continuations are local to a Racket process, meaning that any web
server that leverages them is &lt;i&gt;stateful&lt;/i&gt;.  That's not inherently a bad
thing, but it does mean that you have to be careful when it comes to
load balancing and deploying new versions of your application: all new
deployments invalidate all existing continuations and you have to rely
on session affinity (cookie each user and use that to determine which
server they connect to) to tie individual users to particular
instances of the web server.&lt;/p&gt;&lt;p&gt;Racket's particular implementation of continuations stores the
continuation id as a parameter in the URI, meaning that if someone
guesses the id of your continuation then they can effectively steal
your session.  This is fairly easy to work around by leveraging
&lt;a href="https://docs.racket-lang.org/guide/parameterize.html"&gt;dynamic binding&lt;/a&gt; in racket:&lt;/p&gt;&lt;ol class="tight" start="1"&gt;&lt;li&gt;for each request, read the continuation security token from the user
agent; if it doesn't exist then generate a large unique value and
store that in a cookie on the user agent,&lt;/li&gt;&lt;li&gt;&lt;a href="https://docs.racket-lang.org/guide/parameterize.html"&gt;dynamically bind&lt;/a&gt; the current continuation security
token for the request,&lt;/li&gt;&lt;li&gt;before executing each continuation, ensure that the value of the
user's continuation security token cookie is the same as the value
of the current continuation security token parameter; if the value
is different then return a 403 Forbidden or similar response.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Because of how the continuation machinery works, each continuation
will "remember" the continuation security token of the request that
created it, ensuring that each continuation is tied to the browser
session that it was created by.  You can find an implementation of
this pattern in my &lt;a href="https://github.com/Bogdanp/racket-webapp-template/blob/master/app-name-here/components/continuation.rkt#L1-L85"&gt;racket-webapp-template&lt;/a&gt; project.  All in all, it
takes less than a hundred lines of code to implement.&lt;/p&gt;&lt;p&gt;Because of the session-stealing issue and the fact that continuation
URLs are fairly ugly, they're not a good match for URLs that should be
shareable between users (or for SEO, for that matter).  I tend to
limit my use of continuations to "actions" that a user may perform on
an object local to the current page (eg. adding an item to the
shopping cart, or increasing the quantity of said item in the cart,
etc.).&lt;/p&gt;&lt;p&gt;Finally, because continuations close over local scope, any objects
captured are going to live for the duration that the continuation
exists.  So if you're not careful, then you may end up leaking memory.
To combat this and to prevent servers' memory usage from growing
indefinitely, the &lt;code&gt;web-server&lt;/code&gt; library has a robust implementation of
an LRU continuation manager that expires continuations quicker the
more memory pressure there is.  In addition, you can write your own
manager implementation to suit your application if the built-in ones
don't cut it.&lt;/p&gt;&lt;h2&gt;And a Conclusion&lt;/h2&gt;&lt;p&gt;That may seem like a lot of "bad", but all of those points are
straightforward tradeoffs that I believe are worth it in the long run
given the ergonomics that continuations on the web buy you.  Plus,
writing code in this way is just so. much. fun!&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Google Groups Without Google Accounts</title><link>https://defn.io/2019/02/05/google-groups-without-google-accounts</link><guid>https://defn.io/2019/02/05/google-groups-without-google-accounts</guid><pubDate>Tue, 5 Feb 2019 20:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;It turns out that when you &lt;a href="/2019/02/04/bye-bye-google/"&gt;delete your Google accounts&lt;/a&gt;, Google
unsubscribes you from any (public and private) Google Groups you're
a member of. I found out about this only because my inbox traffic
these past couple of days felt unusually light so I went and looked at
&lt;a href="https://groups.google.com/forum/#!forum/racket-users"&gt;racket-users&lt;/a&gt; and, lo and behold, there were a bunch of new posts I
hadn't received.&lt;/p&gt;&lt;p&gt;I get it. They want to avoid sending emails to an address that, from
their perspective, no longer exists. Makes sense, although they could
certainly tell you that you're gonna be unsubscribed when you cancel,
maybe even throw in a list of all the groups you're currently subscribed
to for good measure.&lt;/p&gt;&lt;p&gt;What annoys me, though, is how hard they make it for someone who
doesn't have a Google account to join a Google Group. If you visit a
group and you're not logged in, there's no way for you to join via
the UI. This makes it seem like you can't join unless you create
an account, but that's not the case! You can still join groups by
sending an e-mail to &lt;code&gt;group-name+subscribe@googlegroups.com&lt;/code&gt;. For
example, to join the &lt;code&gt;racket-users&lt;/code&gt; group, you would send an e-mail to
&lt;code&gt;racket-users+subscribe@googlegroups.com&lt;/code&gt;. If the group you want to join
has spaces in its name, replace the spaces with dashes in the e-mail
address.&lt;/p&gt;&lt;p&gt;If you were wondering about how to join a group without a Google
account, now you know! And if you're considering starting a new mailing
list, I urge you to take this factor into consideration and maybe use
something like &lt;a href="https://groups.io/"&gt;groups.io&lt;/a&gt; or &lt;a href="https://lists.sr.ht/"&gt;lists.sr.ht&lt;/a&gt; instead.&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Bye, Bye, Google</title><link>https://defn.io/2019/02/04/bye-bye-google</link><guid>https://defn.io/2019/02/04/bye-bye-google</guid><pubDate>Mon, 4 Feb 2019 09:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;I spent this past weekend de-Google-ifying my life and, despite my
expectations, it wasn't &lt;i&gt;too hard&lt;/i&gt; to do.&lt;/p&gt;&lt;p&gt;I started by moving all of my websites off of Google App Engine and
onto a dedicated box that I had already owned. That was straightforward
enough. Next, I removed any Google Analytics snippets from each of them
and replaced those with my own analytics server that I had built a while
back (it doesn't store any PII, only aggregate information (and very
little of that, too)). Afterwards, I replaced any Google Fonts that I
had been using with system fonts and, finally, I moved the screencasts
for &lt;a href="https://dramatiq.io"&gt;Dramatiq&lt;/a&gt; and &lt;a href="https://moltenframework.com"&gt;molten&lt;/a&gt; from YouTube to &lt;a href="https://peertube.social/accounts/bogdan/videos"&gt;peertube&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Next, it was time for e-mail. I had already been using &lt;a href="http://isync.sourceforge.net/"&gt;isync&lt;/a&gt; to
download all my mail so it was easy for me to switch my &lt;code&gt;@defn.io&lt;/code&gt;
and &lt;code&gt;@cleartype.io&lt;/code&gt; e-mail addresses over to &lt;a href="https://www.fastmail.com/"&gt;FastMail&lt;/a&gt; (who really
deserve their name!). I created my accounts, updated my &lt;code&gt;.mbsyncrc&lt;/code&gt;,
ran a sync and successfully imported all my mail into the new accounts.
So far so good! The problem was my &lt;code&gt;@gmail.com&lt;/code&gt; account that I had
been using since 2005. Most of my internet accounts were tied to that
e-mail address. Thankfully, &lt;a href="https://1password.com/"&gt;1Password&lt;/a&gt; lets you filter your accounts by
username and so I did that and, one by one, I updated all of my internet
accounts to use my &lt;code&gt;@defn.io&lt;/code&gt; e-mail address (and deleted a number of
accounts in the process -- some companies make this process really
annoying (I shit you not, one in particular made me go through about
20 dialogs before it finally let me delete the account)). That was a
time-consuming, but satisfying, process; sorta like spring cleaning. In
the end, I was able to switch about 130 internet accounts to use my new
e-mail address after a few hours of work. There were only 5 instances
where I could neither change my e-mail address or delete the account so,
for all of those, I e-mailed support and I'm currently waiting to hear
back.&lt;/p&gt;&lt;p&gt;I've debated deleting the &lt;code&gt;@gmail.com&lt;/code&gt; e-mail address, but I think it's
wiser to keep it and essentially squat the username lest someone else
take it over and cause me trouble down the line. I've made it forward
and delete any new mail it gets to &lt;code&gt;@defn.io&lt;/code&gt;. As time goes on, the
amount of incoming email to that address should tend toward 0.&lt;/p&gt;&lt;p&gt;Why go through all this trouble? I've grown increasingly concerned
this past year with how much access Google has to our lives. They are
the world's biggest advertising company and they have access to most
of our web browsing via Google Chrome (62.5% market share -- although
given the amount of broken websites (some explicitly Chrome-only!) I've
found since switching to Firefox, I believe this number may actually
be higher), all our website visitors via Google Analytics and Google
Fonts. Much of our communication via GMail and Google Apps and much of
the content we consume every day via YouTube. I'm not even going to get
into all the information they gather from people who use Android phones.
While I don't believe that folks working at Google are actively trying
to do harm, I believe that, due to the sheer size of the company, no
one is truly at the helm and this massive organism will tend toward
maximizing profits at whatever expense so I've decided to do my best to
support the smaller alternatives that are out there.&lt;/p&gt;&lt;p&gt;P.S. Here's a funny response I got to an account close e-mail from
&lt;a href="https://www.kraken.com/"&gt;Kraken&lt;/a&gt; just as I was writing this post! They're offering me the
privilege to keep my user account and receive their news.&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;We can offer you to keep your account with us instead of closing
it. You'll get the newest updates about us/our services that you
might be interested in.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Aren't I lucky?&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Announcing north</title><link>https://defn.io/2019/01/31/ann-north</link><guid>https://defn.io/2019/01/31/ann-north</guid><pubDate>Thu, 31 Jan 2019 07:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;A couple of days ago, I released &lt;a href="http://docs.racket-lang.org/north/index.html"&gt;north&lt;/a&gt;, a database schema migration
tool written in Racket. It currently supports PostgreSQL and SQLite, but
new adapters are extremely easy to add and my main reason for building
it was because I wanted not only a CLI utility but also programmatic
access to do migrations from Racket. I'm going to make that last part
easy for everyone with the next release after I clean up some of the
internals and write more docs.&lt;/p&gt;&lt;p&gt;If you do check it out, let me know what you think!&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Announcing forms</title><link>https://defn.io/2019/01/21/ann-forms</link><guid>https://defn.io/2019/01/21/ann-forms</guid><pubDate>Mon, 21 Jan 2019 21:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;Today marks the first public release of &lt;a href="http://docs.racket-lang.org/forms/index.html"&gt;forms&lt;/a&gt;, a Racket library for
web form validation. Racket's &lt;a href="https://docs.racket-lang.org/web-server/formlets.html"&gt;formlets&lt;/a&gt; module from the standard
library already does something similar, but, unfortunately, it lacks any
facilities for easily showing validation errors to end users which is a
big part of what I want from this kind of library. Another nice thing
about this new library is it'll be able to validate things other than
forms -- like JSON -- soon!&lt;/p&gt;&lt;p&gt;I hope you check it out, and, if you do, let me know what you think!&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Try Firefox</title><link>https://defn.io/2018/12/17/try-firefox</link><guid>https://defn.io/2018/12/17/try-firefox</guid><pubDate>Mon, 17 Dec 2018 02:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;Since Microsoft officially announced that they will switch Edge's
rendering engine to Chromium, many people have written about how this
poses a danger to the future of the web.  I'm not going to repeat
those same arguments, as I feel &lt;a href="https://adactio.com/journal/14608"&gt;others&lt;/a&gt; have done a good job of it.&lt;/p&gt;&lt;p&gt;What I want to do is urge you to try &lt;a href="https://firefox.com"&gt;Firefox&lt;/a&gt; for a couple of days
this week.  That's it.  Give it a try.  More than the 15 minutes you
gave it that one time.  I bet you you'll find some things are better,
some are worse; but you might realize you like it.  You might also
find that that thing you only ever tested in Chrome doesn't quite work
as it should -- I found out my stock broker only supports Chrome --
and you might fix it and that might prompt you to test things in
multiple browsers in the future and we just might avoid a future where
the web is controlled by an ad-powered super-org.  Or don't.  It's up
to you.&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Advent of Racket 2018</title><link>https://defn.io/2018/12/01/advent-of-racket-2018</link><guid>https://defn.io/2018/12/01/advent-of-racket-2018</guid><pubDate>Sat, 1 Dec 2018 07:45:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;I decided to do this year's &lt;a href="https://adventofcode.com/"&gt;Advent of Code&lt;/a&gt; in &lt;a href="https://racket-lang.org"&gt;Racket&lt;/a&gt; and stream
the whole thing. We'll see how far I make it (getting up this early is
&lt;i&gt;rough&lt;/i&gt;!), but so far I finished day one. The code is &lt;a href="https://github.com/Bogdanp/racket-aoc-2018"&gt;here&lt;/a&gt; and
the playlist for the recordings is &lt;a href="https://www.youtube.com/playlist?list=PLNLLd8yFpp6W2_T-jzcWar4NGVyoNNisX"&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;If you want to get notified as soon as I jump on to start streaming, you
can follow me on &lt;a href="https://www.twitch.tv/popabogdanp"&gt;Twitch&lt;/a&gt;.&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Announcing geoip</title><link>https://defn.io/2018/11/29/ann-geoip</link><guid>https://defn.io/2018/11/29/ann-geoip</guid><pubDate>Thu, 29 Nov 2018 12:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;I released &lt;a href="https://github.com/Bogdanp/racket-geoip"&gt;geoip&lt;/a&gt; today. It's a Racket library for working with
MaxMind's geolocation databases. It's got a tiny API surface (3
functions!), but it should be useful to anyone needing to do geolocation
in Racket. As always, check it out and let me know what you think!&lt;/p&gt;&lt;p&gt;BTW, I streamed the whole process on &lt;a href="https://www.twitch.tv/popabogdanp"&gt;Twitch&lt;/a&gt; so, if that's your thing,
you can check out the recordings &lt;a href="https://www.youtube.com/watch?v=3pNPeRFUm0Q&amp;amp;list=PLNLLd8yFpp6VAswHtM6oUsjFykHEva4-G"&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Announcing net-ip</title><link>https://defn.io/2018/11/16/ann-net-ip</link><guid>https://defn.io/2018/11/16/ann-net-ip</guid><pubDate>Fri, 16 Nov 2018 13:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;I released &lt;a href="http://docs.racket-lang.org/net-ip/index.html"&gt;net-ip&lt;/a&gt; -- a small Racket library for working with IP (v4
and v6) addresses and networks -- today. I needed this to be able to
work on another library I'm going to release at some point in the future
for doing geo-location based on Maxmind's databases. Check it out and
let me know what you think!&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Announcing component</title><link>https://defn.io/2018/11/14/ann-component</link><guid>https://defn.io/2018/11/14/ann-component</guid><pubDate>Wed, 14 Nov 2018 21:10:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;I released &lt;a href="http://docs.racket-lang.org/component/index.html"&gt;component&lt;/a&gt; the other day. It's a Racket library for managing
the lifecycle of stateful objects in long-running applications and for
doing dependency injection. It was inspired by the Clojure library of
the same name by Stuart Sierra. Check it out and let me know what you
think!&lt;/p&gt;&lt;p&gt;P.S. Expect more Racket libraries from me in the coming weeks and
months. I'm really enjoying the language so far!&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Announcing cursive_re</title><link>https://defn.io/2018/10/22/ann-cursive-re</link><guid>https://defn.io/2018/10/22/ann-cursive-re</guid><pubDate>Mon, 22 Oct 2018 12:04:55 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;I released &lt;a href="https://github.com/Bogdanp/cursive_re"&gt;cursive_re&lt;/a&gt; today. It's a &lt;i&gt;tiny&lt;/i&gt; Python library made up of
combinators that help you write regular expressions you can read and
modify six months down the line.&lt;/p&gt;&lt;p&gt;Here's what it looks like:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;&amp;gt;&amp;gt;&amp;gt; from cursive_re import *
&amp;gt;&amp;gt;&amp;gt; domain_name = one_or_more(any_of(in_range("a", "z") + in_range("0", "9") + text("-")))
&amp;gt;&amp;gt;&amp;gt; domain = domain_name + zero_or_more(text(".") + domain_name)
&amp;gt;&amp;gt;&amp;gt; path_segment = zero_or_more(none_of("/"))
&amp;gt;&amp;gt;&amp;gt; path = zero_or_more(text("/") + path_segment)
&amp;gt;&amp;gt;&amp;gt; url = (
...     group(one_or_more(any_of(in_range("a", "z"))), name="scheme") + text("://") +
...     group(domain, name="domain") +
...     group(path, name="path")
... )
&amp;gt;&amp;gt;&amp;gt; str(url)
"(?P&amp;lt;scheme&amp;gt;[a-z]+)://(?P&amp;lt;domain&amp;gt;[a-z0-9\-]+(?:\.[a-z0-9\-]+)*)(?P&amp;lt;path&amp;gt;(?:/[^/]*)*)"
&lt;/code&gt;&lt;/pre&gt;&lt;/article&gt;</description></item><item><title>Racket</title><link>https://defn.io/2018/10/21/racket</link><guid>https://defn.io/2018/10/21/racket</guid><pubDate>Sun, 21 Oct 2018 20:00:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;I've been playing around with &lt;a href="https://racket-lang.org"&gt;Racket&lt;/a&gt; every chance I got since early
September of this year.  This post is going to serve as a sort of
experience report of my foray into Racket so far.&lt;/p&gt;&lt;h2&gt;Things I Like&lt;/h2&gt;&lt;h3&gt;Editor Support&lt;/h3&gt;&lt;p&gt;&lt;a href="http://greghendershott.com/"&gt;Greg Hendershott&lt;/a&gt;'s &lt;a href="https://github.com/greghendershott/racket-mode"&gt;racket-mode&lt;/a&gt; for emacs has been wonderful to
work with.  It provides syntax highlighting, REPL integration and
auto-completion as well as a macro stepper.  All of which are highly
useful while working on Racket code.&lt;/p&gt;&lt;h3&gt;Documentation&lt;/h3&gt;&lt;p&gt;There seems to be a huge emphasis on documentation within the
community, which is great.  Everything is thoroughly documented and
the documentation for installed packages is all built locally (with
cross-references!) so you have access to it regardless of
connectivity.  And it's pretty!  The Racket ecosystem has some of the
most readable websites I've ever been to.&lt;/p&gt;&lt;h3&gt;Consistency&lt;/h3&gt;&lt;p&gt;The language is remarkably consistent, from the way modules and
packages are organized to the way documentation is written.&lt;/p&gt;&lt;h3&gt;Interactive Development&lt;/h3&gt;&lt;p&gt;Despite primarily working with dynamic languages every day, I find
that I don't use the REPL with those languages nearly as much or as
tightly as I do with lisps.  I don't know for sure why that is but it
probably boils down to the nature of s-expressions: it's just &lt;i&gt;so
convenient&lt;/i&gt; to &lt;code&gt;eval-last-sexp&lt;/code&gt; in lisps in a way that you can only
approximate in a language like Python (and, believe me, I've tried!).&lt;/p&gt;&lt;h3&gt;Contracts&lt;/h3&gt;&lt;p&gt;&lt;a href="https://docs.racket-lang.org/reference/contracts.html"&gt;Contracts&lt;/a&gt; are a way to specify and validate the boundaries between
parts of a system.  They are highly expressive (eg. &lt;code&gt;in-range?&lt;/code&gt;) and
composable and produce great error messages when the invariants they
describe get broken.  Unfortunately, they do incur a runtime cost and
there's no way to disable them automatically for production, but they
can be specified separately from the implementation of the things they
describe such that you can add them to the functions and structures
that your module exports and even have "unsafe" flavors of your
modules from which you export versions of the same functions and
structs without contracts for those times when performance really is
an issue.&lt;/p&gt;&lt;p&gt;For a cool-but-unrelated-to-Racket talk on contracts, check out
&lt;a href="https://www.youtube.com/watch?v=lNITrPhl2_A"&gt;"Contracts For Getting More Programs Less Wrong"&lt;/a&gt; by Rob Simmons from
this year's Strange Loop.&lt;/p&gt;&lt;h3&gt;Modules&lt;/h3&gt;&lt;p&gt;I really like the fact that you have to be explicit about what things
your modules export.  This reminds me a lot of ML-style languages and
is great for encapsulation.&lt;/p&gt;&lt;h2&gt;Things I Dislike&lt;/h2&gt;&lt;p&gt;"Dislike" may be too strong a word for what I'm about to describe.
Most of these things simply represent different tradeoffs to what I'm
normally used to.&lt;/p&gt;&lt;h3&gt;Error Reporting&lt;/h3&gt;&lt;p&gt;This may just be because I'm not used to the error messages yet, but I
find Racket's exception reporting hard to decipher and I often run
into errors that contain no information regarding &lt;i&gt;where&lt;/i&gt; in the
source code the error occurred.&lt;/p&gt;&lt;h3&gt;Lack of Docstrings&lt;/h3&gt;&lt;p&gt;There's no built-in support for function docstrings and most of the
code I've read seems to separate code and documentation.  On the one
hand this makes it so looking up a function's documentation via the
REPL is not possible which is slightly annoying (though racket-mode
provides &lt;code&gt;racket-describe&lt;/code&gt; for this purpose), but, on the other hand,
I like that there is a single documentation format that everyone
agrees on (&lt;a href="https://docs.racket-lang.org/scribble/"&gt;Scribble&lt;/a&gt;) and the prevalence of cross-references makes
looking up a function's documentation easy enough.&lt;/p&gt;&lt;h3&gt;Unit Testing&lt;/h3&gt;&lt;p&gt;Racket's &lt;a href="https://docs.racket-lang.org/rackunit/"&gt;rackunit&lt;/a&gt; is lacking support for a few things I'd expect
from such a library.  There doesn't seem to be any built-in way to do
set up and teardown (I've been using &lt;code&gt;dynamic-wind&lt;/code&gt; for this purpose),
the facilities for grouping tests together feel flimsy and I don't
understand why &lt;code&gt;test-suite&lt;/code&gt;s have to be run manually.&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Prometheus with Molten</title><link>https://defn.io/2018/10/21/prometheus-with-molten</link><guid>https://defn.io/2018/10/21/prometheus-with-molten</guid><pubDate>Sun, 21 Oct 2018 03:00:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;&lt;a href="https://moltenframework.com"&gt;molten&lt;/a&gt; has built-in support for exporting the following &lt;a href="https://prometheus.io/"&gt;Prometheus&lt;/a&gt;
metrics:&lt;/p&gt;&lt;ul class="tight"&gt;&lt;li&gt;&lt;code&gt;http_request_duration_seconds{method,path}&lt;/code&gt; -- a histogram of the
request duration percentiles by request method and path,&lt;/li&gt;&lt;li&gt;&lt;code&gt;http_requests_total{method,path,status}&lt;/code&gt; -- a counter of the total
number of requests by method, path and status,&lt;/li&gt;&lt;li&gt;and &lt;code&gt;http_requests_inprogress{method,path}&lt;/code&gt; -- a gauge of the number of
requests in progress by method and path.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Let's say that you have a basic molten app.  Something like this:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;from molten import App, Route


def index():
    return {}

app = App(
    routes=[
        Route("/", index),
    ],
)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To start tracking metrics, add &lt;code&gt;prometheus_middleware&lt;/code&gt; to your app's
&lt;code&gt;middlewares&lt;/code&gt; list:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;from molten import App, ResponseRendererMiddleware, Route
from molten.contrib.prometheus import prometheus_middleware


def index():
    return {}

app = App(
    middleware=[
        prometheus_middleware,
        ResponseRendererMiddleware()
    ],
    routes=[
        Route("/", index),
    ],
)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The app will then begin keeping track of metrics in memory, but won't
expose them anywhere.  Prometheus uses a pull-based model to gather
metrics from servers, which means you have tell it where (i.e. which
servers/ports) to look for metrics and your server needs to be able to
expose those metrics in a way that Prometheus can understand.&lt;/p&gt;&lt;p&gt;To start exposing metrics, add &lt;code&gt;expose_metrics&lt;/code&gt; to your routes:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;from molten import App, ResponseRendererMiddleware, Route
from molten.contrib.prometheus import expose_metrics, prometheus_middleware


def index():
    return {}

app = App(
    middleware=[
        prometheus_middleware,
        ResponseRendererMiddleware()
    ],
    routes=[
        Route("/", index),
        Route("/metrics", expose_metrics),
    ],
)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you run your app now and visit the index handler a couple of times
and then visit &lt;code&gt;/metrics&lt;/code&gt;, you should see all the metrics that were
gathered up until that point.&lt;/p&gt;&lt;p&gt;One caveat related to &lt;code&gt;expose_metrics&lt;/code&gt; is it should only be used in
single-process configurations.  If your application uses multiple
processes to server requests (for example, multiple &lt;code&gt;gunicorn&lt;/code&gt;
workers) then you should use &lt;code&gt;expose_metrics_multiprocess&lt;/code&gt; instead.&lt;/p&gt;&lt;p&gt;Assuming you're running the app on &lt;code&gt;localhost:8000&lt;/code&gt;, you can then use
a &lt;a href="https://prometheus.io/docs/prometheus/latest/configuration/configuration/#%3Cscrape_config%3E"&gt;scrape config&lt;/a&gt; such as this one to let Prometheus know where to
look for metrics:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-yaml"&gt;scrape_configs:
  - job_name: 'a-molten-app'
    scrape_interval: 15s
    static_configs:
      - targets: ['localhost:8000']
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Save that to a file called &lt;code&gt;prometheus.yml&lt;/code&gt;, run prometheus and then
visit http://localhost:9090 and you should be able to start querying
the metrics I mentioned above.  Check out Prometheus' &lt;a href="https://prometheus.io/docs/prometheus/latest/querying/basics/"&gt;querying&lt;/a&gt;
documentation to find out how to construct queries.&lt;/p&gt;&lt;p&gt;While Prometheus' dashboard is nice for inspecting metrics, it's not
meant for long-lived graphs and dashboards.  For that, you should take
a look at &lt;a href="https://prometheus.io/docs/visualization/grafana/"&gt;Grafana&lt;/a&gt;.&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Announcing Molten</title><link>https://defn.io/2018/06/23/ann-molten</link><guid>https://defn.io/2018/06/23/ann-molten</guid><pubDate>Sat, 23 Jun 2018 03:00:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;Today marks the first public release of &lt;a href="https://moltenframework.com"&gt;molten&lt;/a&gt;, a modern API framework
for Python I've been working on over the past couple of weeks.&lt;/p&gt;&lt;p&gt;Check it out and let me know what you think!&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Web application from scratch, Part IV</title><link>https://defn.io/2018/05/12/web-app-from-scratch-04</link><guid>https://defn.io/2018/05/12/web-app-from-scratch-04</guid><pubDate>Sat, 12 May 2018 03:00:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;This is the fourth post in my web app from scratch series. If you
haven't read them yet, you should check out parts 1 through 3 first!&lt;/p&gt;&lt;p&gt;In this part we're going to cover building an &lt;code&gt;Application&lt;/code&gt;
abstraction.  If you're following along at home, you can find the
source code for part 3 &lt;a href="https://github.com/Bogdanp/web-app-from-scratch/tree/part-03"&gt;here&lt;/a&gt;.  Let's get to it!&lt;/p&gt;&lt;h2&gt;Housekeeping&lt;/h2&gt;&lt;h3&gt;Type checking&lt;/h3&gt;&lt;p&gt;So far we've used type annotations mostly as documentation and haven't
worried about type checking our code.  In commit &lt;a href="https://github.com/Bogdanp/web-app-from-scratch/commit/6fa54c489ae34b95466d40e63f7fb7adc635ea0e"&gt;6fa54c4&lt;/a&gt;, I
introduced &lt;a href="https://mypy.readthedocs.io"&gt;mypy&lt;/a&gt; as a dev dependency and started type-checking the
code using it.  I won't describe the changes I had to make here
because the process was mostly mechanical:&lt;/p&gt;&lt;ol class="tight" start="1"&gt;&lt;li&gt;run &lt;code&gt;mypy server.py&lt;/code&gt;,&lt;/li&gt;&lt;li&gt;make necessary changes,&lt;/li&gt;&lt;li&gt;repeat.&lt;/li&gt;&lt;/ol&gt;&lt;h3&gt;Unit testing&lt;/h3&gt;&lt;p&gt;In commit &lt;a href="https://github.com/Bogdanp/web-app-from-scratch/commit/4455e04"&gt;4455e04&lt;/a&gt;, I moved the modules from the repo root into a package
called &lt;code&gt;scratch&lt;/code&gt; and added a &lt;code&gt;tests&lt;/code&gt; package at the top level.&lt;/p&gt;&lt;h2&gt;The Application&lt;/h2&gt;&lt;p&gt;Last time, we added support for mounting request handlers to specific
paths and today we're going to build upon that work by writing an
abstraction that can hold multiple request handlers and route to them
based on the request path.&lt;/p&gt;&lt;p&gt;In &lt;code&gt;scratch/application.py&lt;/code&gt; add the following:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;from .request import Request
from .response import Response


class Application:
    def __call__(self, request: Request) -&amp;gt; Response:
        return Response("501 Not Implemented", content="Not Implemented")
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you remember from last time, we defined request handlers as
functions that take a &lt;code&gt;Request&lt;/code&gt; and return a &lt;code&gt;Response&lt;/code&gt;.  This means
that our &lt;code&gt;Application&lt;/code&gt;s are themselves going to be request handlers.&lt;/p&gt;&lt;p&gt;Let's remove the old server instantiation code from
&lt;code&gt;scratch/server.py&lt;/code&gt; -- delete everything from &lt;code&gt;wrap_auth&lt;/code&gt; onward --
and create a new CLI entrypoint for our package in &lt;code&gt;__main__.py&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;import sys

from .application import Application
from .server import HTTPServer


def main() -&amp;gt; int:
    application = Application()

    server = HTTPServer()
    server.mount(application)
    server.serve_forever()
    return 0


if __name__ == "__main__":
    sys.exit(main())
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We can now run our server from the repo root with &lt;code&gt;python -m scratch&lt;/code&gt;
and if we try to visit &lt;code&gt;http://127.0.0.1:9000&lt;/code&gt;, we should get a 501
response back.&lt;/p&gt;&lt;h3&gt;The Router&lt;/h3&gt;&lt;p&gt;Each application is going to contain an instance of a &lt;code&gt;Router&lt;/code&gt;.  The
router's responsibility will be to map incoming request method, path
pairs like &lt;code&gt;POST /users&lt;/code&gt; or &lt;code&gt;GET /users/{user_id}&lt;/code&gt; to request
handlers.  Here's what it looks like (in &lt;code&gt;scratch/application.py&lt;/code&gt;):&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;import re
from collections import OrderedDict, defaultdict
from functools import partial
from typing import Callable, Dict, Optional, Pattern, Set, Tuple

from .request import Request
from .response import Response
from .server import HandlerT

RouteT = Tuple[Pattern[str], HandlerT]
RoutesT = Dict[str, Dict[str, RouteT]]
RouteHandlerT = Callable[..., Response]


class Router:
    def __init__(self) -&amp;gt; None:
        self.routes_by_method: RoutesT = defaultdict(OrderedDict)
        self.route_names: Set[str] = set()

    def add_route(self, name: str, method: str, path: str, handler: RouteHandlerT) -&amp;gt; None:
        assert path.startswith("/"), "paths must start with '/'"
        if name in self.route_names:
            raise ValueError(f"A route named {name} already exists.")

        route_template = ""
        for segment in path.split("/")[1:]:
            if segment.startswith("{") and segment.endswith("}"):
                segment_name = segment[1:-1]
                route_template += f"/(?P&amp;lt;{segment_name}&amp;gt;[^/]+)"
            else:
                route_template += f"/{segment}"

        route_re = re.compile(f"^{route_template}$")
        self.routes_by_method[method][name] = route_re, handler
        self.route_names.add(name)

    def lookup(self, method: str, path: str) -&amp;gt; Optional[HandlerT]:
        for route_re, handler in self.routes_by_method[method].values():
            match = route_re.match(path)
            if match is not None:
                params = match.groupdict()
                return partial(handler, **params)
        return None
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Its &lt;code&gt;add_route&lt;/code&gt; method iterates over all the parts of the path it's
given and generates a regular expression in the process, replacing all
dynamic path segments with named capture groups in the regex
(&lt;code&gt;"/users/{user_id}"&lt;/code&gt; becomes &lt;code&gt;"^/users/(?P&amp;lt;user_id&amp;gt;[^/]+)$"&lt;/code&gt;).&lt;/p&gt;&lt;p&gt;When a path is looked up via its &lt;code&gt;lookup&lt;/code&gt; method, it iterates over all
of the available routes for that method and checks the path against
the regex for matches.  When it finds a match, it partially applies
the dynamic capture groups (if any) to the handler function and then
returns that value.&lt;/p&gt;&lt;p&gt;Hooking the router into our application class is pretty
straightforward.  We instantiate a router on application init, add a
method to proxy adding routes and update our &lt;code&gt;__call__&lt;/code&gt; method to look
up and execute handlers when a request comes in:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;class Application:
    def __init__(self) -&amp;gt; None:
        self.router = Router()

    def add_route(self, method: str, path: str, handler: RouteHandlerT, name: Optional[str] = None) -&amp;gt; None:
        self.router.add_route(method, path, handler, name or handler.__name__)

    def __call__(self, request: Request) -&amp;gt; Response:
        handler = self.router.lookup(request.method, request.path)
        if handler is None:
            return Response("404 Not Found", content="Not Found")
        return handler(request)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As an added bit of sugar, we're also going to define a &lt;code&gt;route&lt;/code&gt;
decorator on the &lt;code&gt;Application&lt;/code&gt; class:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;    def route(
            self,
            path: str,
            method: str = "GET",
            name: Optional[str] = None,
    ) -&amp;gt; Callable[[RouteHandlerT], RouteHandlerT]:
        def decorator(handler: RouteHandlerT) -&amp;gt; RouteHandlerT:
            self.add_route(method, path, handler, name)
            return handler
        return decorator
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;With that all in place, we can go ahead and update our code in
&lt;code&gt;__main__&lt;/code&gt; to register handlers for various routes.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;import functools
import json
import sys
from typing import Callable, Tuple, Union

from .application import Application
from .request import Request
from .response import Response
from .server import HTTPServer

USERS = [
    {"id": 1, "name": "Jim"},
    {"id": 2, "name": "Bruce"},
    {"id": 3, "name": "Dick"},
]


def jsonresponse(handler: Callable[..., Union[dict, Tuple[str, dict]]]) -&amp;gt; Callable[..., Response]:
    @functools.wraps(handler)
    def wrapper(*args, **kwargs):
        result = handler(*args, **kwargs)
        if isinstance(result, tuple):
            status, result = result
        else:
            status, result = "200 OK", result

        response = Response(status=status)
        response.headers.add("content-type", "application/json")
        response.body.write(json.dumps(result).encode())
        return response
    return wrapper


app = Application()


@app.route("/users")
@jsonresponse
def get_users(request: Request) -&amp;gt; dict:
    return {"users": USERS}


@app.route("/users/{user_id}")
@jsonresponse
def get_user(request: Request, user_id: str) -&amp;gt; Union[dict, Tuple[str, dict]]:
    try:
        return {"user": USERS[int(user_id)]}
    except (IndexError, ValueError):
        return "404 Not Found", {"error": "Not found"}


def main() -&amp;gt; int:
    server = HTTPServer()
    server.mount("", app)
    server.serve_forever()
    return 0


if __name__ == "__main__":
    sys.exit(main())
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;jsonresponse&lt;/code&gt; is a little helper decorator that converts handler
results to JSON responses.  Other than that, everything is pretty
straightforward: we create an application instance, register a couple
route handlers and then mount that application inside our server.  And,
with that, we have a little JSON API for listing users.&lt;/p&gt;&lt;h2&gt;Winding down&lt;/h2&gt;&lt;p&gt;That's it for part 4.  Next time we're going to cover extending the
&lt;code&gt;Request&lt;/code&gt; object to parse query strings and cookies as well as to be
able to hold user-defined data per request.  If you'd like to check
out the full source code and follow along, you can find it
&lt;a href="https://github.com/Bogdanp/web-app-from-scratch/tree/part-04"&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;See ya next time!&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Announcing dramatiq version 1.0!</title><link>https://defn.io/2018/03/31/ann-dramatiq-1-0</link><guid>https://defn.io/2018/03/31/ann-dramatiq-1-0</guid><pubDate>Sat, 31 Mar 2018 01:00:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;Today marks the 1.0 release of &lt;a href="https://dramatiq.io"&gt;dramatiq&lt;/a&gt;! With this release, the
project has been re-licensed from AGPL to the much more permissive LGPL.
Check it out and let me know what you think!&lt;/p&gt;&lt;p&gt;&lt;video controls="" src="https://media.defn.io/dramatiq.mp4" width="100%"&gt;&lt;/video&gt;&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Announcing dramatiq_sqs</title><link>https://defn.io/2018/03/27/ann-dramatiq-sqs</link><guid>https://defn.io/2018/03/27/ann-dramatiq-sqs</guid><pubDate>Tue, 27 Mar 2018 01:00:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;I just released &lt;a href="https://github.com/Bogdanp/dramatiq_sqs"&gt;dramatiq_sqs&lt;/a&gt;, an Amazon SQS broker for &lt;a href="https://dramatiq.io"&gt;dramatiq&lt;/a&gt;.
Check it out an let me know what you think!&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Web application from scratch, Part III</title><link>https://defn.io/2018/03/20/web-app-from-scratch-03</link><guid>https://defn.io/2018/03/20/web-app-from-scratch-03</guid><pubDate>Tue, 20 Mar 2018 02:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;This is the third post in my web app from scratch series.  If you
haven't read them yet, you can find the &lt;a href="/2018/02/25/web-app-from-scratch-01"&gt;first part here&lt;/a&gt; and the
&lt;a href="/2018/03/04/web-app-from-scratch-02"&gt;second part here&lt;/a&gt;.  You'll want to read them first.&lt;/p&gt;&lt;p&gt;This part is going to be short and sweet.  We're going to cover
request handlers and middleware.  &lt;a href="https://github.com/Bogdanp/web-app-from-scratch/tree/part-02"&gt;Here's&lt;/a&gt; the source for part 2 so
you can follow along.  Let's get to it!&lt;/p&gt;&lt;h2&gt;Handlers&lt;/h2&gt;&lt;p&gt;Last time we implemented all our request handling logic inside the
&lt;code&gt;HTTPWorker&lt;/code&gt; class.  That's not an appropriate place for application
logic to live in so we need to update that code to run arbitrary
application code that it knows nothing about.  To do this, we're going
to introduce the concept of a request handler.  In our case a request
handler is going to be a function that takes in a &lt;code&gt;Request&lt;/code&gt; object and
returns a &lt;code&gt;Response&lt;/code&gt; object.  Expressed as a type, that looks like
this:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;HandlerT = Callable[[Request], Response]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let's modify our &lt;code&gt;HTTPServer&lt;/code&gt; so that it stores a set of request
handlers, each one assigned to a particular path prefix so that we can
host different applications at different paths.  In &lt;code&gt;HTTPServer&lt;/code&gt;'s
constructor, let's assign an empty list to the &lt;code&gt;handlers&lt;/code&gt; instance
variable.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;class HTTPServer:
    def __init__(self, host="127.0.0.1", port=9000, worker_count=16) -&amp;gt; None:
        self.handlers = []
        ...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next, let's add a method that we can use to add handlers to the
handler list.  Call it &lt;code&gt;mount&lt;/code&gt;.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;    def mount(self, path_prefix: str, handler: HandlerT) -&amp;gt; None:
        """Mount a request handler at a particular path.  Handler
        prefixes are tested in the order that they are added so the
        first match "wins".
        """
        self.handlers.append((path_prefix, handler))
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now we need to update the &lt;code&gt;HTTPWorker&lt;/code&gt; class to take advantage of
these handlers.  We need to make the workers' constructor take the
handlers list as a parameter.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;class HTTPWorker(Thread):
    def __init__(self, connection_queue: Queue, handlers: List[Tuple[str, HandlerT]]) -&amp;gt; None:
        super().__init__(daemon=True)

        self.connection_queue = connection_queue
        self.handlers = handlers
        self.running = False
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And then we need to update the &lt;code&gt;handle_client&lt;/code&gt; method to delegate
request handling to the handler functions.  If none of the handlers
match the current path, then we'll return a 404 and if one of the
handlers raises an exception then we'll return a 500 error to the
client.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;    def handle_client(self, client_sock: socket.socket, client_addr: typing.Tuple[str, int]) -&amp;gt; None:
        with client_sock:
            try:
                request = Request.from_socket(client_sock)
            except Exception:
                LOGGER.warning("Failed to parse request.", exc_info=True)
                response = Response(status="400 Bad Request", content="Bad Request")
                response.send(client_sock)
                return

            # Force clients to send their request bodies on every
            # request rather than making the handlers deal with this.
            if "100-continue" in request.headers.get("expect", ""):
                response = Response(status="100 Continue")
                response.send(client_sock)

            for path_prefix, handler in self.handlers:
                if request.path.startswith(path_prefix):
                    try:
                        request = request._replace(path=request.path[len(path_prefix):])
                        response = handler(request)
                        response.send(client_sock)
                    except Exception as e:
                        LOGGER.exception("Unexpected error from handler %r.", handler)
                        response = Response(status="500 Internal Server Error", content="Internal Error")
                        response.send(client_sock)
                    finally:
                        break
            else:
                response = Response(status="404 Not Found", content="Not Found")
                response.send(client_sock)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Lastly, we have to make sure we pass the handler list to the
&lt;code&gt;HTTPWorker&lt;/code&gt;s when we instantiate them in &lt;code&gt;serve_forever&lt;/code&gt;.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;    def serve_forever(self) -&amp;gt; None:
        workers = []
        for _ in range(self.worker_count):
            worker = HTTPWorker(self.connection_queue, self.handlers)
            worker.start()
            workers.append(worker)

        ...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now, whenever an &lt;code&gt;HTTPWorker&lt;/code&gt; receives a new connection, it'll parse
the request and try to find a request handler to process it with.
Before the request is passed to a request handler, we remove the
prefix from its path property so that request handlers don't have to
be aware of what prefix they're running under.  This'll come in handy
when we write a handler that serves static files.&lt;/p&gt;&lt;p&gt;Since we haven't mounted any request handlers yet, our server will
reply with a 404 to any incoming request.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;~&amp;gt; curl -v 127.0.0.1:9000
* Rebuilt URL to: 127.0.0.1:9000/
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 9000 (#0)
&amp;gt; GET / HTTP/1.1
&amp;gt; Host: 127.0.0.1:9000
&amp;gt; User-Agent: curl/7.54.0
&amp;gt; Accept: */*
&amp;gt;
&amp;lt; HTTP/1.1 404 Not Found
&amp;lt; content-length: 9
&amp;lt;
* Connection #0 to host 127.0.0.1 left intact
Not Found
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let's mount a request handler that always returns the same response.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;def app(request: Request) -&amp;gt; Response:
  return Response(status="200 OK", content="Hello!")


server = HTTPServer()
server.mount("", app)
server.serve_forever()
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Whatever path we visit now, we'll get the same &lt;code&gt;Hello!&lt;/code&gt; response.
Let's mount another handler to serve static files from a local folder.
To do this, we're going to update our old &lt;code&gt;serve_file&lt;/code&gt; function and
turn it into a function that takes the path to some folder on disk and
returns a request handler that can serve files from that folder.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;def serve_static(server_root: str) -&amp;gt; HandlerT:
    """Generate a request handler that serves file off of disk
    relative to server_root.
    """

    def handler(request: Request) -&amp;gt; Response:
        path = request.path
        if request.path == "/":
            path = "/index.html"

        abspath = os.path.normpath(os.path.join(server_root, path.lstrip("/")))
        if not abspath.startswith(server_root):
            return Response(status="404 Not Found", content="Not Found")

        try:
            content_type, encoding = mimetypes.guess_type(abspath)
            if content_type is None:
                content_type = "application/octet-stream"

            if encoding is not None:
                content_type += f"; charset={encoding}"

            body_file = open(abspath, "rb")
            response = Response(status="200 OK", body=body_file)
            response.headers.add("content-type", content_type)
            return response
        except FileNotFoundError:
            return Response(status="404 Not Found", content="Not Found")

    return handler
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally, we're going to call serve static and mount the result under
"/static" before we mount our application handler.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;server = HTTPServer()
server.mount("/static", serve_static("www")),
server.mount("", app)
server.serve_forever()
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;All requests that begin with &lt;code&gt;"/static"&lt;/code&gt; will now be handled by the
generated static file handler and everything else will be handled by
the app handler.&lt;/p&gt;&lt;h2&gt;Middleware&lt;/h2&gt;&lt;p&gt;Given that our request handlers are plain functions that take a
request and return a response, writing middleware -- arbitrary
functionality that can run before or after every request -- is pretty
straightforward: any function that takes a request handler as input
and itself generates a request handler is a middleware.&lt;/p&gt;&lt;p&gt;Here's how we might write a middleware that ensures that all incoming
requests have a valid &lt;code&gt;Authorization&lt;/code&gt; header:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;def wrap_auth(handler: HandlerT) -&amp;gt; HandlerT:
    def auth_handler(request: Request) -&amp;gt; Response:
        authorization = request.headers.get("authorization", "")
        if authorization.startswith("Bearer ") and authorization[len("Bearer "):] == "opensesame":
            return handler(request)
        return Response(status="403 Forbidden", content="Forbidden!")
    return auth_handler
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To use it, we just pass it the app handler and mount the result.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;server = HTTPServer()
server.mount("/static", serve_static("www")),
server.mount("", wrap_auth(app))
server.serve_forever()
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now all requests to the root handler will have to contain an
authorization header with our super secret hard-coded value, otherwise
they'll get back a 403 response.&lt;/p&gt;&lt;h2&gt;Winding down&lt;/h2&gt;&lt;p&gt;That's it for part 3.  In part 4 we're going to cover extracting an
&lt;code&gt;Application&lt;/code&gt; abstraction and implementing request routing.  If you'd
like to check out the full source code and follow along, you can find
it &lt;a href="https://github.com/Bogdanp/web-app-from-scratch/tree/part-03"&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;See ya next time!&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Web application from scratch, Part II</title><link>https://defn.io/2018/03/04/web-app-from-scratch-02</link><guid>https://defn.io/2018/03/04/web-app-from-scratch-02</guid><pubDate>Sun, 4 Mar 2018 02:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;This is the second post in my web app from scratch series. If you
haven't read it yet, you can find the &lt;a href="/2018/02/25/web-app-from-scratch-01"&gt;first part here&lt;/a&gt;. You'll want to
read that one first.&lt;/p&gt;&lt;p&gt;In this part we're going to cover improvements to the &lt;code&gt;Request&lt;/code&gt; data
structure, adding &lt;code&gt;Response&lt;/code&gt; and &lt;code&gt;Server&lt;/code&gt; abstractions and making the
server able to serve concurrent requests.&lt;/p&gt;&lt;h2&gt;Requests&lt;/h2&gt;&lt;p&gt;The &lt;code&gt;Request&lt;/code&gt; class we wrote last time was able to store the request
&lt;code&gt;method&lt;/code&gt;, its &lt;code&gt;path&lt;/code&gt; and its &lt;code&gt;headers&lt;/code&gt;.  Let's first improve it by
making it possible to store multiple values for a single header.  To
do that, we're going to define a class called &lt;code&gt;Headers&lt;/code&gt; that acts as a
mapping from case-insensitive header names to lists of header values.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;from collections import defaultdict


class Headers:
    def __init__(self) -&amp;gt; None:
        self._headers = defaultdict(list)

    def add(self, name: str, value: str) -&amp;gt; None:
        self._headers[name.lower()].append(value)

    def get_all(self, name: str) -&amp;gt; typing.List[str]:
        return self._headers[name.lower()]

    def get(self, name: str, default: typing.Optional[str] = None) -&amp;gt; typing.Optional[str]:
        try:
            return self.get_all(name)[-1]
        except IndexError:
            return default
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Pretty straightforward: each instance of the class has an underlying
dict whose keys are lower-cased header names and whose values are
lists of header values.  If we plug &lt;code&gt;Headers&lt;/code&gt; into our &lt;code&gt;Request&lt;/code&gt; class
now, it should look something like this:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;class Request(typing.NamedTuple):
    method: str
    path: str
    headers: Headers

    @classmethod
    def from_socket(cls, sock: socket.socket) -&amp;gt; "Request":
        """Read and parse the request from a socket object.

        Raises:
          ValueError: When the request cannot be parsed.
        """
        lines = iter_lines(sock)

        try:
            request_line = next(lines).decode("ascii")
        except StopIteration:
            raise ValueError("Request line missing.")

        try:
            method, path, _ = request_line.split(" ")
        except ValueError:
            raise ValueError(f"Malformed request line {request_line!r}.")

        headers = Headers()
        for line in lines:
            try:
                name, _, value = line.decode("ascii").partition(":")
                headers.add(name, value.lstrip())
            except ValueError:
                raise ValueError(f"Malformed header line {line!r}.")

        return cls(method=method.upper(), path=path, headers=headers)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next up, let's make it possible to read request bodies using our
request class.  Since reading the full request body on every request
is potentially wasteful (and an attack vector!), we're going to define
a &lt;code&gt;BodyReader&lt;/code&gt; class that will behave as a read-only file object so
that users of the &lt;code&gt;Request&lt;/code&gt; class can decide when and how much data
they want to read from the request body.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;class BodyReader(io.IOBase):
    def __init__(self, sock: socket.socket, *, buff: bytes = b"", bufsize: int = 16_384) -&amp;gt; None:
        self._sock = sock
        self._buff = buff
        self._bufsize = bufsize

    def readable(self) -&amp;gt; bool:
        return True

    def read(self, n: int) -&amp;gt; bytes:
        """Read up to n number of bytes from the request body.
        """
        while len(self._buff) &amp;lt; n:
            data = self._sock.recv(self._bufsize)
            if not data:
                break

            self._buff += data

        res, self._buff = self._buff[:n], self._buff[n:]
        return res
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;BodyReader&lt;/code&gt; wraps a socket and reads data in &lt;code&gt;bufsize&lt;/code&gt; chunks
into an in-memory buffer.  To understand why its buffer can be
pre-filled (the &lt;code&gt;buff&lt;/code&gt; parameter in the constructor), lets take
another look at the &lt;code&gt;iter_lines&lt;/code&gt; function we defined last time:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;def iter_lines(sock: socket.socket, bufsize: int = 16_384) -&amp;gt; typing.Generator[bytes, None, bytes]:
    """Given a socket, read all the individual CRLF-separated lines
    and yield each one until an empty one is found.  Returns the
    remainder after the empty line.
    """
    buff = b""
    while True:
        data = sock.recv(bufsize)
        if not data:
            return b""

        buff += data
        while True:
            try:
                i = buff.index(b"\r\n")
                line, buff = buff[:i], buff[i + 2:]
                if not line:
                    return buff

                yield line
            except IndexError:
                break
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can see that once it finds the marker for the end of the request
headers (&lt;code&gt;if not line:&lt;/code&gt;), it &lt;i&gt;returns&lt;/i&gt; any additional data that it may
have read.  To give a concrete example, say a request contains 4KiB of
header data and 100KiB of body data, since &lt;code&gt;iter_lines&lt;/code&gt; reads data in
chunks of 16KiB by default, it might read all the header data &lt;i&gt;plus&lt;/i&gt;
an additional 12KiB of body data in one call.  The 4KiB of header data
will be split into lines and yielded by the generator and the
additional data that was read past the request headers will be
returned from the generator.  We'll use the returned data to
pre-populate the &lt;code&gt;RequestReader&lt;/code&gt;'s internal buffer.&lt;/p&gt;&lt;p&gt;We're going to have to change our header parsing logic to use a while
loop instead of a for loop so that we can capture the return value of
the generator.  Once we have the return value, we can construct a body
reader and pass that to the &lt;code&gt;Request&lt;/code&gt; constructor.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;class Request(typing.NamedTuple):
    method: str
    path: str
    headers: Headers
    body: BodyReader

    @classmethod
    def from_socket(cls, sock: socket.socket) -&amp;gt; "Request":
        """Read and parse the request from a socket object.

        Raises:
          ValueError: When the request cannot be parsed.
        """
        lines = iter_lines(sock)

        try:
            request_line = next(lines).decode("ascii")
        except StopIteration:
            raise ValueError("Request line missing.")

        try:
            method, path, _ = request_line.split(" ")
        except ValueError:
            raise ValueError(f"Malformed request line {request_line!r}.")

        headers = Headers()
        buff = b""
        while True:
            try:
                line = next(lines)
            except StopIteration as e:
                # StopIteration.value contains the return value of the generator.
                buff = e.value
                break

            try:
                name, _, value = line.decode("ascii").partition(":")
                headers.add(name, value.lstrip())
            except ValueError:
                raise ValueError(f"Malformed header line {line!r}.")

        body = BodyReader(sock, buff=buff)
        return cls(method=method.upper(), path=path, headers=headers, body=body)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now that we have a body reader, we can update our main server loop to
read and print out incoming request bodies:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;    while True:
        client_sock, client_addr = server_sock.accept()
        print(f"Received connection from {client_addr}...")
        with client_sock:
            try:
                request = Request.from_socket(client_sock)
                try:
                    content_length = int(request.headers.get("content-length", "0"))
                except ValueError:
                    content_length = 0

                if content_length:
                    body = request.body.read(content_length)
                    print("Request body", body)

                if request.method != "GET":
                    client_sock.sendall(METHOD_NOT_ALLOWED_RESPONSE)
                    continue

                serve_file(client_sock, request.path)
            except Exception as e:
                print(f"Failed to parse request: {e}")
                client_sock.sendall(BAD_REQUEST_RESPONSE)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you send the server some body data now, you should see it print the
whole thing to standard out.  Cool!&lt;/p&gt;&lt;p&gt;We're done with the &lt;code&gt;Request&lt;/code&gt; data structure for now.  We'll come back
to it later when we add query string parameter parsing but, for now,
let's move it, &lt;code&gt;BodyReader&lt;/code&gt; and &lt;code&gt;iter_lines&lt;/code&gt; into a module called
&lt;code&gt;request.py&lt;/code&gt;.  We'll also move &lt;code&gt;Headers&lt;/code&gt; into its own module called
&lt;code&gt;headers.py&lt;/code&gt; since the &lt;code&gt;Response&lt;/code&gt; class is also going to use the
&lt;code&gt;Headers&lt;/code&gt; data structure.&lt;/p&gt;&lt;p&gt;&lt;code&gt;headers.py&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;import typing

from collections import defaultdict


class Headers:
    def __init__(self) -&amp;gt; None:
        self._headers = defaultdict(list)

    def add(self, name: str, value: str) -&amp;gt; None:
        self._headers[name.lower()].append(value)

    def get_all(self, name: str) -&amp;gt; typing.List[str]:
        return self._headers[name.lower()]

    def get(self, name: str, default: typing.Optional[str] = None) -&amp;gt; typing.Optional[str]:
        try:
            return self.get_all(name)[-1]
        except IndexError:
            return default
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;request.py&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;import io
import socket
import typing

from collections import defaultdict
from headers import Headers


class BodyReader(io.IOBase):
    def __init__(self, sock: socket.socket, *, buff: bytes = b"", bufsize: int = 16_384) -&amp;gt; None:
        self._sock = sock
        self._buff = buff
        self._bufsize = bufsize

    def readable(self) -&amp;gt; bool:
        return True

    def read(self, n: int) -&amp;gt; bytes:
        """Read up to n number of bytes from the request body.
        """
        while len(self._buff) &amp;lt; n:
            data = self._sock.recv(self._bufsize)
            if not data:
                break

            self._buff += data

        res, self._buff = self._buff[:n], self._buff[n:]
        return res


class Request(typing.NamedTuple):
    method: str
    path: str
    headers: Headers
    body: BodyReader

    @classmethod
    def from_socket(cls, sock: socket.socket) -&amp;gt; "Request":
        """Read and parse the request from a socket object.

        Raises:
          ValueError: When the request cannot be parsed.
        """
        lines = iter_lines(sock)

        try:
            request_line = next(lines).decode("ascii")
        except StopIteration:
            raise ValueError("Request line missing.")

        try:
            method, path, _ = request_line.split(" ")
        except ValueError:
            raise ValueError(f"Malformed request line {request_line!r}.")

        headers = Headers()
        buff = b""
        while True:
            try:
                line = next(lines)
            except StopIteration as e:
                buff = e.value
                break

            try:
                name, _, value = line.decode("ascii").partition(":")
                headers.add(name, value.lstrip())
            except ValueError:
                raise ValueError(f"Malformed header line {line!r}.")

        body = BodyReader(sock, buff=buff)
        return cls(method=method.upper(), path=path, headers=headers, body=body)


def iter_lines(sock: socket.socket, bufsize: int = 16_384) -&amp;gt; typing.Generator[bytes, None, bytes]:
    """Given a socket, read all the individual CRLF-separated lines
    and yield each one until an empty one is found.  Returns the
    remainder after the empty line.
    """
    buff = b""
    while True:
        data = sock.recv(bufsize)
        if not data:
            return b""

        buff += data
        while True:
            try:
                i = buff.index(b"\r\n")
                line, buff = buff[:i], buff[i + 2:]
                if not line:
                    return buff

                yield line
            except IndexError:
                break
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;100 Continue&lt;/h3&gt;&lt;p&gt;If you use cURL to send more than 1KiB of data to the server you might
notice it hangs for about a second before it reads all the data.
That's because cURL uses the &lt;code&gt;100 Continue&lt;/code&gt; status code to figure out
if and when it should send large request bodies to the server.&lt;/p&gt;&lt;p&gt;When you make a cURL request with a payload larger than 1KiB, it
automatically sends the server an &lt;code&gt;Expect: 100-continue&lt;/code&gt; header and
waits until either&lt;/p&gt;&lt;ol class="tight" start="1"&gt;&lt;li&gt;it receives a &lt;code&gt;HTTP/1.1 100 Continue&lt;/code&gt; response status line from the
server, at which point it sends the request body to the server, or&lt;/li&gt;&lt;li&gt;it receives some other response status line from the server, in
which case it doesn't send the request body to the server at all, or&lt;/li&gt;&lt;li&gt;its 1 second timeout lapses with the server having not sent it any
data, at which point it sends the request body to the server.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;This mechanism lets clients pause the request until the server
determines whether or not it wants to process it.  Our server will
accept all requests for now so we'll just make it send the client a
&lt;code&gt;100 Continue&lt;/code&gt; status every time it gets such an &lt;code&gt;Expect&lt;/code&gt; header:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;    while True:
        client_sock, client_addr = server_sock.accept()
        print(f"Received connection from {client_addr}...")
        with client_sock:
            try:
                request = Request.from_socket(client_sock)
                if "100-continue" in request.headers.get("expect", ""):
                    client_sock.sendall(b"HTTP/1.1 100 Continue\r\n\r\n")

                try:
                    content_length = int(request.headers.get("content-length", "0"))
                except ValueError:
                    content_length = 0

                if content_length:
                    body = request.body.read(content_length)
                    print("Request body", body)

                if request.method != "GET":
                    client_sock.sendall(METHOD_NOT_ALLOWED_RESPONSE)
                    continue

                serve_file(client_sock, request.path)
            except Exception as e:
                print(f"Failed to parse request: {e}")
                client_sock.sendall(BAD_REQUEST_RESPONSE)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Send the server more than 1KiB of data using cURL now.  It should be
much faster than it was before.&lt;/p&gt;&lt;h2&gt;Responses&lt;/h2&gt;&lt;p&gt;So far all the responses we've returned have been "hand-written".
It's time for us to write a &lt;code&gt;Response&lt;/code&gt; abstraction to make that side
of things a little more manageable.&lt;/p&gt;&lt;p&gt;Back in &lt;code&gt;server.py&lt;/code&gt;, let's define our &lt;code&gt;Response&lt;/code&gt; class:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;class Response:
    """An HTTP response.

    Parameters:
      status: The resposne status line (eg. "200 OK").
      headers: The response headers.
      body: A file containing the response body.
      content: A string representing the response body.  If this is
        provided, then body is ignored.
      encoding: An encoding for the content, if provided.
    """

    def __init__(
            self,
            status: str,
            headers: typing.Optional[Headers] = None,
            body: typing.Optional[typing.IO] = None,
            content: typing.Optional[str] = None,
            encoding: str = "utf-8"
    ) -&amp;gt; None:

        self.status = status.encode()
        self.headers = headers or Headers()

        if content is not None:
            self.body = io.BytesIO(content.encode(encoding))
        elif body is None:
            self.body = io.BytesIO()
        else:
            self.body = body

    def send(self, sock: socket.socket) -&amp;gt; None:
        """Write this response to a socket.
        """
        raise NotImplementedError
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We'll keep it relatively simple for now: a response is just a class
that holds the response &lt;code&gt;status&lt;/code&gt;, &lt;code&gt;headers&lt;/code&gt; and a file representing
the response body.  In addition to that, it knows how to write itself
to a socket (rather, it will know, since we haven't implemented that
part yet).  We can use this to rewrite &lt;code&gt;serve_file&lt;/code&gt; and our main loop.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;def serve_file(sock: socket.socket, path: str) -&amp;gt; None:
    """Given a socket and the relative path to a file (relative to
    SERVER_ROOT), send that file to the socket if it exists.  If the
    file doesn't exist, send a "404 Not Found" response.
    """
    if path == "/":
        path = "/index.html"

    abspath = os.path.normpath(os.path.join(SERVER_ROOT, path.lstrip("/")))
    if not abspath.startswith(SERVER_ROOT):
        response = Response(status="404 Not Found", content="Not Found")
        response.send(sock)
        return

    try:
        with open(abspath, "rb") as f:
            content_type, encoding = mimetypes.guess_type(abspath)
            if content_type is None:
                content_type = "application/octet-stream"

            if encoding is not None:
                content_type += f"; charset={encoding}"

            response = Response(status="200 OK", body=f)
            response.headers.add("content-type", content_type)
            response.send(sock)
            return
    except FileNotFoundError:
        response = Response(status="404 Not Found", content="Not Found")
        response.send(sock)
        return


with socket.socket() as server_sock:
    server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_sock.bind((HOST, PORT))
    server_sock.listen(0)
    print(f"Listening on {HOST}:{PORT}...")

    while True:
        client_sock, client_addr = server_sock.accept()
        print(f"Received connection from {client_addr}...")
        with client_sock:
            try:
                request = Request.from_socket(client_sock)
                if "100-continue" in request.headers.get("expect", ""):
                    response = Response(status="100 Continue")
                    response.send(client_sock)

                try:
                    content_length = int(request.headers.get("content-length", "0"))
                except ValueError:
                    content_length = 0

                if content_length:
                    body = request.body.read(content_length)
                    print("Request body", body)

                if request.method != "GET":
                    response = Response(status="405 Method Not Allowed", content="Method Not Allowed")
                    response.send(client_sock)
                    continue

                serve_file(client_sock, request.path)
            except Exception as e:
                print(f"Failed to parse request: {e}")
                response = Response(status="400 Bad Request", content="Bad Request")
                response.send(client_sock)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Before we can implement &lt;code&gt;Response.send&lt;/code&gt;, we have to add a method to
&lt;code&gt;Headers&lt;/code&gt; that'll let us iterate over all the headers.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;HeadersDict = typing.Dict[str, typing.List[str]]
HeadersGenerator = typing.Generator[typing.Tuple[str, str], None, None]


class Headers:
    def __init__(self) -&amp;gt; None:
        self._headers = defaultdict(list)

    def add(self, name: str, value: str) -&amp;gt; None:
        self._headers[name.lower()].append(value)

    def get_all(self, name: str) -&amp;gt; typing.List[str]:
        return self._headers[name.lower()]

    def get(self, name: str, default: typing.Optional[str] = None) -&amp;gt; typing.Optional[str]:
        try:
            return self.get_all(name)[-1]
        except IndexError:
            return default

    def __iter__(self) -&amp;gt; HeadersGenerator:
        for name, values in self._headers.items():
            for value in values:
                yield name, value
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;With this in place, we can now write &lt;code&gt;Response.send&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;    def send(self, sock: socket.socket) -&amp;gt; None:
        """Write this response to a socket.
        """
        content_length = self.headers.get("content-length")
        if content_length is None:
            try:
                body_stat = os.fstat(self.body.fileno())
                content_length = body_stat.st_size
            except OSError:
                self.body.seek(0, os.SEEK_END)
                content_length = self.body.tell()
                self.body.seek(0, os.SEEK_SET)

            if content_length &amp;gt; 0:
                self.headers.add("content-length", content_length)

        headers = b"HTTP/1.1 " + self.status + b"\r\n"
        for header_name, header_value in self.headers:
            headers += f"{header_name}: {header_value}\r\n".encode()

        sock.sendall(headers + b"\r\n")
        if content_length &amp;gt; 0:
            sock.sendfile(self.body)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;send&lt;/code&gt; tries to figure out what the size of the request body is, then
it joins up the status line with the headers and sends them over the
socket.  Finally, it writes the body file to the socket if there is at
least one byte in it.&lt;/p&gt;&lt;p&gt;Python's &lt;code&gt;socket.sendfile&lt;/code&gt; has the nice property that it figures out
if its parameter is just a regular file or not.  If it is, then it
uses the high-performance &lt;code&gt;sendfile&lt;/code&gt; system call to write the file to
the socket and if it isn't then it falls back to regular &lt;code&gt;send&lt;/code&gt; calls.&lt;/p&gt;&lt;p&gt;We're done with &lt;code&gt;Response&lt;/code&gt; for the time being so let's move it into its
own module, called &lt;code&gt;response.py&lt;/code&gt;.&lt;/p&gt;&lt;h2&gt;Server&lt;/h2&gt;&lt;p&gt;At this point, our server loop in &lt;code&gt;server.py&lt;/code&gt; is pretty short and sweet:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;with socket.socket() as server_sock:
    server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_sock.bind((HOST, PORT))
    server_sock.listen(0)
    print(f"Listening on {HOST}:{PORT}...")

    while True:
        client_sock, client_addr = server_sock.accept()
        print(f"Received connection from {client_addr}...")
        with client_sock:
            try:
                request = Request.from_socket(client_sock)
                if "100-continue" in request.headers.get("expect", ""):
                    response = Response(status="100 Continue")
                    response.send(client_sock)

                try:
                    content_length = int(request.headers.get("content-length", "0"))
                except ValueError:
                    content_length = 0

                if content_length:
                    body = request.body.read(content_length)
                    print("Request body", body)

                if request.method != "GET":
                    response = Response(status="405 Method Not Allowed", content="Method Not Allowed")
                    response.send(client_sock)
                    continue

                serve_file(client_sock, request.path)
            except Exception as e:
                print(f"Failed to parse request: {e}")
                response = Response(status="400 Bad Request", content="Bad Request")
                response.send(client_sock)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let's wrap it in a class called &lt;code&gt;HTTPServer&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;class HTTPServer:
    def __init__(self, host="127.0.0.1", port=9000) -&amp;gt; None:
        self.host = host
        self.port = port

    def serve_forever(self) -&amp;gt; None:
        with socket.socket() as server_sock:
            server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            server_sock.bind((self.host, self.port))
            server_sock.listen(0)
            print(f"Listening on {self.host}:{self.port}...")

            while True:
                client_sock, client_addr = server_sock.accept()
                self.handle_client(client_sock, client_addr)

    def handle_client(self, client_sock: socket.socket, client_addr: typing.Tuple[str, int]) -&amp;gt; None:
        with client_sock:
            try:
                request = Request.from_socket(client_sock)
                if "100-continue" in request.headers.get("expect", ""):
                    response = Response(status="100 Continue")
                    response.send(client_sock)

                try:
                    content_length = int(request.headers.get("content-length", "0"))
                except ValueError:
                    content_length = 0

                if content_length:
                    body = request.body.read(content_length)
                    print("Request body", body)

                if request.method != "GET":
                    response = Response(status="405 Method Not Allowed", content="Method Not Allowed")
                    response.send(client_sock)
                    return

                serve_file(client_sock, request.path)
            except Exception as e:
                print(f"Failed to parse request: {e}")
                response = Response(status="400 Bad Request", content="Bad Request")
                response.send(client_sock)


server = HTTPServer()
server.serve_forever()
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is still the exact same logic, but now it's a little more
reusable.  &lt;code&gt;serve_forever&lt;/code&gt; sets up the server socket and accepts new
connections on a loop, it then sends those connections over to
&lt;code&gt;handle_client&lt;/code&gt; which processes the request and sends a response over
the socket.&lt;/p&gt;&lt;p&gt;Next, let's throw a little bit of concurrency into the mix by defining
an &lt;code&gt;HTTPWorker&lt;/code&gt; class.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;class HTTPWorker(Thread):
    def __init__(self, connection_queue: Queue) -&amp;gt; None:
        super().__init__(daemon=True)

        self.connection_queue = connection_queue
        self.running = False

    def stop(self) -&amp;gt; None:
        self.running = False

    def run(self) -&amp;gt; None:
        self.running = True
        while self.running:
            try:
                client_sock, client_addr = self.connection_queue.get(timeout=1)
            except Empty:
                continue

            try:
                self.handle_client(client_sock, client_addr)
            except Exception:
                print(f"Unhandled error: {e}")
                continue
            finally:
                self.connection_queue.task_done()

    def handle_client(self, client_sock: socket.socket, client_addr: typing.Tuple[str, int]) -&amp;gt; None:
        with client_sock:
            try:
                request = Request.from_socket(client_sock)
                if "100-continue" in request.headers.get("expect", ""):
                    response = Response(status="100 Continue")
                    response.send(client_sock)

                try:
                    content_length = int(request.headers.get("content-length", "0"))
                except ValueError:
                    content_length = 0

                if content_length:
                    body = request.body.read(content_length)
                    print("Request body", body)

                if request.method != "GET":
                    response = Response(status="405 Method Not Allowed", content="Method Not Allowed")
                    response.send(client_sock)
                    return

                serve_file(client_sock, request.path)
            except Exception as e:
                print(f"Failed to parse request: {e}")
                response = Response(status="400 Bad Request", content="Bad Request")
                response.send(client_sock)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;HTTP workers are OS threads that wait for new connections to pop up on
a queue and then act on them.  Let's plug workers into &lt;code&gt;HTTPServer&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;class HTTPServer:
    def __init__(self, host="127.0.0.1", port=9000, worker_count=16) -&amp;gt; None:
        self.host = host
        self.port = port
        self.worker_count = worker_count
        self.worker_backlog = worker_count * 8
        self.connection_queue = Queue(self.worker_backlog)

    def serve_forever(self) -&amp;gt; None:
        workers = []
        for _ in range(self.worker_count):
            worker = HTTPWorker(self.connection_queue)
            worker.start()
            workers.append(worker)

        with socket.socket() as server_sock:
            server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            server_sock.bind((self.host, self.port))
            server_sock.listen(self.worker_backlog)
            print(f"Listening on {self.host}:{self.port}...")

            while True:
                try:
                    self.connection_queue.put(server_sock.accept())
                except KeyboardInterrupt:
                    break

        for worker in workers:
            worker.stop()

        for worker in workers:
            worker.join(timeout=30)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now all the HTTP server class does is it spins up some number of
worker threads, then it sets up the server socket and starts accepting
new connections.  It pushes connections onto the shared connection
queue so that the workers can pick them up and handle them.&lt;/p&gt;&lt;p&gt;Just for fun, here's what running Apache Bench on the server yields
on my machine:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ ab -n 10000 -c 32 http://127.0.0.1:9000/
This is ApacheBench, Version 2.3 &amp;lt;$Revision: 1807734 $&amp;gt;
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:
Server Hostname:        127.0.0.1
Server Port:            9000

Document Path:          /
Document Length:        15 bytes

Concurrency Level:      32
Time taken for tests:   3.320 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      790000 bytes
HTML transferred:       150000 bytes
Requests per second:    3011.73 [#/sec] (mean)
Time per request:       10.625 [ms] (mean)
Time per request:       0.332 [ms] (mean, across all concurrent requests)
Transfer rate:          232.35 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.1      0       2
Processing:     2   11   5.1     10      73
Waiting:        1    9   4.6      8      72
Total:          3   11   5.1     10      73

Percentage of the requests served within a certain time (ms)
  50%     10
  66%     11
  75%     11
  80%     11
  90%     12
  95%     12
  98%     14
  99%     17
 100%     73 (longest request)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;3k requests per second.  Not bad considering this is a threaded web
server implemented in pure Python!&lt;/p&gt;&lt;h2&gt;Winding down&lt;/h2&gt;&lt;p&gt;Whew!  That was a lot of stuff to cover in one post.  I didn't think
you'd make it, but here you are!  That's it for part 2.  In part 3
we're going to cover request handlers and middleware.  If you'd like
to check out the full source code and follow along, you can find it
&lt;a href="https://github.com/Bogdanp/web-app-from-scratch/tree/part-02"&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;See ya next time!&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Web application from scratch, Part I</title><link>https://defn.io/2018/02/25/web-app-from-scratch-01</link><guid>https://defn.io/2018/02/25/web-app-from-scratch-01</guid><pubDate>Sun, 25 Feb 2018 03:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;This is the first in a series of posts in which I'm going to go
through the process of building a web application (and its web server)
from scratch in Python.  For the purposes of this series, I'm going to
solely rely on the Python standard library and I'm going to ignore the
WSGI standard.&lt;/p&gt;&lt;p&gt;Without further ado, let's get to it!&lt;/p&gt;&lt;h2&gt;The web server&lt;/h2&gt;&lt;p&gt;To begin with, we're going to write the HTTP server that will power
our web app.  But first, we need to spend a little time looking into
how the HTTP protocol works.&lt;/p&gt;&lt;h3&gt;How HTTP works&lt;/h3&gt;&lt;p&gt;Simply put, HTTP clients connect to HTTP servers over the network and
send them a string of data representing the request.  The server then
interprets that request and sends the client back a response.  The
entire protocol and the formats of those requests and responses are
described in &lt;a href="https://tools.ietf.org/html/rfc2616"&gt;RFC2616&lt;/a&gt;, but I'm going to informally describe them
below so you don't have to read the whole thing.&lt;/p&gt;&lt;h4&gt;Request format&lt;/h4&gt;&lt;p&gt;Requests are represented by a series of &lt;code&gt;\r\n&lt;/code&gt;-separated lines, the
first of which is called the "request line".  The request line is made
up of an HTTP method, followed by a space, followed by the path of the
file being requested, followed by another space, followed by the HTTP
protocol version the client speaks and, finally, followed by a
carriage return (&lt;code&gt;\r&lt;/code&gt;) and a line feed (&lt;code&gt;\n&lt;/code&gt;) character:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-http"&gt;GET /some-path HTTP/1.1\r\n
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After the request line come zero or more header lines.  Each header
line is made up of the header name, followed by a colon, followed by
an optional value, followed by &lt;code&gt;\r\n&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-http"&gt;Host: example.com\r\n
Accept: text/html\r\n
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The end of the headers section is signaled by an empty line:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-http"&gt;\r\n
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally, the request may contain a "body" -- an arbitrary payload that
is sent to the server with the request.&lt;/p&gt;&lt;p&gt;Putting it all together, here's a simple &lt;code&gt;GET&lt;/code&gt; request:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-http"&gt;GET / HTTP/1.1\r\n
Host: example.com\r\n
Accept: text/html\r\n
\r\n
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;and here's a simple &lt;code&gt;POST&lt;/code&gt; request with a body:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-http"&gt;POST / HTTP/1.1\r\n
Host: example.com\r\n
Accept: application/json\r\n
Content-type: application/json\r\n
Content-length: 2\r\n
\r\n
{}
&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;Response format&lt;/h4&gt;&lt;p&gt;Responses, like requests, are made up of a series of &lt;code&gt;\r\n&lt;/code&gt;-separated
lines.  The first line in the response is called the "status line" and
it is made up of the HTTP protocol version, followed by a space,
followed by the response status code, followed by another space, then
the status code reason, followed by &lt;code&gt;\r\n&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-http"&gt;HTTP/1.1 200 OK\r\n
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After the status line come the response headers, then an empty line
and then an optional response body:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-http"&gt;HTTP/1.1 200 OK\r\n
Content-type: text/html\r\n
Content-length: 15\r\n
\r\n
&amp;lt;h1&amp;gt;Hello!&amp;lt;/h1&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;A simple server&lt;/h3&gt;&lt;p&gt;Based on what we know so far about the protocol, let's write a server
that sends the same response regardless of the incoming request.&lt;/p&gt;&lt;p&gt;To start out, we need to create a socket, bind it to an address and
then start listening for connections.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;import socket

HOST = "127.0.0.1"
PORT = 9000

# By default, socket.socket creates TCP sockets.
with socket.socket() as server_sock:
    # This tells the kernel to reuse sockets that are in `TIME_WAIT` state.
    server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

    # This tells the socket what address to bind to.
    server_sock.bind((HOST, PORT))

    # 0 is the number of pending connections the socket may have before
    # new connections are refused.  Since this server is going to process
    # one connection at a time, we want to refuse any additional connections.
    server_sock.listen(0)
    print(f"Listening on {HOST}:{PORT}...")
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you try to run this code now, it'll print to standard out that it's
listening on &lt;code&gt;127.0.0.1:9000&lt;/code&gt; and then exit.  In order to actually
process incoming connections we need to call the &lt;code&gt;accept&lt;/code&gt; method on
our socket.  Doing so will block the process until a client connects
to our server.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;with socket.socket() as server_sock:
    server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_sock.bind((HOST, PORT))
    server_sock.listen(0)
    print(f"Listening on {HOST}:{PORT}...")

    client_sock, client_addr = server_sock.accept()
    print(f"New connection from {client_addr}.")
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Once we have a socket connection to the client, we can start to
communicate with it.  Using the &lt;code&gt;sendall&lt;/code&gt; method, let's send the
connecting client an example response:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;RESPONSE = b"""\
HTTP/1.1 200 OK
Content-type: text/html
Content-length: 15

&amp;lt;h1&amp;gt;Hello!&amp;lt;/h1&amp;gt;""".replace(b"\n", b"\r\n")

with socket.socket() as server_sock:
    server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_sock.bind((HOST, PORT))
    server_sock.listen(0)
    print(f"Listening on {HOST}:{PORT}...")

    client_sock, client_addr = server_sock.accept()
    print(f"New connection from {client_addr}.")
    with client_sock:
        client_sock.sendall(RESPONSE)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you run the code now and then visit http://127.0.0.1:9000 in your
favourite browser, it should render the string "Hello!".  Unfortunately,
the server will exit after it sends the response so refreshing the
page will fail.  Let's fix that:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;RESPONSE = b"""\
HTTP/1.1 200 OK
Content-type: text/html
Content-length: 15

&amp;lt;h1&amp;gt;Hello!&amp;lt;/h1&amp;gt;""".replace(b"\n", b"\r\n")

with socket.socket() as server_sock:
    server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_sock.bind((HOST, PORT))
    server_sock.listen(0)
    print(f"Listening on {HOST}:{PORT}...")

    while True:
        client_sock, client_addr = server_sock.accept()
        print(f"New connection from {client_addr}.")
        with client_sock:
            client_sock.sendall(RESPONSE)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;At this point we have a web server that can serve a simple HTML web
page on every request, all in about 25 lines of code.  That's not too
bad!&lt;/p&gt;&lt;h3&gt;A file server&lt;/h3&gt;&lt;p&gt;Let's extend the HTTP server so that it can serve files off of disk.&lt;/p&gt;&lt;h4&gt;Request abstraction&lt;/h4&gt;&lt;p&gt;Before we can do that, we have to be able to read and parse incoming
request data from the client.  Since we know that request data is
represented by a series of lines, each separated by &lt;code&gt;\r\n&lt;/code&gt; characters,
let's write a generator function that reads data from a socket and
yields each individual line:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;import typing


def iter_lines(sock: socket.socket, bufsize: int = 16_384) -&amp;gt; typing.Generator[bytes, None, bytes]:
    """Given a socket, read all the individual CRLF-separated lines
    and yield each one until an empty one is found.  Returns the
    remainder after the empty line.
    """
    buff = b""
    while True:
        data = sock.recv(bufsize)
        if not data:
            return b""

        buff += data
        while True:
            try:
                i = buff.index(b"\r\n")
                line, buff = buff[:i], buff[i + 2:]
                if not line:
                    return buff

                yield line
            except IndexError:
                break
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This may look a bit daunting, but essentially what it does is it reads
as much data as it can from the socket (in &lt;code&gt;bufsize&lt;/code&gt; chunks), joins
that data together in a buffer (&lt;code&gt;buff&lt;/code&gt;) and continually splits the
buffer into individual lines, yielding one at a time.  Once it finds
an empty line, it returns the extra data that it read.&lt;/p&gt;&lt;p&gt;Using &lt;code&gt;iter_lines&lt;/code&gt;, we can begin printing the requests we get from our
clients:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;with socket.socket() as server_sock:
    server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_sock.bind((HOST, PORT))
    server_sock.listen(0)
    print(f"Listening on {HOST}:{PORT}...")

    while True:
        client_sock, client_addr = server_sock.accept()
        print(f"New connection from {client_addr}.")
        with client_sock:
            for request_line in iter_lines(client_sock):
                print(request_line)

            client_sock.sendall(RESPONSE)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you run the server now and visit http://127.0.0.1:9000, you should
see something like this in your console:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Received connection from ('127.0.0.1', 62086)...
b'GET / HTTP/1.1'
b'Host: localhost:9000'
b'Connection: keep-alive'
b'Cache-Control: max-age=0'
b'Upgrade-Insecure-Requests: 1'
b'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.167 Safari/537.36'
b'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8'
b'Accept-Encoding: gzip, deflate, br'
b'Accept-Language: en-US,en;q=0.9,ro;q=0.8'
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Pretty neat!  Let's abstract over that data by defining a &lt;code&gt;Request&lt;/code&gt; class:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;import typing


class Request(typing.NamedTuple):
    method: str
    path: str
    headers: typing.Mapping[str, str]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For now, the request class is only going to know about methods, paths
and request headers.  We'll leave parsing query string parameters and
reading request bodies for later.&lt;/p&gt;&lt;p&gt;To encapsulate the logic needed to build up a request, we'll add a
class method to &lt;code&gt;Request&lt;/code&gt; called &lt;code&gt;from_socket&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;class Request(typing.NamedTuple):
    method: str
    path: str
    headers: typing.Mapping[str, str]

    @classmethod
    def from_socket(cls, sock: socket.socket) -&amp;gt; "Request":
        """Read and parse the request from a socket object.

        Raises:
          ValueError: When the request cannot be parsed.
        """
        lines = iter_lines(sock)

        try:
            request_line = next(lines).decode("ascii")
        except StopIteration:
            raise ValueError("Request line missing.")

        try:
            method, path, _ = request_line.split(" ")
        except ValueError:
            raise ValueError(f"Malformed request line {request_line!r}.")

        headers = {}
        for line in lines:
            try:
                name, _, value = line.decode("ascii").partition(":")
                headers[name.lower()] = value.lstrip()
            except ValueError:
                raise ValueError(f"Malformed header line {line!r}.")

        return cls(method=method.upper(), path=path, headers=headers)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It uses the &lt;code&gt;iter_lines&lt;/code&gt; function we defined earlier to read the
request line.  That's where it gets the &lt;code&gt;method&lt;/code&gt; and the &lt;code&gt;path&lt;/code&gt;, then
it reads each individual header line and parses those.  Finally, it
builds the &lt;code&gt;Request&lt;/code&gt; object and returns it.  If we plug that into our
server loop, it should look something like this:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;with socket.socket() as server_sock:
    server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_sock.bind((HOST, PORT))
    server_sock.listen(0)
    print(f"Listening on {HOST}:{PORT}...")

    while True:
        client_sock, client_addr = server_sock.accept()
        print(f"Received connection from {client_addr}...")
        with client_sock:
            request = Request.from_socket(client_sock)
            print(request)
            client_sock.sendall(RESPONSE)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you connect to the server now, you should see lines like this one
get printed out:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;Request(method='GET', path='/', headers={'host': 'localhost:9000', 'user-agent': 'curl/7.54.0', 'accept': '*/*'})
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Because &lt;code&gt;from_socket&lt;/code&gt; can raise an exception under certain
circumstances, the server might crash if given an invalid request
right now.  To simulate this, you can use telnet to connect to the
server and send it some bogus data:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;~&amp;gt; telnet 127.0.0.1 9000
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
hello
Connection closed by foreign host.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Sure enough, the server crashed:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Received connection from ('127.0.0.1', 62404)...
Traceback (most recent call last):
  File "server.py", line 53, in parse
    request_line = next(lines).decode("ascii")
ValueError: not enough values to unpack (expected 3, got 1)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "server.py", line 82, in &amp;lt;module&amp;gt;
    with client_sock:
  File "server.py", line 55, in parse
    raise ValueError("Request line missing.")
ValueError: Malformed request line 'hello'.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To handle these kinds of issues a little more gracefully, let's wrap
the call to &lt;code&gt;from_socket&lt;/code&gt; in a try-except block and send the client a
"400 Bad Request" response when we get a malformed request:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;BAD_REQUEST_RESPONSE = b"""\
HTTP/1.1 400 Bad Request
Content-type: text/plain
Content-length: 11

Bad Request""".replace(b"\n", b"\r\n")

with socket.socket() as server_sock:
    server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_sock.bind((HOST, PORT))
    server_sock.listen(0)
    print(f"Listening on {HOST}:{PORT}...")

    while True:
        client_sock, client_addr = server_sock.accept()
        print(f"Received connection from {client_addr}...")
        with client_sock:
            try:
                request = Request.from_socket(client_sock)
                print(request)
                client_sock.sendall(RESPONSE)
            except Exception as e:
                print(f"Failed to parse request: {e}")
                client_sock.sendall(BAD_REQUEST_RESPONSE)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If we try to break it now, our client will get a response back and the
server will stay up:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;~&amp;gt; telnet 127.0.0.1 9000
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
hello
HTTP/1.1 400 Bad Request
Content-type: text/plain
Content-length: 11

Bad RequestConnection closed by foreign host.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;At this point we're ready to start implementing the file serving part,
but first let's make our default response a "404 Not Found" response:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;NOT_FOUND_RESPONSE = b"""\
HTTP/1.1 404 Not Found
Content-type: text/plain
Content-length: 9

Not Found""".replace(b"\n", b"\r\n")

#...

with socket.socket() as server_sock:
    server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_sock.bind((HOST, PORT))
    server_sock.listen(0)
    print(f"Listening on {HOST}:{PORT}...")

    while True:
        client_sock, client_addr = server_sock.accept()
        print(f"Received connection from {client_addr}...")
        with client_sock:
            try:
                request = Request.from_socket(client_sock)
                print(request)
                client_sock.sendall(NOT_FOUND_RESPONSE)
            except Exception as e:
                print(f"Failed to parse request: {e}")
                client_sock.sendall(BAD_REQUEST_RESPONSE)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Additionally, let's add a "405 Method Not Allowed" response.  We're
going to need it for when we get anything other than a &lt;code&gt;GET&lt;/code&gt; request.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;METHOD_NOT_ALLOWED_RESPONSE = b"""\
HTTP/1.1 405 Method Not Allowed
Content-type: text/plain
Content-length: 17

Method Not Allowed""".replace(b"\n", b"\r\n")
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let's define a &lt;code&gt;SERVER_ROOT&lt;/code&gt; constant to represent where the server
should serve files from and a &lt;code&gt;serve_file&lt;/code&gt; function.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;import mimetypes
import os
import socket
import typing

SERVER_ROOT = os.path.abspath("www")

FILE_RESPONSE_TEMPLATE = """\
HTTP/1.1 200 OK
Content-type: {content_type}
Content-length: {content_length}

""".replace("\n", "\r\n")


def serve_file(sock: socket.socket, path: str) -&amp;gt; None:
    """Given a socket and the relative path to a file (relative to
    SERVER_SOCK), send that file to the socket if it exists.  If the
    file doesn't exist, send a "404 Not Found" response.
    """
    if path == "/":
        path = "/index.html"

    abspath = os.path.normpath(os.path.join(SERVER_ROOT, path.lstrip("/")))
    if not abspath.startswith(SERVER_ROOT):
        sock.sendall(NOT_FOUND_RESPONSE)
        return

    try:
        with open(abspath, "rb") as f:
            stat = os.fstat(f.fileno())
            content_type, encoding = mimetypes.guess_type(abspath)
            if content_type is None:
                content_type = "application/octet-stream"

            if encoding is not None:
                content_type += f"; charset={encoding}"

            response_headers = FILE_RESPONSE_TEMPLATE.format(
                content_type=content_type,
                content_length=stat.st_size,
            ).encode("ascii")

            sock.sendall(response_headers)
            sock.sendfile(f)
    except FileNotFoundError:
        sock.sendall(NOT_FOUND_RESPONSE)
        return
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;serve_file&lt;/code&gt; takes the client socket and a path to a file.  It then
tries to resolve that path to a real file inside of the &lt;code&gt;SERVER_ROOT&lt;/code&gt;,
returning a "not found" response if the file resolves outside of the
server root.  Then it tries to open the file and figure out its mime
type and size (using &lt;code&gt;os.fstat&lt;/code&gt;), then it constructs the response
headers and uses the &lt;code&gt;sendfile&lt;/code&gt; system call to write the file to the
socket.  If it can't find the file on disk, then it sends a "not
found" response.&lt;/p&gt;&lt;p&gt;If we add &lt;code&gt;serve_file&lt;/code&gt; into the mix, our server loop should now look
like this:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;with socket.socket() as server_sock:
    server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_sock.bind((HOST, PORT))
    server_sock.listen(0)
    print(f"Listening on {HOST}:{PORT}...")

    while True:
        client_sock, client_addr = server_sock.accept()
        print(f"Received connection from {client_addr}...")
        with client_sock:
            try:
                request = Request.from_socket(client_sock)
                if request.method != "GET":
                    client_sock.sendall(METHOD_NOT_ALLOWED_RESPONSE)
                    continue

                serve_file(client_sock, request.path)
            except Exception as e:
                print(f"Failed to parse request: {e}")
                client_sock.sendall(BAD_REQUEST_RESPONSE)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you add a file called &lt;code&gt;www/index.html&lt;/code&gt; next to your &lt;code&gt;server.py&lt;/code&gt;
file and visit http://localhost:9000 you should see the contents
of that file.  Cool, eh?&lt;/p&gt;&lt;h3&gt;Winding down&lt;/h3&gt;&lt;p&gt;That's it for part 1.  In part 2 we're going to cover extracting
&lt;code&gt;Server&lt;/code&gt; and &lt;code&gt;Response&lt;/code&gt; abstractions as well as making the server
handle multiple concurrent connections.  If you'd like to check out
the full source code and follow along, you can find it &lt;a href="https://github.com/Bogdanp/web-app-from-scratch/tree/part-01"&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;See ya next time!&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Automatic retries with Celery</title><link>https://defn.io/2018/02/25/celery-retries</link><guid>https://defn.io/2018/02/25/celery-retries</guid><pubDate>Sun, 25 Feb 2018 02:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;One of the things that I think Celery could be doing better out of the
box is to provide support for automatically retrying tasks on failure
(thereby forcing users to write idempotent tasks by default).&lt;/p&gt;&lt;p&gt;Fortunately, you can achieve this using &lt;a href="http://docs.celeryproject.org/en/latest/userguide/signals.html"&gt;signals&lt;/a&gt;, specifically the
&lt;a href="http://docs.celeryproject.org/en/latest/userguide/signals.html#task-failure"&gt;task-failure&lt;/a&gt; signal. All you have to do is connect to it and call the
&lt;code&gt;retry&lt;/code&gt; method on your task:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;import logging

from celery import signals


@signals.task_failure.connect
def retry_task_on_exception(*args, **kwargs):
  task = kwargs.get("sender")
  einfo = kwargs.get("einfo")
  logging.warning("Uncaught exception: %r for task: %s", einfo, task)
  task.retry(countdown=3600)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The above will retry failing tasks once an hour. The task object also
contains information about how many times it's been retried and you can
use that information in order to retry tasks with exponential backoff
and to make them stop after a while. For example:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;@signals.task_failure.connect
def retry_task_on_exception(*args, **kwargs):
  task = kwargs.get("sender")
  einfo = kwargs.get("einfo")
  logging.warning("Uncaught exception: %r for task: %s", einfo, task)

  # Backoffs: 60, 120, 240, 480, 960, 1920, 3600, 3600, ...
  backoff = min(60 * 2 ** task.request.retries, 3600)
  task.retry(countdown=backoff)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Not too bad. One caveat with this approach is you might run into issues
if you have a large volume of failing tasks due to Celery's weak support
for delayed tasks.&lt;/p&gt;&lt;p&gt;Check out &lt;a href="https://dramatiq.io"&gt;dramatiq&lt;/a&gt; if you'd rather not have to worry about this kind
of stuff.&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Dramatiq cron with APScheduler</title><link>https://defn.io/2018/01/11/dramatiq-cron</link><guid>https://defn.io/2018/01/11/dramatiq-cron</guid><pubDate>Thu, 11 Jan 2018 02:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;Here's a quick way you can combine &lt;a href="https://dramatiq.io/"&gt;Dramatiq&lt;/a&gt; and &lt;a href="https://apscheduler.readthedocs.io/en/latest/"&gt;APScheduler&lt;/a&gt; to
automatically schedule tasks to execute at certain times.&lt;/p&gt;&lt;p&gt;Install Dramatiq and APScheduler using &lt;a href="http://pipenv.org/"&gt;pipenv&lt;/a&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;pipenv install dramatiq apscheduler
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next, declare a task and decorate it with &lt;code&gt;@cron&lt;/code&gt;. We'll define the cron
function afterwards. In a module called &lt;code&gt;tasks.py&lt;/code&gt;, add the following
code:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;import dramatiq

from cron import cron
from datetime import datetime


@cron("* * * * *")  # Run once a minute
@dramatiq.actor
def print_current_datetime():
    print(datetime.now())
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then define the decorator in &lt;code&gt;cron.py&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;import importlib

from apscheduler.triggers.cron import CronTrigger

JOBS = []


def cron(crontab):
    """Wrap a Dramatiq actor in a cron schedule.
    """
    trigger = CronTrigger.from_crontab(crontab)

    def decorator(actor):
        module_path = actor.fn.__module__
        func_name = actor.fn.__name__
        JOBS.append((trigger, module_path, func_name))
        return actor

    return decorator
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;JOBS&lt;/code&gt; is where the job definitions are stored. When we run the
scheduler from the command line, we'll iterate over this list and
register jobs based on entries made here.&lt;/p&gt;&lt;p&gt;&lt;code&gt;cron&lt;/code&gt; just builds a cron trigger and adds a job definition to &lt;code&gt;JOBS&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Now that we have all the components in place, we just need a way to run
a scheduler from the command line. In a file called &lt;code&gt;run_cron.py&lt;/code&gt; add
the following:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;import cron
import logging
import signal
import sys
import tasks  # imported for its side-effects

from apscheduler.schedulers.blocking import BlockingScheduler

logging.basicConfig(
    format="[%(asctime)s] [PID %(process)d] [%(threadName)s] [%(name)s] [%(levelname)s] %(message)s",
    level=logging.DEBUG,
)

# Pika is a bit noisy w/ Debug logging so we have to up its level.
logging.getLogger("pika").setLevel(logging.WARNING)


def main():
    scheduler = BlockingScheduler()
    for trigger, module_path, func_name in cron.JOBS:
        job_path = f"{module_path}:{func_name}.send"
        job_name = f"{module_path}.{func_name}"
        scheduler.add_job(job_path, trigger=trigger, name=job_name)

    def shutdown(signum, frame):
        scheduler.shutdown()

    signal.signal(signal.SIGINT, shutdown)
    signal.signal(signal.SIGTERM, shutdown)

    scheduler.start()
    return 0


if __name__ == "__main__":
    sys.exit(main())
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here we set up logging, instantiate a blocking scheduler and register
all the jobs that were declared in &lt;code&gt;tasks.py&lt;/code&gt; -- which is why we import
it in the first place, if we didn't, then the code that registers the
jobs would never run. Finally, we add a signal handler to shut down the
scheduler whenever the process receives a &lt;code&gt;SIGINT&lt;/code&gt; or a &lt;code&gt;SIGTERM&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Run &lt;code&gt;rabbitmq-server&lt;/code&gt; then &lt;code&gt;python run_cron.py&lt;/code&gt; and &lt;code&gt;dramatiq tasks&lt;/code&gt; in
a separate terminal and you're done! You should see your workers print
the current time once a minute.&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Podcast dot init interview</title><link>https://defn.io/2017/12/24/podcast-init-interview</link><guid>https://defn.io/2017/12/24/podcast-init-interview</guid><pubDate>Sun, 24 Dec 2017 02:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;I was interviewed by Tobias Macey of &lt;a href="https://www.podcastinit.com"&gt;Podcast.__init__&lt;/a&gt; a few
days ago. The &lt;a href="https://www.podcastinit.com/dramatiq-with-bogdan-popa-episode-141/"&gt;episode&lt;/a&gt; was just published a few hours ago. Check it
out if you want to hear me ramble about Dramatiq and task queueing in
Python! :-)&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Prometheus metrics and API Star</title><link>https://defn.io/2017/11/25/apistar-prometheus</link><guid>https://defn.io/2017/11/25/apistar-prometheus</guid><pubDate>Sat, 25 Nov 2017 02:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;This past week I started playing with &lt;a href="https://github.com/encode/apistar/"&gt;API Star&lt;/a&gt; and I'm kind of in
love with it right now. Being a new project, its docs are a bit lacking
-- its source code, however, is high quality and easy to understand --
so it took me a little time to figure out a way to automatically track
request and response metrics using Prometheus.&lt;/p&gt;&lt;h2&gt;The Goal&lt;/h2&gt;&lt;p&gt;My goal was to globally track request durations, request counts and the
number of requests in progress at any given time. AFAICT, there are two
major ways to do this with API Star: either write a WSGI middleware and
wrap the API star &lt;code&gt;App&lt;/code&gt; object or leverage a &lt;a href="https://github.com/encode/apistar/#components"&gt;component&lt;/a&gt; along with
&lt;code&gt;BEFORE_REQUEST&lt;/code&gt; and &lt;code&gt;AFTER_REQUEST&lt;/code&gt; hooks. I ended up going with the
latter.&lt;/p&gt;&lt;h2&gt;The Component&lt;/h2&gt;&lt;p&gt;First I defined a &lt;code&gt;Prometheus&lt;/code&gt; component. In the app's lifecycle this is
a singleton (&lt;code&gt;preload=True&lt;/code&gt;) object that can be injected in the before
and after response hooks. It is used to keep track of some thread-local
state (like the start time of each request) and to update the prom
metrics.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;import time

from apistar import http
from http import HTTPStatus
from prometheus_client import Counter, Gauge, Histogram
from threading import local

REQUEST_DURATION = Histogram(
    "http_request_duration_seconds",
    "Time spent processing a request.",
    ["method", "handler"],
)
REQUEST_COUNT = Counter(
    "http_requests_total",
    "Request count by method, handler and response code.",
    ["method", "handler", "code"],
)
REQUESTS_INPROGRESS = Gauge(
    "http_requests_inprogress",
    "Requests in progress by method and handler",
    ["method", "handler"],
)


class Prometheus:
    def __init__(self):
        self.data = local()

    def track_request_start(self, method, handler):
        self.data.start_time = time.monotonic()

        handler_name = f"{handler.__module__}.{handler.__name__}"
        REQUESTS_INPROGRESS.labels(method, handler_name).inc()

    def track_request_end(self, method, handler, ret):
        status = 200
        if isinstance(ret, http.Response):
            status = HTTPStatus(ret.status).value

        handler_name = "&amp;lt;builtin&amp;gt;"
        if handler is not None:
            handler_name = f"{handler.__module__}.{handler.__name__}"
            duration = time.monotonic() - self.data.start_time
            del self.data.start_time
            REQUEST_DURATION.labels(method, handler_name).observe(duration)

        REQUEST_COUNT.labels(method, handler_name, status).inc()
        REQUESTS_INPROGRESS.labels(method, handler_name).dec()
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Thread-local data is stored in the &lt;code&gt;data&lt;/code&gt; property of the singleton and
the &lt;code&gt;track_request_start&lt;/code&gt; and &lt;code&gt;track_request_end&lt;/code&gt; methods are meant to
be called at the beginning and end of each request, respectively.&lt;/p&gt;&lt;h2&gt;The Hooks&lt;/h2&gt;&lt;p&gt;The hooks request the &lt;code&gt;Prometheus&lt;/code&gt; component along with information
about the current request method and handler. All they do is pass that
information along to &lt;code&gt;track_request_start&lt;/code&gt; and &lt;code&gt;track_request_end&lt;/code&gt;.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;def before_request(prometheus: Prometheus,
                   method: http.Method,
                   handler: Handler):
    prometheus.track_request_start(method, handler)


def after_request(prometheus: Prometheus,
                  method: http.Method,
                  handler: Handler,
                  ret: ReturnValue):
    prometheus.track_request_end(method, handler, ret)
    return ret
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;One odd thing I ran into was the fact that for builtin request handlers,
such as the 404 handler, the &lt;code&gt;before_request&lt;/code&gt; hook doesn't get called.
This is why &lt;code&gt;track_request_end&lt;/code&gt; checks whether or not the &lt;code&gt;handler&lt;/code&gt; is
&lt;code&gt;None&lt;/code&gt; before trying to compute the time spent running it. In those
cases, &lt;code&gt;self.data.start_time&lt;/code&gt; is never set because &lt;code&gt;track_request_start&lt;/code&gt;
was never called.&lt;/p&gt;&lt;h2&gt;The Exposition Handler&lt;/h2&gt;&lt;p&gt;One problem I ran into while trying to expose the metrics was the fact
that API Star doesn't seem to provide a plaintext renderer. Defining my
own was straightforward enough:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;from apistar import http
from apistar.renderers import Renderer


class PlaintextRenderer(Renderer):
    def render(self, data: http.ResponseData) -&amp;gt; bytes:
        return data
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally, the exposition handler just calls the prom client's
&lt;code&gt;generate_latest&lt;/code&gt; function and renders it using the &lt;code&gt;PlaintextRenderer&lt;/code&gt;.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;from apistar import Response, annotate
from prometheus_client import CONTENT_TYPE_LATEST


@annotate(renderers=[PlaintextRenderer()])
def expose_metrics():
    return Response(generate_latest(), headers={
        "content-type": CONTENT_TYPE_LATEST,
    })
&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;The App&lt;/h2&gt;&lt;p&gt;To hook it all up, I added all the bits I defined previously to
the appropriate spots in the &lt;code&gt;WSGIApp&lt;/code&gt;'s config, making sure the
&lt;code&gt;before_request&lt;/code&gt; and &lt;code&gt;after_request&lt;/code&gt; hooks were the first and last ones
to be added to the configuration, respectively.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;import prometheus_component

from apistar import Component, Route, hooks
from apistar.frameworks.wsgi import WSGIApp as App

components = [
    Component(prometheus_component.Prometheus, preload=True),
]

routes = [
    Route("/metrics", "GET", prometheus_component.expose_metrics),
]

settings = {
    "BEFORE_REQUEST": [
        prometheus_component.before_request,
        hooks.check_permissions,
    ],
    "AFTER_REQUEST": [
        hooks.render_response,
        prometheus_component.after_request,
    ],
}

app = App(
    components=components,
    routes=routes,
    settings=settings,
)
&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Epilogue&lt;/h2&gt;&lt;p&gt;In the end I packed all of this up in a library so I could reuse the
functionality across apps. You can find it &lt;a href="https://github.com/Bogdanp/apistar_prometheus"&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Like I said at the beginning, API Star has been a joy to use so far and
I can't wait to play around with it some more. I'm likely going to put
it into production soon so expect more content to come about it.&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Building a PDF API with Django and Dramatiq</title><link>https://defn.io/2017/11/12/django-dramatiq-pdf-api</link><guid>https://defn.io/2017/11/12/django-dramatiq-pdf-api</guid><pubDate>Sun, 12 Nov 2017 20:12:43 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;In this post I talk about how you can use &lt;a href="http://djangoproject.com"&gt;Django&lt;/a&gt;,
&lt;a href="https://dramatiq.io"&gt;Dramatiq&lt;/a&gt; and &lt;a href="https://github.com/Bogdanp/h2p"&gt;h2p&lt;/a&gt; to create a simple HTTP API that can turn
any URL into a PDF.&lt;/p&gt;&lt;h2&gt;What is Dramatiq?&lt;/h2&gt;&lt;p&gt;Dramatiq is a distributed task processing library for Python 3 that I've
been working on as an alternative to &lt;a href="http://www.celeryproject.org"&gt;Celery&lt;/a&gt;. Using Dramatiq,
you can transparently run functions in the background across a large
number of machines. In this post I'm going to use it to offload the work
of generating PDFs from the web server onto a background processing
server.&lt;/p&gt;&lt;h3&gt;Why use a task queue?&lt;/h3&gt;&lt;p&gt;Long-running or computationally-intensive tasks in the middle of the
request-response cycle of a web server can severily impact the latency
and throughput of that server. A common pattern to work around this
issue is to use a task queue to offload the parts of the request that
can be done later and in the background off to a different fleet of
servers known as workers. This has other advantages, too: tasks may
easily be retried later in case there's an error and you can run tasks
completely outside of the request-response cycle (eg. using a cron job).&lt;/p&gt;&lt;p&gt;Generating PDFs from web pages is a slow process so we want to take that
out of the request and give the requester a way to poll for the result
of the operation.&lt;/p&gt;&lt;h2&gt;Setup&lt;/h2&gt;&lt;p&gt;First things first, we're going to need a message broker. Dramatiq
currently works with &lt;a href="https://redis.io"&gt;Redis&lt;/a&gt; and &lt;a href="https://www.rabbitmq.com/"&gt;RabbitMQ&lt;/a&gt;, but for this post I'm going
to use RabbitMQ. To install it on macOS, you can run:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-bash"&gt;$ brew install rabbitmq
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Run it with &lt;code&gt;rabbitmq-server&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Next, we're going to create a new virtual environment and, inside
of that environment, use &lt;a href="https://docs.pipenv.org"&gt;pipenv&lt;/a&gt; to install all the prerequisite
libraries:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-bash"&gt;$ pipenv install django djangorestframework django_dramatiq "dramatiq[rabbitmq, watch]" h2p
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;small&gt;&lt;code&gt;django_dramatiq&lt;/code&gt; is a small Django app that makes integrating Dramatiq and Django easy.&lt;/small&gt;&lt;/p&gt;&lt;p&gt;After that's done, we're going to create a Django project called
&lt;code&gt;pdfapi&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-bash"&gt;$ django-admin.py startproject pdfapi .
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally, we need to configure &lt;code&gt;django_dramatiq&lt;/code&gt; to use RabbitMQ. In
&lt;code&gt;pdfapi/settings.py&lt;/code&gt;, add &lt;code&gt;django_dramatiq&lt;/code&gt; and &lt;code&gt;rest_framework&lt;/code&gt; to your
&lt;code&gt;INSTALLED_APPS&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'django_dramatiq',
    'rest_framework',
]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And configure the broker in the same file:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;DRAMATIQ_BROKER = {
    "BROKER": "dramatiq.brokers.rabbitmq.RabbitmqBroker",
    "OPTIONS": {
        "url": "amqp://localhost:5672",
    },
    "MIDDLEWARE": [
        "dramatiq.middleware.Prometheus",
        "dramatiq.middleware.AgeLimit",
        "dramatiq.middleware.TimeLimit",
        "dramatiq.middleware.Retries",
        "django_dramatiq.middleware.AdminMiddleware",
        "django_dramatiq.middleware.DbConnectionsMiddleware",
    ]
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let's run the migrations and then the server to make sure everything's
working so far:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-bash"&gt;$ python manage.py migrate
$ python manage.py runserver
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you visit http://127.0.0.1:8000, you should now see the familiar
"Congratulations on your first Django-powered page" view. Kill the
server and create a new app called &lt;code&gt;pdfs&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-bash"&gt;$ python manage.py startapp pdfs
&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;The API&lt;/h2&gt;&lt;p&gt;The API we're going to define is going to be very simple.  It will
accept &lt;code&gt;POST&lt;/code&gt; requests to &lt;code&gt;/v1/pdfs&lt;/code&gt; containing the &lt;code&gt;url&lt;/code&gt; we're
expected to convert into a PDF, these requests will submit a task to
generate the PDF and immediately return a JSON object with an &lt;code&gt;id&lt;/code&gt; and
a &lt;code&gt;status&lt;/code&gt; field that the caller can then use to keep track of the
job.&lt;/p&gt;&lt;p&gt;Using the &lt;code&gt;id&lt;/code&gt; field from the response, the caller will be able to
poll &lt;code&gt;/v1/pdfs/{id}&lt;/code&gt; to find out what the status of the task is.&lt;/p&gt;&lt;h3&gt;The PDF model&lt;/h3&gt;&lt;p&gt;In &lt;code&gt;pdfs/models.py&lt;/code&gt; declare the following model:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;class PDF(models.Model):
    STATUS_PENDING = "pending"
    STATUS_FAILED = "failed"
    STATUS_DONE = "done"
    STATUSES = [
        (STATUS_PENDING, "Pending"),
        (STATUS_FAILED, "Failed"),
        (STATUS_DONE, "Done"),
    ]

    source_url = models.CharField(max_length=512)
    status = models.CharField(
        max_length=10,
        default=STATUS_PENDING,
        choices=STATUSES,
    )

    @property
    def filename(self):
        raise NotImplementedError

    @property
    def pdf_url(self):
        raise NotImplementedError
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We're going to skip the implementations of the &lt;code&gt;filename&lt;/code&gt; and
&lt;code&gt;pdf_url&lt;/code&gt; properties for now.&lt;/p&gt;&lt;p&gt;Build and run the migrations:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-bash"&gt;$ python manage.py makemigrations
$ python manage.py migrate
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then add a serializer for that model in &lt;code&gt;pdfs/serializers.py&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;from rest_framework import serializers

from .models import PDF


class PDFSerializer(serializers.ModelSerializer):
    source_url = serializers.URLField(max_length=512)
    pdf_url = serializers.URLField(read_only=True)

    class Meta:
        model = PDF
        fields = ("id", "source_url", "pdf_url", "status")
        read_only_fields = ("status",)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We're going to use this serializer to render PDF models as JSON and to
validate incoming requests.&lt;/p&gt;&lt;h3&gt;The Views&lt;/h3&gt;&lt;p&gt;In &lt;code&gt;pdfs/views.py&lt;/code&gt; add the following views:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;from django.views.decorators.csrf import csrf_exempt
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response

from .models import PDF
from .serializers import PDFSerializer


@csrf_exempt
@api_view(["POST"])
def create_pdf(request):
    serializer = PDFSerializer(data=request.data)
    if serializer.is_valid():
        serializer.save()
        return Response(serializer.data)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


@api_view(["GET"])
def view_pdf(request, pk):
    try:
        pdf = PDF.objects.get(pk=pk)
        serializer = PDFSerializer(pdf)
        return Response(serializer.data)
    except PDF.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And then hook them up in &lt;code&gt;pdfs/urls.py&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;from django.conf.urls import url

from . import views

app_name = "pdfs"
urlpatterns = [
    url(r"^$", views.create_pdf, name="create_pdf"),
    url(r"^(?P&amp;lt;pk&amp;gt;\d+)$", views.view_pdf, name="view_pdf"),
]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Add the &lt;code&gt;pdfs&lt;/code&gt; app to &lt;code&gt;pdfapi/settings.py&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'django_dramatiq',
    'rest_framework',

    'pdfs',
]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally, include the &lt;code&gt;pdfs&lt;/code&gt; urls in &lt;code&gt;pdfapi/urls.py&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;from django.conf.urls import url, include
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^v1/pdfs/', include("pdfs.urls")),
]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;At this point if you run the development server and visit &lt;code&gt;/v1/pdfs&lt;/code&gt;
you should be able to interact with the API.&lt;/p&gt;&lt;h2&gt;The Task&lt;/h2&gt;&lt;p&gt;So far we've declared the model and an API that lets us interact with
it, but we haven't done anything to actually generate PDFs so every
PDF we create using the API is going to be in a perpetual &lt;code&gt;pending&lt;/code&gt;
state.  Let's fix that.&lt;/p&gt;&lt;p&gt;In &lt;code&gt;pdfs/tasks.py&lt;/code&gt; add the following task:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;import dramatiq
import h2p

from .models import PDF


@dramatiq.actor
def generate_pdf(pk):
    pdf = PDF.objects.get(pk=pk)

    try:
        h2p.generate_pdf(
            pdf.filename,
            source_uri=pdf.source_url,
        ).result()

        pdf.status = PDF.STATUS_DONE
    except h2p.ConversionError:
        pdf.status = PDF.STATUS_FAILED

    pdf.save()
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Let's break this down a little bit.  &lt;code&gt;generate_pdf&lt;/code&gt; is just a normal
Python function that we've decorated with &lt;code&gt;@dramatiq.actor&lt;/code&gt;.  This
makes it possible to run the function asynchronously.&lt;/p&gt;&lt;p&gt;&lt;code&gt;generate_pdf&lt;/code&gt; takes a &lt;code&gt;pk&lt;/code&gt; parameter representing the id of a &lt;code&gt;PDF&lt;/code&gt;,
this is important because tasks are distributed and we wouldn't want
to send entire &lt;code&gt;PDF&lt;/code&gt; objects over the network.  It delegates the work
of actually creating the PDF to &lt;code&gt;h2p&lt;/code&gt; and updates the &lt;code&gt;PDF&lt;/code&gt; object's
status based on the result of that operation.&lt;/p&gt;&lt;p&gt;We're passing the &lt;code&gt;filename&lt;/code&gt; property of &lt;code&gt;PDF&lt;/code&gt; to &lt;code&gt;h2p.generate_pdf&lt;/code&gt;
but we haven't implemented it yet so let's fill it and &lt;code&gt;pdf_url&lt;/code&gt; in on
the &lt;code&gt;PDF&lt;/code&gt; model in &lt;code&gt;pdfs/models.py&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;    @property
    def filename(self):
        return f"{settings.MEDIA_ROOT}{self.pk}.pdf"

    @property
    def pdf_url(self):
        return f"{settings.MEDIA_URL}{self.pk}.pdf"
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Don't forget to add &lt;code&gt;MEDIA_ROOT&lt;/code&gt; and &lt;code&gt;MEDIA_URL&lt;/code&gt; to
&lt;code&gt;pdfapi/settings.py&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;MEDIA_ROOT = os.path.join(BASE_DIR, "files/")
MEDIA_URL = "/media"
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Create the &lt;code&gt;files&lt;/code&gt; folder and then add a static handler to
&lt;code&gt;pdfapi/urls.py&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;from django.conf import settings
from django.conf.urls import url, include
from django.conf.urls.static import static
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^v1/pdfs/', include("pdfs.urls")),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
&lt;/code&gt;&lt;/pre&gt;&lt;h1&gt;Hooking 'em up&lt;/h1&gt;&lt;p&gt;At this point we've created a task that can generate PDF files and an
API that can submit and keep track of that work.  Let's hook them up!&lt;/p&gt;&lt;p&gt;In the &lt;code&gt;create_pdf&lt;/code&gt; view from &lt;code&gt;pdfs/views.py&lt;/code&gt; change the
&lt;code&gt;serializer.save()&lt;/code&gt; call to:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;    if serializer.is_valid():
        pdf = serializer.save()
        generate_pdf.send(pdf.pk)
        return Response(serializer.data)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now every time someone creates a &lt;code&gt;PDF&lt;/code&gt; object using the API, we'll
enqueue a &lt;code&gt;generate_pdf&lt;/code&gt; task.  Spin up the API server and some
Dramatiq workers and test it out.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-bash"&gt;$ python manage.py runserver
$ python manage.py rundramatiq  # in a separate terminal window
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To test it out, send a create request using curl:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-bash"&gt;$ curl -d"source_url=http://example.com" http://127.0.0.1:8000/v1/pdfs/
{"id":1,"source_url":"http://example.com","pdf_url":"/media/1.pdf","status":"pending"}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then poll using GET requests until it's ready:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-bash"&gt;$ curl http://127.0.0.1:8000/v1/pdfs/1
{"id":1,"source_url":"http://example.com","pdf_url":"/media/1.pdf","status":"done"}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally, visit http://127.0.0.1:8000/media/1.pdf to view the generated PDF.&lt;/p&gt;&lt;h1&gt;Next Steps&lt;/h1&gt;&lt;p&gt;You can find the full code on &lt;a href="https://github.com/Bogdanp/django_dramatiq_pdfapi_example"&gt;GitHub&lt;/a&gt;. If you want to
learn more about Dramatiq (and hopefully you do!) head on to the
&lt;a href="https://dramatiq.io"&gt;docs&lt;/a&gt;. I've put a lot of work into making them as accessible
as possible.&lt;/p&gt;&lt;p&gt;Happy coding!&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Announcing h2p</title><link>https://defn.io/2017/11/05/ann-h2p</link><guid>https://defn.io/2017/11/05/ann-h2p</guid><pubDate>Sun, 5 Nov 2017 02:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;This past week I released &lt;a href="https://github.com/Bogdanp/h2p"&gt;h2p&lt;/a&gt;, a simple python frontend to
&lt;a href="https://wkhtmltopdf.org"&gt;libwkhtmltox&lt;/a&gt; that lets you generate PDF files from web pages
without needing to spawn subprocesses.&lt;/p&gt;&lt;p&gt;You can use pip or pipenv to install it:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;pipenv install h2p
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And the API is straightforward:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-python"&gt;import h2p

websites = ["https://google.com", "https://example.com", "https://defn.io"]

tasks = []
for i, website in enumerate(websites, 1):
   filename = f"output-{i}.pdf"
   tasks.append(h2p.generate_pdf(filename, website))
   print(f"Enqueued task for {website!r} -&amp;gt; {filename!r}.")

# ... do other stuff while your pdfs are being generated ...

for task in tasks:
   task.result()

print("All tasks are done.")
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Each call to &lt;code&gt;generate_pdf&lt;/code&gt; returns an asynchronous task that represents
the action of generating that pdf. Calling &lt;code&gt;result&lt;/code&gt; on that task will
block until it's done.&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Announcing django_dramatiq</title><link>https://defn.io/2017/11/04/ann-django-dramatiq</link><guid>https://defn.io/2017/11/04/ann-django-dramatiq</guid><pubDate>Sat, 4 Nov 2017 23:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;This past week I released &lt;a href="https://github.com/Bogdanp/django_dramatiq"&gt;django_dramatiq&lt;/a&gt;, an app
that helps you integrate &lt;a href="https://github.com/Bogdanp/dramatiq"&gt;Dramatiq&lt;/a&gt; and &lt;a href="http://djangoproject.com/"&gt;Django&lt;/a&gt;.
Additionally, I released these two example repos:&lt;/p&gt;&lt;ul class="tight"&gt;&lt;li&gt;&lt;a href="https://github.com/Bogdanp/django_dramatiq_example"&gt;django_dramatiq_example&lt;/a&gt;, a small Django app that
shows off &lt;code&gt;django_dramatiq&lt;/code&gt;, and&lt;/li&gt;&lt;li&gt;&lt;a href="https://github.com/Bogdanp/flask_dramatiq_example"&gt;flask_dramatiq_example&lt;/a&gt;, a small &lt;a href="http://flask.pocoo.org/"&gt;Flask&lt;/a&gt;
app that shows off how you can integrate Flask and Dramatiq.&lt;/li&gt;&lt;/ul&gt;&lt;/article&gt;</description></item><item><title>Announcing dramatiq</title><link>https://defn.io/2017/06/29/ann-dramatiq</link><guid>https://defn.io/2017/06/29/ann-dramatiq</guid><pubDate>Thu, 29 Jun 2017 01:00:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;Today marks the first public release of &lt;a href="http://dramatiq.io"&gt;Dramatiq&lt;/a&gt;,
a distributed task queueing library for Python with a focus on
reliability, simplicity and performance.&lt;/p&gt;&lt;p&gt;Check it out and let me know what you think!&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Announcing elm-cookiecutter</title><link>https://defn.io/2016/06/24/ann-elm-cookiecutter</link><guid>https://defn.io/2016/06/24/ann-elm-cookiecutter</guid><pubDate>Fri, 24 Jun 2016 03:00:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;Since most of my Elm apps tend to follow a similar structure, I've
decided to create and open source a &lt;a href="https://github.com/audreyr/cookiecutter"&gt;cookiecutter&lt;/a&gt; template that
deals with all of the boilerplate required to set up one of these apps
the way I like it. The repository is available &lt;a href="https://github.com/Bogdanp/elm-cookiecutter"&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;h2&gt;Quickstart&lt;/h2&gt;&lt;p&gt;To get started immediately:&lt;/p&gt;&lt;ol class="tight" start="1"&gt;&lt;li&gt;&lt;code&gt;pip install cookiecutter&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;cookiecutter gh:Bogdanp/elm-cookiecutter&lt;/code&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;You will be prompted for a project name and description.  Once you've
completed the process, &lt;code&gt;cd&lt;/code&gt; into &lt;code&gt;$PROJECT_NAME&lt;/code&gt; and run:&lt;/p&gt;&lt;ol class="tight" start="1"&gt;&lt;li&gt;&lt;code&gt;npm install&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;make serve&lt;/code&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The latter will build your sources, start up a web server and open the
app in a web browser.  Once you've made changes to any of the sources,
run &lt;code&gt;make&lt;/code&gt; again to rebuild or run &lt;code&gt;make watch&lt;/code&gt; in a separate terminal
to have it automatically rebuild the app when any of the sources
change.&lt;/p&gt;&lt;p&gt;To produce a production build, run &lt;code&gt;env NPM_ENV=prod make&lt;/code&gt;.&lt;/p&gt;&lt;h2&gt;Structure&lt;/h2&gt;&lt;p&gt;Some of the more interesting files are described below.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;.
├── Makefile               # Rules for building the app. See `make help`
├── README.md              # The app's README
├── bin
│   └── server.js          # The development server. Points all nonexistent paths to `index.html`
├── build
│   ├── index.html
│   └── js                 # The final product ends up in js/app.js. This includes styles
├── css
│   ├── app.scss           # The entrypoint for styles
│   └── normalize.scss     # A slightly modified Normalize.css
├── elm
│   ├── Main.elm           # The entrypoint for Elm code
│   ├── Model.elm          # Definitions for Flags, the Model and the root Msg. Referenced by Update and View
│   ├── Pages
│   │   └── Dashboard.elm
│   ├── Routes.elm
│   ├── Update.elm
│   ├── Util.elm
│   └── View.elm
├── elm-package.json
├── js
│   └── app.js             # Sets up the Elm app and deals with flags, subscriptions and ports
├── package.json
└── webpack.config.js

7 directories, 17 files
&lt;/code&gt;&lt;/pre&gt;&lt;/article&gt;</description></item><item><title>Announcing elm-datepicker</title><link>https://defn.io/2016/06/01/elm-datepicker-ann</link><guid>https://defn.io/2016/06/01/elm-datepicker-ann</guid><pubDate>Wed, 1 Jun 2016 03:00:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;I found myself needing a reusable date picker component for an app so I
went ahead and built one. You can find the package &lt;a href="http://package.elm-lang.org/packages/Bogdanp/elm-datepicker/latest"&gt;here&lt;/a&gt; and a
demo &lt;a href="http://bogdanp.github.io/elm-datepicker/"&gt;here&lt;/a&gt; (&lt;a href="https://github.com/Bogdanp/elm-datepicker/tree/master/examples/simple"&gt;source&lt;/a&gt;).&lt;/p&gt;&lt;/article&gt;</description></item><item><title>elm-mode refactoring demo</title><link>https://defn.io/2016/02/27/elm-mode-refactoring-demo</link><guid>https://defn.io/2016/02/27/elm-mode-refactoring-demo</guid><pubDate>Sat, 27 Feb 2016 02:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;I recorded a short demo of &lt;a href="https://github.com/jcollard/elm-mode"&gt;elm-mode&lt;/a&gt;'s new refactoring
features. You can view it on &lt;a href="https://www.youtube.com/watch?v=GdzylbPJCtk"&gt;Youtube&lt;/a&gt;.&lt;/p&gt;&lt;/article&gt;</description></item><item><title>Announcing elm-route</title><link>https://defn.io/2016/02/21/ann-elm-route</link><guid>https://defn.io/2016/02/21/ann-elm-route</guid><pubDate>Sun, 21 Feb 2016 02:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;Today marks the first release of &lt;a href="https://github.com/Bogdanp/elm-route"&gt;elm-route&lt;/a&gt;, a type safe
route parsing DSL built on top of &lt;a href="https://github.com/Bogdanp/elm-combine"&gt;elm-combine&lt;/a&gt;. Its main
additions to the world of type safe route parsing are:&lt;/p&gt;&lt;ul class="tight"&gt;&lt;li&gt;A generic DSL for expressing arbitrarily-nested dynamic routes (at
the cost of uglier route constructors as the depth increases).&lt;/li&gt;&lt;li&gt;An automatic way to do reverse routing that when coupled with a
small amount of boilerplate should provide the safest approach to
reverse routing that the Elm language can currently support.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;You can see a working demo &lt;a href="http://bogdanp.github.io/elm-route/"&gt;here&lt;/a&gt; (&lt;a href="https://github.com/Bogdanp/elm-route/tree/master/examples/app"&gt;source&lt;/a&gt;). Note that
direct linking to routes in the demo does not work due to a limitation
of Gihub Pages.&lt;/p&gt;&lt;h2&gt;Related work&lt;/h2&gt;&lt;p&gt;&lt;a href="https://github.com/etaque/elm-route-parser"&gt;elm-route-parser&lt;/a&gt; is another type safe route parsing library. In
contrast to &lt;code&gt;elm-route&lt;/code&gt;, its more rigid matchers make it possible to have
cleaner route constructors (for example, &lt;code&gt;HomeR&lt;/code&gt; instead of &lt;code&gt;HomeR ()&lt;/code&gt;).
It does not yet provide automatic reverse routing support.&lt;/p&gt;&lt;h2&gt;Example&lt;/h2&gt;&lt;p&gt;Here's a short taste of what the DSL looks like:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-haskell"&gt;type Sitemap
  = HomeR ()
  | UsersR ()
  | UserR Int
  | UserPostR (Int, String)

homeR = HomeR := static ""
usersR = UsersR := static "users"
userR = UserR := "users" &amp;lt;//&amp;gt; int
userPostR = UserPostR := "users" &amp;lt;//&amp;gt; int &amp;lt;/&amp;gt; string
sitemap = router [homeR, usersR, userR, userPostR]

match : String -&amp;gt; Maybe Sitemap
match = Route.match sitemap

route : Sitemap -&amp;gt; String
route r =
  case r of
    HomeR () -&amp;gt; reverse homeR []
    UsersR () -&amp;gt; reverse usersR []
    UserR id -&amp;gt; reverse userR [toString id]
    UserPostR (uid, pid) -&amp;gt; reverse userPostR [toString uid, pid]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For more check out the &lt;a href="https://github.com/Bogdanp/elm-route#example"&gt;README&lt;/a&gt; and the &lt;a href="https://github.com/Bogdanp/elm-route/tree/master/examples"&gt;examples&lt;/a&gt; folder.&lt;/p&gt;&lt;/article&gt;</description></item><item><title>elm-mode TAGS demo</title><link>https://defn.io/2016/01/17/elm-mode-tags-demo</link><guid>https://defn.io/2016/01/17/elm-mode-tags-demo</guid><pubDate>Sun, 17 Jan 2016 02:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;I recorded a short demo of &lt;a href="https://github.com/jcollard/elm-mode"&gt;elm-mode&lt;/a&gt;'s new automatic TAGS
file generation feature. You can view it on &lt;a href="https://www.youtube.com/watch?v=TSZJBLNCv4Q"&gt;Youtube&lt;/a&gt;.&lt;/p&gt;&lt;/article&gt;</description></item><item><title>elm-mode demo</title><link>https://defn.io/2016/01/13/elm-mode-demo</link><guid>https://defn.io/2016/01/13/elm-mode-demo</guid><pubDate>Wed, 13 Jan 2016 02:00:00 +0200</pubDate><description>&lt;article&gt;&lt;p&gt;I recorded a short screencast showing off some of the features available
in &lt;a href="https://github.com/jcollard/elm-mode"&gt;elm-mode&lt;/a&gt;. You can view it on &lt;a href="https://www.youtube.com/watch?v=wZ9uvU0lZ-E"&gt;Youtube&lt;/a&gt;.&lt;/p&gt;&lt;/article&gt;</description></item><item><title>ido-mode</title><link>https://defn.io/2015/10/12/ido-mode</link><guid>https://defn.io/2015/10/12/ido-mode</guid><pubDate>Mon, 12 Oct 2015 03:00:00 +0300</pubDate><description>&lt;article&gt;&lt;p&gt;Ido mode is one of those Emacs packages that you can't imagine living
without once you embed it into your workflow. It stands for "interactive
do" and in this post I'm going to talk about what it does, some of the
configuration options that come bundled with it and how you can enhance
it further.&lt;/p&gt;&lt;h2&gt;What it does&lt;/h2&gt;&lt;p&gt;Ido extends the built-in &lt;code&gt;find-file&lt;/code&gt; and &lt;code&gt;switch-to-buffer&lt;/code&gt;
commands with interactive completion. It displays all the items
that are available to you based on the current selection, narrowing
down to the most relevant ones as you type to expand said
selection. You can find a brief demonstration of this functionality
&lt;a href="https://asciinema.org/a/27740"&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Turn it on by calling &lt;code&gt;ido-mode&lt;/code&gt;.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-emacs-lisp"&gt;(require 'ido)
(ido-mode)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Calling &lt;code&gt;ido-everywhere&lt;/code&gt; will ensure that ido is used for all buffer and
file selections in Emacs.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-emacs-lisp"&gt;(ido-everywhere)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If, for some reason, you'd prefer to only apply ido to one of those
commands but not the other you can do so by calling &lt;code&gt;ido-mode&lt;/code&gt; with a
&lt;code&gt;​'buffer&lt;/code&gt; or &lt;code&gt;​'file&lt;/code&gt; argument to limit ido to buffer switching and
finding files respectively.&lt;/p&gt;&lt;h3&gt;Completion&lt;/h3&gt;&lt;p&gt;As you type, ido narrows the result set based on which items your
selection is a substring of. You can select the first result by pressing
&lt;code&gt;RET&lt;/code&gt; and you can cycle through the result set at any point with &lt;code&gt;C-s&lt;/code&gt;
and &lt;code&gt;C-r&lt;/code&gt;. This can pose a problem if, for example, you want to create
a new file whose name is a substring of another file in the current
directory. You can use &lt;code&gt;C-j&lt;/code&gt; to tell ido to use whatever you typed
verbatim.&lt;/p&gt;&lt;p&gt;Ido limits the list of visible completions to a few at a time, but if
you want to view the full list of available completions you can hit &lt;code&gt;?&lt;/code&gt;.
Doing so will display all of the available completions in a separate
buffer.&lt;/p&gt;&lt;h4&gt;Prefix matching&lt;/h4&gt;&lt;p&gt;Toggle prefix matching inside an ido buffer by hitting &lt;code&gt;C-p&lt;/code&gt;. This is
similar to the standard Emacs completion method in that it will only
display results that start with your selection.&lt;/p&gt;&lt;p&gt;You can make prefix matching the default behavior by setting
&lt;code&gt;ido-enable-prefix&lt;/code&gt; to a truthy value.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-emacs-lisp"&gt;(setq ido-enable-prefix t)
&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;Flexible matching&lt;/h4&gt;&lt;p&gt;Ido's flexible matching makes it so that any items containing all of the
selection's characters in order will appear in the result set.&lt;/p&gt;&lt;p&gt;Turn it on by setting &lt;code&gt;ido-enable-flex-matching&lt;/code&gt; to a truthy value.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-emacs-lisp"&gt;(setq ido-enable-flex-matching t)
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;Finding files&lt;/h3&gt;&lt;p&gt;&lt;code&gt;ido-find-file&lt;/code&gt; comes with a few file-specific bindings. Find more
information about this command with &lt;code&gt;C-h f ido-find-file RET&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;I most commonly use &lt;code&gt;C-d&lt;/code&gt; to open the current matching directory in
dired and &lt;code&gt;C-k&lt;/code&gt; to delete the current matching file.&lt;/p&gt;&lt;p&gt;To ignore certain file extensions when finding files set
&lt;code&gt;ido-ignore-extensions&lt;/code&gt; to a truthy value and add the extension to the
&lt;code&gt;completion-ignored-extensions&lt;/code&gt; list.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-emacs-lisp"&gt;(setq ido-ignore-extensions t)
(add-to-list 'completion-ignored-extensions ".pyc")
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Sometimes it is convenient to use the filename under the cursor as a
starting point for ido completion, &lt;code&gt;ido-use-filename-at-point&lt;/code&gt; tells ido
to do just that.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-emacs-lisp"&gt;(setq ido-use-filename-at-point 'guess)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;By default, ido will ask for confirmation every time you attempt to
create a new buffer. This can become annoying if you like to create many
buffers. Turn it off with:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-emacs-lisp"&gt;(setq ido-create-new-buffer 'always)
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;Switching buffers&lt;/h3&gt;&lt;p&gt;As with finding files, switching buffers using ido comes with a number
of commands you can run on the current selection. Find out more about
this command with &lt;code&gt;C-h f ido-switch-buffer RET&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;I most commonly use &lt;code&gt;C-k&lt;/code&gt; to kill the current matching buffer. If you
find that the buffer you meant to switch to isn't open, you can switch
to &lt;code&gt;find-file&lt;/code&gt; by hitting &lt;code&gt;C-f&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;You can use &lt;code&gt;ido-switch-buffer&lt;/code&gt; to switch to recently-used buffers by
enabling &lt;code&gt;ido-use-virtual-buffers&lt;/code&gt;. Doing so will turn &lt;code&gt;recentf&lt;/code&gt; mode
on which means you will be able to open a file in Emacs, close it then
start it back up and switch to that file's buffer as if it were already
open.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-emacs-lisp"&gt;(setq ido-use-virtual-buffers t)
&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Extensions&lt;/h2&gt;&lt;h3&gt;smex&lt;/h3&gt;&lt;p&gt;&lt;a href="https://github.com/nonsequitur/smex"&gt;smex&lt;/a&gt; is an ido-based replacement for &lt;code&gt;M-x&lt;/code&gt;. In addition to
bringing ido completion to &lt;code&gt;M-x&lt;/code&gt;, &lt;code&gt;smex&lt;/code&gt; maintains a list of your
most-used commands so that it can order results by frequency.&lt;/p&gt;&lt;p&gt;You can grab it off of &lt;a href="http://melpa.org/"&gt;MELPA&lt;/a&gt; and bind it to your preferred key.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-emacs-lisp"&gt;(require 'smex)
(smex-initialize)

;; My personal preference is C-;
(global-set-key (kbd "C-;") #'smex)
;; but you can also override M-x
(global-set-key (kbd "M-x") #'smex)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Smex comes with command completion for the current major mode. You can
use this by binding &lt;code&gt;smex-major-mode-commands&lt;/code&gt; to a key.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-emacs-lisp"&gt;(global-set-key (kbd "M-X") #'smex-major-mode-commands)
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;ido-ubiquitous&lt;/h3&gt;&lt;p&gt;Like the name implies, &lt;a href="https://github.com/DarwinAwardWinner/ido-ubiquitous"&gt;ido-ubiquitous&lt;/a&gt; is a package
that attempts to weave in ido completion wherever it can. With this
package, most functions that use &lt;code&gt;completing-read&lt;/code&gt; will automatically
start using ido for completion.&lt;/p&gt;&lt;p&gt;The package is available on MELPA and you can turn it on by calling
&lt;code&gt;ido-ubiquitous-mode&lt;/code&gt;.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-emacs-lisp"&gt;(require 'ido-ubiquitous)
(ido-ubiquitous-mode)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you run into any issues because of &lt;code&gt;ido-ubiquitous&lt;/code&gt;, view
the documentation for &lt;code&gt;ido-ubiquitous-command-overrides&lt;/code&gt; and
&lt;code&gt;ido-ubiquitous-function-overrides&lt;/code&gt; by calling &lt;code&gt;describe-variable&lt;/code&gt;
(bound to &lt;code&gt;C-h v&lt;/code&gt; by default). You can use those variables to turn ido
off for specific functions or commands.&lt;/p&gt;&lt;p&gt;Note that &lt;code&gt;ido-ubiquitous&lt;/code&gt; does not turn ido completion on for packages
that come with built in ido support (even if it is not turned on by
default) like Magit and Org mode. I have included a section below on how
you can turn ido on for both of those modes.&lt;/p&gt;&lt;h3&gt;ido-vertical-mode&lt;/h3&gt;&lt;p&gt;&lt;a href="https://github.com/creichert/ido-vertical-mode.el"&gt;ido-vertical-mode&lt;/a&gt; modifies the ido completion
buffer so that it displays vertically rather than horizontally, making
it so that the most relevant completions are displayed at the top.&lt;/p&gt;&lt;p&gt;&lt;code&gt;ido-vertical-mode&lt;/code&gt; is available on MELPA.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-emacs-lisp"&gt;(require 'ido-vertical-mode)
(ido-vertical-mode)
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;ido-clever-match&lt;/h3&gt;&lt;p&gt;Finally, &lt;a href="https://github.com/Bogdanp/ido-clever-match"&gt;ido-clever-match&lt;/a&gt; is a simple package I
wrote that wraps the built-in ido matching function in order to try to
provide predictable prefix, substring and flex matching. You can find
more information about how it works on its Github page but the gist of
it is it ranks matches based on class (&lt;code&gt;exact&lt;/code&gt;, &lt;code&gt;prefix&lt;/code&gt;, &lt;code&gt;substring&lt;/code&gt; or
&lt;code&gt;flex&lt;/code&gt;) and then some sub-metric within that class. The package ensures
that &lt;code&gt;prefix&lt;/code&gt; matches always come before &lt;code&gt;substring&lt;/code&gt; which always come
before &lt;code&gt;flex&lt;/code&gt; matches.&lt;/p&gt;&lt;p&gt;It is available on MELPA and you can enable it with:&lt;/p&gt;&lt;pre&gt;&lt;code info="language-emacs-lisp"&gt;(require 'ido-clever-match)
(ido-clever-match-enable)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you find that you prefer ido's standard matching behavior and would
like to go back simply call &lt;code&gt;ido-clever-match-disable&lt;/code&gt;.&lt;/p&gt;&lt;h2&gt;Other packages&lt;/h2&gt;&lt;p&gt;I've found that the following packages work particularly well when
paired with ido.&lt;/p&gt;&lt;h3&gt;Magit&lt;/h3&gt;&lt;p&gt;&lt;a href="https://github.com/magit/magit"&gt;Magit&lt;/a&gt; comes with its own completion function which you can
replace with ido by setting &lt;code&gt;magit-completing-read-function&lt;/code&gt; to
&lt;code&gt;magit-ido-completing-read&lt;/code&gt;.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-emacs-lisp"&gt;(setq magit-completing-read-function #'magit-ido-completing-read)
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;Org mode&lt;/h3&gt;&lt;p&gt;Like Magit, &lt;a href="http://orgmode.org/"&gt;Org mode&lt;/a&gt; comes with its own completion function
which you can replace with ido by setting &lt;code&gt;org-completion-use-ido&lt;/code&gt; to a
truthy value.&lt;/p&gt;&lt;p&gt;The documentation recommends that you turn off
&lt;code&gt;org-outline-complete-in-steps&lt;/code&gt; if you switch to ido completion.&lt;/p&gt;&lt;pre&gt;&lt;code info="language-emacs-lisp"&gt;(setq org-completion-use-ido t
      org-outline-path-complete-in-steps nil)
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;Projectile&lt;/h3&gt;&lt;p&gt;Ido is &lt;a href="https://github.com/bbatsov/projectile"&gt;Projectile's&lt;/a&gt; default completion method. The
maintainers recommend you install &lt;a href="https://github.com/lewang/flx"&gt;flx-ido&lt;/a&gt; for its flexible
matching but you can also use Projectile with ido-clever-match.&lt;/p&gt;&lt;h2&gt;Wrapping up&lt;/h2&gt;&lt;p&gt;In closing, ido-mode is an extremely versatile package that can
massively enhance one's workflow when using Emacs.&lt;/p&gt;&lt;p&gt;I highly recommend you try it out!&lt;/p&gt;&lt;/article&gt;</description></item></channel></rss>