#lang rackjure
1 Background/  philosophy
2 #lang rackjure vs. require
3 Threading macros
4 Applicable dictionaries
4.1 Not-found values
5 Dictionary initialization using {}
6 Dictionary utilities
7 Strings
8 Conditionals
9 Operational equivalence
9.1 egal? and structs
10 Other
10.1 Partial application
10.2 Atomic swap
11 Reader function literals

#lang rackjure

Provide a few Clojure-inspired ideas in Racket. Where Racket and Clojure conflict, prefer Racket.


This is tested on Racket versions 6.0 and newer.

    1 Background/philosophy

    2 #lang rackjure vs. require

    3 Threading macros

    4 Applicable dictionaries

      4.1 Not-found values

    5 Dictionary initialization using {}

    6 Dictionary utilities

    7 Strings

    8 Conditionals

    9 Operational equivalence

      9.1 egal? and structs

    10 Other

      10.1 Partial application

      10.2 Atomic swap

    11 Reader function literals

1 Background/philosophy

Asumu Takikawa’s #lang clojure showed me what’s possible and is the basis for much of this. Why not just use that? Because I wanted to use some Clojure ideas in Racket, not use Clojure.

For example the threading macros are ~> and ~>> (using ~ instead of -) because Racket already uses -> for contracts. Plus as Danny Yoo pointed out to me, ~ is more "thready".

When it must choose, #lang rackjure chooses to be more Rackety.

2 #lang rackjure vs. require

 (require rackjure) package: rackjure

Most features work if you merely (require rackjure) or a specific module such as (require rackjure/threading) in any module language such as racket or racket/base.

However a few features only work as a module language — by using #lang rackjure at the start of your source file, or by supplying rackjure as the language in a module form. This is because they depend on redefining #%app or extending the Racket reader. These are:

Of course, because they must make #%app do more work at runtime, there is some performance overhead.

However the overhead is only for function applications within a module using rackjure as its language — not for function applications in other modules.

If you do not need those features, you can (require rackjure) or even just the specific modules you use, in a "leaner" lang such as racket/base.

For example you can use just the threading macros ~> and ~>> in racket/base:

#lang racket/base
(require rackjure/threading)

3 Threading macros

 (require rackjure/threading) package: rackjure

As of version 0.9, instead of providing its own implementation, this module now re-provides all of the threading package, which has additional features not described here. Please refer to its documentation.


(~> expression form ...)

Threads expression through the forms. Inserts expression as the second item in the first form, making a list of it if it is not a list already. If there are more forms, inserts the first form as the second item in second form, etc.


(~>> expression form ...)

Like ~> but inserting as the last item in each form.

The "threading" macros let you thread values through a series of applications in data-flow order. This can be a really refreshing way to express a series of transforms, instead of nested function calls.

