#lang axe
1 Reader Extension
1.1 Raw String
regex-escape-raw
pregexp-raw
regexp-raw
1.2 Raw regexp
1.3 Quick Keyword
1.4 Applicable Dictionary
1.5 Dictionary initialization with {}
current-curly-dict
1.6 Lambda literals
2 Handy Macros
2.1 Threading Macros
~>
~>>
and~>
and~>>
lambda~>
λ~>
lambda~>>
λ~>>
lambda~>*
λ~>*
lambda~>>*
λ~>>*
lambda-and~>
λ-and~>
lambda-and~>>
λ-and~>>
lambda-and~>*
λ-and~>*
lambda-and~>>*
λ-and~>>*
2.2 Conditionals
if-let
when-let
if-not
when-not
cond-let
3 Utility Functions
3.1 Dictionary
dict-extend
dict-extend!
dict-merge
dict-merge!
dict-merge-with
dict-merge-with!
8.12

#lang axe🔗

jinzhouz

Some handy tools that you might need. Just like the axe in your hand. Source

    1 Reader Extension

      1.1 Raw String

      1.2 Raw regexp

      1.3 Quick Keyword

      1.4 Applicable Dictionary

      1.5 Dictionary initialization with {}

      1.6 Lambda literals

    2 Handy Macros

      2.1 Threading Macros

      2.2 Conditionals

    3 Utility Functions

      3.1 Dictionary

 #lang axe package: axe

1 Reader Extension🔗

Reader extension is enabled by the #lang axe line on the top of your source file.

1.1 Raw String🔗

#lang axe
r"raw string"
r’raw string’

This syntax is borrowed from Python. If the string literals you are trying to write contains lots of (mixed) quotes, or escaped forms, raw string can help a lot.

For example if we want to match a single digit we have to compose regular expression: (pregexp "\\d"). Note that we have to write \\ to make racket’s reader happy and make us unhappy.

With this reader extension, the \\ character is interpreted as raw. But note that \" is interpreted as " in r"\"" form. The same goes to ' in r'\''.

>

 

(list r'\t\a\b\c\d\1 \'quote\'')

'("\\t\\a\\b\\c\\d\\1 'quote'")

If your your string contains lots of mixed quotes, the three quotes version might be more convenient:
#lang axe
r"""raw string"""
r”’raw string”’

Here’s an example:

>

 

(list r'''mixe 'single quote' with "double quote""''')

'("mixe 'single quote' with \"double quote\"\"")

