100 Languages Speedrun: Episode 34: Racket Scheme
Tags: racket, scheme, lisp
Published: 2021-12-26 17:25:23

Racket is Lisp style language. It used to be called "PLT Scheme", but it diverged from Scheme enough that they decided to rename the language.

Scheme is a fairly small language, and that's fine for teaching, but functionality Scheme standards defines is not enough for any serious programming. So every Scheme implementation adds a lot of extra functionality to their Scheme, making Scheme programs not very compatible with each other. Racket just followed this path a bit further than most, and ended up declaring itself its own language.

Racket and Clojure are the two most popular Lisp variants nowadays. In fact even though Racket isn't officially a "Scheme" anymore, most of the time when people say "Scheme", they're actually talking about Racket.

### Hello, World!

Hello, World already starts spicy:

```
#!/usr/bin/env racket
#lang racket

(display "Hello World\n")
```

`#!` line just tells the operating system to run the file with Racket.

The most interesting is `#lang racket` line. Every file in Racket starts with a declaration which language it's written in with `#lang` (or `module` equivalent). Then the actual code follows.

If you're using REPL, you don't need any of that:

```
$ racket
Welcome to Racket v8.3 [cs].
> (display "Hello World\n")
Hello World
> ^D
```

### Other builtin languages

`#lang` can be used to provide compatibility with other Lisp variants, but it's also used for Racket to be a platform for programming language research.

There's a lot of very spicy languages available ([documentation lists a few](https://docs.racket-lang.org/rash/index.html), but you can add your own). For example package `rash` (Racket comes with builtin package manages, you can install it with `raco pkg install rash`) is a weird Lisp-Shell hybrid:

```
#!/usr/bin/env racket
#lang rash

(define (print-upcase s)
  (display (string-upcase s)))

ls
ls *.rkt | wc -l
ls *.rkt |>> print-upcase
```

Prints this:

```
$ ./rash.rkt
hello.rkt	rash.rkt
       2
/USERS/TAW/100-LANGUAGES-SPEEDRUN/EPISODE-34-RACKET-SCHEME/HELLO.RKT
/USERS/TAW/100-LANGUAGES-SPEEDRUN/EPISODE-34-RACKET-SCHEME/RASH.RKT
```

It's a weird mix of Racket code, shell commands, shell pipelines, and pipelining from shell world into Racket world.

I'll stick to `#lang racket` for the rest of the episode.

### FizzBuzz

```
#!/usr/bin/env racket
#lang racket

; FizzBuzz
(define (fizzbuzz n)
  (cond ((zero? (remainder n 15)) "FizzBuzz")
        ((zero? (remainder n 5)) "Buzz")
        ((zero? (remainder n 3)) "Fizz")
        (else n)))

(define (fizzbuzz-loop start end)
  (for ([n (range start (+ 1 end))])
    (display (fizzbuzz n))
    (newline)))

(fizzbuzz-loop 1 30)
```

Which prints the obvious thing:

```
$ ./fizzbuzz.rkt
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
Fizz
19
Buzz
Fizz
22
23
Fizz
Buzz
26
Fizz
28
29
FizzBuzz
```

Let's go through it:

* it's parentheses everywhere, we're in a Lisp world - did you know that VSCode comes with builtin parentheses colorizer, and pretty much every editor has some kind of coloring plugins at least?
* `(define (function-name arguments) body)` defines a function
* `(cond (cond1 then-code1) (cond2 then-code-2) ... (else else-code))` is a way to do `if / else if / else` chain
* `(zero? (remainder n 15))` is a way to check if `n` is divisible by 15 - you could also do `(= 0 (remainder n 15))` if you prefer that style
* `(range a b)` is a range from `a` to `b-1`. Seriously, why do so many languages following this ridiculous convention? The usual explanation is that it's for iterating array indexes, but nobody iterates array indexes anyway, every reasonable language has `map` and such.
* `(for ([i collection]) code)` is a way to iterate over a collection - `for` and related forms have a lot of different options, we use only the simple one
* `(display s)` and `(newline)` print the string `s` and a newline character, respectively

### Unicode

Racket handles Unicode correctly - unlike Clojure which relies on JVM's broken Unicode support.

```
#!/usr/bin/env racket
#lang racket

(displayln (string-upcase "Żółw"))
(displayln (string-downcase "Żółw"))
(displayln (string-length "Żółw"))
(displayln (string-length "🍰"))
```

```
$ ./unicode.rkt
ŻÓŁW
żółw
4
1
```

### Macros

Racket has multiple macro systems - and then if that's not enough, then you can define your whole language and use `#lang` to do it.

Here's `define-syntax-rule` style macro, to implement `do-range` loops:

```
#!/usr/bin/env racket
#lang racket

(define (fizzbuzz n)
  (cond ((zero? (remainder n 15)) "FizzBuzz")
        ((zero? (remainder n 5)) "Buzz")
        ((zero? (remainder n 3)) "Fizz")
        (else n)))

(define-syntax-rule (do-range var start end . body)
  (let ([r (range start (+ 1 end))])
    (for ([var r]) . body)))

(do-range n 1 30
  (display (fizzbuzz n))
  (newline))
```

The mysterious `.` just indicates rest of the argument list. The rest should be fairly self-explanatory.

If you need other style of macros, like traditional `defmacro`, it's also there (but they strongly discourage that one).

### Network functions

Let's fetch an HTTP GET, parse JSON, and print the results:

```
#!/usr/bin/env racket
#lang racket

(require net/url)
(require json)

(define url "http://worldtimeapi.org/api/timezone/Asia/Tokyo")
(define response
  (call/input-url (string->url url)
                get-pure-port
                read-json))

(displayln (hash-ref response 'datetime))
```

It works as expected:

```
$ ./network.rkt
2021-12-27T02:19:07.009236+09:00
```

It's a bit more verbose than other languages (it's just `curl -s http://worldtimeapi.org/api/timezone/Asia/Tokyo | jq .datetime` in shell), but for a Lisp it's not too bad.

### Should you use Racket?

If you want to use Lisp, your top choices are either Racket or Clojure.

They take a different approach. Clojure runs on the JVM with all the benefits and tradeoffs that entails. Racket is standalone, so you don't get access to the JVM ecosystem, but then you're spared from all the painful JVM issues.

Clojure designed the whole language from scratch so it had a lot of flexibility, but it was forced to do a lot of weird choices for sake of better JVM interoperability. Racket started with Scheme and made modest changes to it.

They both target specific niche - Clojure running Lisp on JVM, Racket providing platform for creating your own programming language. If you don't care for either of these niches, and just want a usable modern Lisp, either of these is fine.

### Code

[All code examples for the series will be in this repository](https://github.com/taw/100-languages-speedrun).

[Code for the Racket Scheme episode is available here](https://github.com/taw/100-languages-speedrun/tree/master/episode-34-racket-scheme).