Although similar to the thrush combinator function (and you may hear them described that way), these are actually macros (in both Clojure and #lang rackjure).

And although similar to compose, the order is reversed, and again, these are macros.

The ~> form "threads" values through a series of forms as the second item of each form. (If a form is a function application, remember that the second item is the first argument.)

For example, instead of:

(string->bytes/utf-8 (number->string (bytes-length #"foobar") 16))

You can write:

(~> #"foobar"
    (number->string 16)

Or if you prefer on one line:

(~> #"foobar" bytes-length (number->string 16) string->bytes/utf-8)

Notice that bytes-length and string->bytes/utf-8 aren’t enclosed in parentheses. They could be, but if they’re not, the ~> macro adds them automatically. A function that takes just one argument can be specified this way.


(some~> expression form ...)

Analogous to some-> in Clojure, i.e. stop threading at a #f value.


(some~>> expression form ...)

Analogous to some->> in Clojure, i.e. stop threading at a #f value.

4 Applicable dictionaries

#lang rackjure redefines #%app to make applications work differently when a dict? is in the first position:

; When (dict? d) is #t
; Set
(d key val)            => (dict-set d key val)
; Get
(d key)                => (dict-ref d key #f)
(d key #:else default) => (dict-ref d key default)

And also when a dict? is in the second position:

; Get
(key d)  => (dict-ref d key)
(key #f) => #f  ; unless (or (procedure? key) (dict? key))

These last two variants, in combination with the ~> threading macro, provide concise notation for accessing nested dictionary (for example the nested hasheqs from Racket’s json module):

(~> dict 'a 'b 'c)

expands to:

('c ('b ('a dict)))

which in turn is applied as:

(dict-ref (dict-ref (dict-ref dict 'a) 'b) 'c)

Note that dictionary keys are not required to be Clojure style :keywords. They may be anything.

This application syntax doesn’t work for a dict? that stores procedure? as keys or values. The reason is that #lang rackjure must provide its own #%app. The only way (AFAIK) it can distinguish a normal function application from a dictionary application is to check for procedure? in the first position. As a result, in those cases you’ll have to use dict-ref and dict-set.

Keep in mind that a dict? is a Racket generic that covers a variety of things besides hash tables and association lists, such as vectors and lists. As a result if v is a vector then (vector-ref v 2) can be written simply as (v 2).

4.1 Not-found values

One issue is how to handle the optional last argument to dict-ref, which is the value to use if the key is not found. We handle this slightly differently than dict-ref:

1. We use an optional keyword argument, #:else. This leaves arity 3 available to mean dict-set.

2. If this arg isn’t supplied and the key isn’t found we return #f (whereas dict-ref raises an error). Returning #f is more convenient, especially when used with threading macro ~>. It’s smart that dict-ref lets you supply a specific value to mean not-found, because what if #f or 'not-found or whatever could be a valid value in the dictionary? But it’s even smarter to have not-found default to something other than raising an error. That way, the burden is only on code that needs to store #f as values in a dict, and such code can use the #:else keyword.

5 Dictionary initialization using {}

#lang rackjure provides a more-concise way to create dictionaries.

You can write

((k0 . v0)(k1 . v1) ...)


{k0 v0 k1 v1 ... ...}

Especially handy with nested dicts:

{'key "value"
 'key1 {'key "value"
        'key1 "value1"}}

The current-curly-dict parameter says what this expands to.


(current-curly-dict)  procedure?

(current-curly-dict v)  void?
  v : procedure?
Defaults to alist. May be set to hash, hasheq or anything with the same (f k v ... ...) signature.


> (parameterize ([current-curly-dict alist])
    {'k0 0 'k1 1})
'((k0 . 0) (k1 . 1))
> (parameterize ([current-curly-dict hasheq])
    {'k0 0 'k1 1})
'#hasheq((k0 . 0) (k1 . 1))

 (require rackjure/alist) package: rackjure


(alist key val ... ...)  (listof (cons any/c any/c))

  key : any/c
  val : any/c
Creates an association list.

> (alist 'k0 0 'k1 1 'k2 2)

'((k0 . 0) (k1 . 1) (k2 . 2))

6 Dictionary utilities

 (require rackjure/dict) package: rackjure

A few utility functions for dicts.


(dict-merge d0 d1)  dict?

  d0 : dict?
  d1 : dict?
Functionally merge d1 into d0. Values in d0 are overriden by values with the same key in d1. Nested dicts are handled recursively.

> (dict-merge {} {'type 'line})
'((type . line))
> (dict-merge {'type 'triangle 'sides  3}
              {'type 'square   'sides  4})
'((type . square) (sides . 4))
> (dict-merge {'people {'john {'age 10}
                        'mary {'age 7}}}
              {'people {'john {'age 11}}})
'((people (john (age . 11)) (mary (age . 7))))

Setting a value in d1 to the current value of the dict-merge-delete-value parameter – which defaults to 'DELETE – causes the key/value in d0 with that key to be deleted from the returned dictionary.

> (dict-merge '([a . a][b . b])
              '([b . DELETE]))
'([a . a])

Defaults to 'DELETE. Used to tell dict-merge that a key/value pair with that key should be deleted.

> (parameterize ([dict-merge-delete-value 'DELETE])
    (dict-merge '([a . a]
                  [b . b])
                '([b . DELETE])))
'([a . a])
> (parameterize ([dict-merge-delete-value 'FOO])
    (dict-merge '([a . a]
                  [b . b])
                '([a . DELETE]
                  [b . FOO])))
'((a . DELETE))


(dict->curly-string d)  string?

  d : dict?
Returns a {} style string describing the dict d, including any nested dicts.

> (define sample-dict '([a . 0]
                        [b . 0]
                        [c . ([a . 0]
                              [b . 0]
                              [c . ([a . 0]
                                    [b . 0]
                                    [c . 0])])]))
> (displayln (dict->curly-string sample-dict))
{'a 0
 'b 0
 'c {'a 0
     'b 0
     'c {'a 0
         'b 0
         'c 0}}}

7 Strings

 (require rackjure/str) package: rackjure


(str expression ... #:fmt fmt #:sep sep)

  (and/c string? immutable?)
  expression : any/c
  fmt : ~a
  sep : ""
str can be a succinct alternative to string-append and/or format.

Also, it returns an immutable string (created via string->immutable-string).

> (str)


> (str "hi")


> (str 1)


> (str #f)


> (str "Yo" "Yo")


> (str "Yo" "Yo" "Ma")


> (apply str '(0 1 2 3))


> (str 0 1 2 3)


> (str '(0 1 2 3))

"(0 1 2 3)"

Our version adds optional keyword arguments, the defaults of which behave like Clojure’s str:

> (str #:fmt ~v "Yo" "Yo")


> (str #:sep " " "Yo" "Yo")

"Yo Yo"

> (str #:fmt ~v  #:sep " " "Yo" "Yo")

"\"Yo\" \"Yo\""

8 Conditionals

 (require rackjure/conditionals) package: rackjure


(if-let [identifier test-expr] then-expr else-expr)

Combines if and let:

(let ([identifier test-expr])
  (if identifier


(when-let [identifier test-expr] body ...+)

Combines when with let:

(let ([identifier test-expr])
  (when identifier
    body ...))


(if-not test-expr then-expr else-expr)

A shortcut for:

(if (not test-expr)


(when-not test-expr body ...+)

A shortcut for:

(when (not test-expr)
  body ...)

However in colloquial Racket we’d simply use unless:

(unless test-expr
  body ...)

9 Operational equivalence

 (require rackjure/egal) package: rackjure


(egal? v1 v2)  boolean?

  v1 : any/c
  v2 : any/c
An implementation of egal? as described in Equal Rights for Functional Objects.

An alternative to equal? and eq? that says whether two things are "operationally equivalent", by taking into account mutability.

In general, two things that are equal? will also be egal? only if they are both immutable. Some things in Racket aren’t immutable by default. For example, although "string-constants" are immutable, strings returned by string or string-join are mutable, unless you also run them through string->immutable-string. Same with bytes. Other things come in both mutable and immutable variants, such as hashes and vectors.

For more details, see egal.rkt for the implementation and test cases. A few examples:

> (require rackjure/egal)
; Although "string" literals are immutable...
> (egal? "a" "a")


; string is mutable...
> (egal? (string #\a) (string #\a))


; Immutable strings are (you guessed it) immutable...
> (egal? (string->immutable-string (string #\a))
         (string->immutable-string (string #\a)))


9.1 egal? and structs

For two structs to be egal?, all of the following must be true:

1. They must have the same field values.

2. They must be instances of the same structure type.

3. The structure type must be #:transparent. (Regular equal? does a field comparison for Racket structs only if they are #:transparent. Otherwise the structs are opaque and eq? is used.)

4. The structure type must not be #:mutable, nor must any of the individual fields be #:mutable.

10 Other

 (require rackjure/utils) package: rackjure

10.1 Partial application


(partial proc v ...)  procedure?

  proc : procedure?
  v : any/c
Function for partial application. Differs from curry in that it doesn’t care about function arity.

((partial + 1) 2) <=> (+ 1 2)

10.2 Atomic swap


(box-swap! box proc v ...)  any/c

  box : box?
  proc : procedure?
  v : any/c
Like swap! in Clojure, but for box?.

Essentially it is:

(define (box-swap! box f . args)
  (let loop ()
    (let* ([old (unbox box)]
           [new (apply f old args)])
      (if (box-cas! box old new)

11 Reader function literals

The Clojure reader lets you succinctly define anonymous function literals. For example

#(+ % %2)

is equivalent to this in Clojure:

(fn [% %2] (+ % %2))

or in Racket:

(λ (% %2) (+ % %2))
(lambda (% %2) (+ % %2))

The Racket reader already uses #( ) for vector literals. Therefore Rackjure instead uses your choice of #fn( ), #λ( ), or #lambda( ).


> (map #λ(+ % 1) '(1 2 3))

'(2 3 4)

> (map #λ(+ % %2) '(1 2 3) '(1 2 3))

'(2 4 6)


;; Rest argument

> (#λ(apply list* % %&) 1 '(2 3))

'(1 2 3)


;; Keyword argument

> (#λ(* 1/2 %#:m (* %#:v %#:v)) #:m 2 #:v 1)



;; Ignores unused arguments

> (#λ(begin %2) "ignored" "used")



;; Handles an arbitary number of arguments

> (apply #λ(list %1 %42) (build-list 42 add1))

(list 1 42)