Note that """ or '''' are matched eagerly. So that in r"""ends with"""", racket will treat the last quote as the begin of another quoted string.

Now we have an easy way to read in raw strings, we still got some troubles building up regular expressions. Thus we provide pregexp-raw and regexp-raw to build up regular expressions from raw strings.

>

 

(regexp r"(\t*)\1")

#rx"(\\t*)\\1"

>

 

(pregexp-raw r"(\t*)\1")

#px"(\t*)\\1"

syntax

(regex-escape-raw raw-string)

Parse the escaped characters in raw strings into. For example If you write r"\t", it is equivalent to string "\\t". regex-escape-raw will parse it into "\t". While raw strings like "\\1" will be used by regexp, so it will remains the same.

Example:
> (regex-escape-raw "\\a\\b\\t\\n\\1")

"\a\b\t\n\\1"

syntax

(pregexp-raw raw-string)

syntax

(regexp-raw raw-string)

It is a wrapper of regex-escape-raw, so that you can create regular expressions directly from raw strings.
(regexp (regex-escape-raw raw-string))
(pregexp (regex-escape-raw raw-string))

1.2 Raw regexp🔗

#lang axe
#rx/raw regexp/
#px/raw regexp/
r/raw regexp string/

Typing regular expressions could be difficult in racket. In python, we can write a regular expression in raw mode: r"(\t*)\1". When translated into racket, we have #px"(\t*)\\1". Note that we need to add another \ character to escape \\1.

It is inconvenient. Luckily we have #lang at-exp so that we can do things like this:

#lang at-exp racket/base

>

 

(regexp-match @regexp{(.*)\1} "123123")

'("1231" "123")

\1 is one example that you do NOT want something to be escaped by racket reader. But sometimes you do need it: such as when type @px"\t", you actually wants to match the #\tab. If we use at-exp for it, it will treat \t as two characters: \\ and t. Which will not be recognized as #\tab character in racket’s "pregexp" compiler, nor "regexp" of course.

Thus we introduce the new form:

#lang axe
(regexp-match #px/(\t*)\1/ "\t\t")

reports '("\t\t" "\t"). That means the you can write raw regexp and enclose it with #px/ and /. The same goes to #rx/raw/

The "raw regexp string" is like raw regular expressions, but they can be used as replace string in regexp-replace. The following two forms are equal:

#lang axe
 
#px/(\t*)\1/
(pregexp r/(\t*)\1/)

And the r/raw regex string/ can be use where regexp strings are needed:

#lang axe
(regexp-replace #px/(\d+)/ "abc123xyz" r/-\1-/)

We got result "abc-123-xyz".

1.3 Quick Keyword🔗

This one is simple. You can replace #:key with :key.

1.4 Applicable Dictionary🔗

Borrowed from #lang rackjure, we redefines #%app to make dictionaries applicable:

; When (dict? d) is #t
 
(d key)         => (dict-ref d key #f)
(d key default) => (dict-ref d key default)

Note here that we don’t support rackjure’s set syntax: (d key value). We prefer clojure style. You can also use key as a procedure to retrieve the contents of a dict:

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

The reason that #f is returned for the (key #f) is that we can use it together with the threading macro to fetch the contents of nested dicts:

(~> dict 'a 'b 'c)

expands to:

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

And is applied as:

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

1.5 Dictionary initialization with {}🔗

Racket supports writting dict literals as:

#hash((k0 . v0) (k1 . v1))

It is straightforward but requires more typing than:

{k0 v0 k1 v1 ...}

Especially when typing nested lists:

{k0 v0,
       k1 {key value,
                     key value}}

Note the character , is optional here. In axe , is treated as whitespace if followed by other whitespaces. Thus you can use it as a delimiter whitespace or use it as unquote normally.

Borrowed form rackjure, we provide current-curly-dict parameter to specify the type of dict it expands to.

parameter

(current-curly-dict)  procedure?

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

Examples:

>

 

(parameterize ([current-curly-dict hasheq])
   {'k0 0, 'k1 1})

'#hasheq((k0 . 0) (k1 . 1))

>

 

(parameterize ([current-curly-dict hasheqv])
   {'k0 0 'k1 1})

'#hasheqv((k0 . 0) (k1 . 1))

1.6 Lambda literals🔗

Thanks to rackjure and curly-fn for the idea and implementation.

The clojure reader lets us to define function literals through:

#(+ % %2)

It is equivalent to this in clojure:

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

Or in racket:

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

In the #(...) form, arguments are denoted using identifiers prefixed with %.

Racket uses #( ) for vector literals by default. It is overwritten by axe. You can use #[ ] for that if needed. Besides, axe provides other forms for lambda literals: #fn(), #λ( ) and #lambda( ).

>

 

(sequence->list (map #(+ 1 %) '(1 2 3)))

'(2 3 4)

>

 

(sequence->list (map #fn(+ % %2) '(1 2 3) '(1 2 3)))

'(2 4 6)

>

 

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

'(1 2 3)

>

 

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

1

>

 

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

1

>

 

(#(begin %2) 'ignored 'used)

'used

Remember in the above example, we use :m to replace #:m, see Quick Keyword.

There are some pitfalls about lambda literals that normally you should not care about:

2 Handy Macros🔗

2.1 Threading Macros🔗

 (require axe/threading) package: axe

Threading macros are first introduced in clojure. It helps us to change nested function calls into "pipelines". For example: we are calculating some values with a complicated nested function calls:

> (- (bytes-ref (string->bytes/utf-8 (symbol->string 'abc)) 1) 2)

96

It would be hard to understand the data flow in this expression. It would be more clear if we convert it into pipelines using the threading macro:
> (~> 'abc
      symbol->string
      string->bytes/utf-8
      (bytes-ref 1)
      (- 2))

96

Note that symbol->string and string->bytes/utf-8 are not enclosed in parenthesis.

~> macro also enables you to use placeholder(_) to specify the position of the arguments you want to place. So that you can achieve something like this:

> (~> '(1 2 3)
       (map sqrt _)
       (apply + _)
       (- 20 (* _ 2)))

11.707471260116055

You can also change the symbol for place holder to any identifier you like:

> (require (rename-in axe [_ %]))
> (~> '(1 2 3)
       (map sqrt %)
       (apply + %)
       (- 20 (* % 2)))

11.707471260116055

Threading macro in axe will handle lambda well, so that the following example works correctly.

> (~> 10 (lambda (x) (- x 1)))

9

> (~> 10 (lambda (x) (- x 1)) (lambda (x) (* x x)))

81

If the placeholder _ is included in the body of lambda expression, the ~> macro will instead replace the placeholder with the result of previous "pipeline" and generate a new lambda function.

(~> 10 (lambda (x) (- x 1))) => ((lambda (x) (- x 1)) 10)
(~> 10 (lambda (x) (- x _))) => (lambda (x) (- x 10))

So that you can do the following:

> ((~> 10 (lambda (x) (- x _))) 1)

-9

Finally, we have some litte hack in threading macro, now you can NOT use [...] or {...} to represent function applications. But we already use {...} for dictionaries, so hope it won’t be too strange for you.

syntax

(~> expr clause ...)

Threads expr through clause. Insert expr as the second item in the first clause. (~> expr (function arg)) is transformed into (~> expr (function _ arg)) and that results in (function expr arg).

If there are multiple clauses, thread the first clause as the second item in the second clause, recursively.

syntax

(~>> expr clause ...)

Like ~> but insert expr as the last item in clause. So that it equals to (~>> expr (function arg _)).

syntax

(and~> expr clause ...)

Works like ~>, but immediatly returns #f if any of the clause returns #f. Like and, this is short-circuiting, so the remaining steps will not be executed.

Examples:

Examples:
> (and~> '(2 4 5)
   (map add1 _)
   (index-where even?)
   (* 2))

4

> (and~> '(2 4 6)
   (map add1 _)
   (index-where even?)
   (* 2))

#f

syntax

(and~>> expr clause ...)

Combines the threading behavior of ~>> and the short-circuiting behavior of and~>.

syntax

(lambda~> clause ...)

syntax

(λ~> clause ...)

Handy wrapper for (λ (arg) (~> arg clause ...)).

Example:
> (map (λ~> add1 (* 2)) (range 5))

#<stream>

syntax

(lambda~>> clause ...)

syntax

(λ~>> clause ...)

Like lambda~> but for ~>>.

syntax

(lambda~>* clause ...)

syntax

(λ~>* clause ...)

Equivalent to (λ args (~> args clause ...)).

syntax

(lambda~>>* clause ...)

syntax

(λ~>>* clause ...)

Like lambda~>* but for ~>>.

syntax

(lambda-and~> clause ...)

syntax

(λ-and~> clause ...)

syntax

(lambda-and~>> clause ...)

syntax

(λ-and~>> clause ...)

syntax

(lambda-and~>* clause ...)

syntax

(λ-and~>* clause ...)

syntax

(lambda-and~>>* clause ...)

syntax

(λ-and~>>* clause ...)

Like lambda~>, but for and~>.

The threading module, and rackjure had already provide such functionality and good documents. But rackjure do not support placeholder and threading module do not support placeholder in nested function calls like (~> expr (fun1 (func2 _ arg))).

2.2 Conditionals🔗

 (require axe/conditionals) package: axe

This module provides alternatives to built in if, when and cond.

syntax

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

combines if and match-let:
(let ([val test-expr])
 (if val
     (match-let ([binding-expr test-expr])
       then-expr)
     else-expr))

syntax

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

combines when and match-let:
(let ([val test-expr])
 (when val
     (match-let ([binding-expr test-expr])
       body ...)))

syntax

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

shotcut for:
(if (not test-expr)
    then-expr
    else-expr)

syntax

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

Just another implementation of unless. To make it consistent in the wording.
(when (not test-expr)
    body ...+)

syntax

(cond-let binding-expr cond-clause ...)

 
cond-clause = [test-expr then-body ...+]
  | [else then-body ...+]
  | [test-expr => proc-expr]
  | [test-expr]
As you can see in the signature, it is almost identical to the built in cond, except it allows an binding-expr for the test-expr, so that in the then-body for proc-expr, you can refer to the result of test-expr with it.

Example:
> (let ([lst '(x y z a b c)])
    (cond-let it
      [(member 'a lst) (length it)]
      [else 0]))

3

match-let pattern can be applied to descruct the values:

Example:
> (let ([dct #hasheq((a . #f) (b . (1 2)) (c . (10 20)))])
    (cond-let (list a b)
      [(dict-ref dct 'a) (+ a b)]
      [(dict-ref dct 'b) (- a b)]
      [(dict-ref dct 'c) (* a b)]
      [else 0]))

-1

3 Utility Functions🔗

3.1 Dictionary🔗

 (require axe/dict) package: axe

Now axe depends on data/collection, you are safely to use it instead.

axe provide some wrappers over the dictionary related functions provided by racket/dict.

procedure

(dict-extend dict key value ...)  (and/c dict? immutable?)

  dict : (and/c dict? immutable?)
  key : any/c
  value : any/c
Iterate over all the key-value pairs and update the dictionary accordingly.

Examples:
> (dict-extend '((a . 1)) 'b 2 'a 3)

'((a . 3) (b . 2))

> (dict-extend #hash() 'a 1 'b 2 'a 3)

'#hash((a . 3) (b . 2))

procedure

(dict-extend! dict key value ...)

  (and/c dict? (not/c immutable?))
  dict : (and/c dict? (not/c immutable?))
  key : any/c
  value : any/c
The mutable version of dict-extend.

Example:
> (dict-extend! (make-hash) 'b 2 'a 3)

procedure

(dict-merge d0 d1 ...)  (and/c dict? immutable?)

  d0 : (and/c dict? immutable?)
  d1 : dict?

procedure

(dict-merge! d0 d1 ...)  (and/c dict? (not/c immutable?))

  d0 : (and/c dict? (not/c immutable?))
  d1 : dict?
Merge several dicts into d0. dict-merge! is the mutable version.

Examples:
> (dict-merge '((a . 1)) '((b . 2) (c . 3)) '((a . 4) (c . 5)))

'((a . 4) (b . 2) (c . 5))

> (define d (make-hash '((a . 1))))
> (dict-merge! d '((b . 2) (c . 3)) #hash((a . 4) (c . 5)))
> d

'#hash((a . 4) (b . 2) (c . 5))

procedure

(dict-merge-with f d0 d1 ...)  (and/c dict? immutable?)

  f : (any/c any/c . -> . any/c)
  d0 : (and/c dict? immutable?)
  d1 : dict?

procedure

(dict-merge-with! f d0 d1 ...)  (and/c dict? (not/c immutable?))

  f : (any/c any/c . -> . any/c)
  d0 : (and/c dict? (not/c immutable?))
  d1 : dict?
Like dict-merge and dict-merge!. But when both dicts contain the same key, merge function f is called with arguments the value in the old dict and value in the new dict.

Examples:
> (dict-merge-with list #hash((a . 1)) '((b . 2) (c . 3)) '((a . 4) (c . 5)))

'#hash((a . (1 4)) (b . 2) (c . (3 5)))

> (dict-merge-with - '((a . 5)) '((b . 2) (c . 3)) '((a . 4) (c . 5)))

'((a . 1) (b . 2) (c . -2))