Skip to content

Instantly share code, notes, and snippets.

@pwightman
Last active February 9, 2018 21:13
Show Gist options
  • Save pwightman/5357988 to your computer and use it in GitHub Desktop.
Save pwightman/5357988 to your computer and use it in GitHub Desktop.

How I Wish Racket Was First Explained To Me

I've been taking a compilers class this semester from Matt Might, which has been a great experience. Amongst the most challenging/interesting aspects of the course has been taming Racket, a Scheme-y/LISP-y language (I'll leave it at that).

Having never used anything functional/LISP-y in my days, this was a brand new experience. On the whole, it was good, but here's how I wish my first introduction to the language had gone as it would have set me on the right foot. While I'm focusing on Racket here, I imagine this same thing applies to LISP/Scheme and its derivatives.

Code vs. Data

I read everywhere that, "In Racket, code and data are the same thing." That sentence alone was useless to me, and it took a number of weeks before I "got it." Perhaps this explanation may have been more helpful:

What does the following snippet represent?

(foo bar)

It could be many things, but let's narrow it to two possiblities:

  1. A list of two elements: foo and bar.
  2. A method, foo, being passed the parameter bar.

So which is it? Well, it's both. It depends on what you, as the programmer, intend it to be! In Racket, you tell the computer whether it's "code" or "data" by using an apostrophe. Prepending an apostrophe means "data" and no apostrophe means "code." Example:

'(foo bar)

The above snippet tells Racket that this is data, more specifically a list of two elements: foo and bar. This:

(foo bar)

would try calling a function foo passing bar as a parameter. This would error out in vanilla Racket since foo and bar are not defined by default. So let's define them:

; Define a function, foo, taking one paramter. Print a simple message.
(define (foo arg)
  (display (string-append "Hi " arg)))

; Create variable bar, we'll just store the string "bar" for now.
(define bar "bar")

; Call function foo, passing bar
(foo bar)

This would print Hi bar. But never forget that the difference between code and data is an apostrophe away!

; This is evaluated as code by racket.
(define (foo arg)
  (display (string-append "Hi " arg)))

; (define my_list ...) is evaluated as code, but
; '(define (foo arg) ...) is the *exact* code from above, but
; since it has an apostrophe, is recognized as just a list of values, nothing more.
(define my_list '(define (foo arg)
                   (display (string-append "Hi " arg))))

; A function that loops through a list and prints each element on its own line.
(define (print-list l)
  (when (not (empty? l))
        (display (first l))
        (display "\n")
        (print-list (rest l))))

(print-list my_list)

The result of this program is the following output:

define
(foo arg)
(display (string-append Hi  arg))

my_list, at the top-level, is just a list with three elements (represented by the three lines you see in the snippet above), some of which were also lists. We can recurisively print all sub-lists to see every element on its own line.

; Recursively prints every element, including sub-lists
(define (recursive-print-list l)
  (when (not (empty? l))
    (define el (first l))
    (if (list? el)
        (recursive-print-list el)
        (begin
          (display el)
          (display "\n")))
    (recursive-print-list (rest l))))

Running (recursive-print-list my_list) would print:

define
foo
arg
display
string-append
Hi 
arg

Knowing that the difference between code and data is more semantic than syntactic, we can convert between the two willy-nilly. For example:

(define my_data '(display "I once was data, but no more"))

(eval my_data)

my_data is nothing more than a list of two elements. But Racket has an eval function that takes in data and interprets it as if it was code, which would print out I once was data, but no more. Pretty cool!

This is a mind-warp initially, but allows meta-programming techniques not possible in languages like C, C++, and Java. You often write programs that output "data" (lists containing data and lists) that is runnable code in a different program. My compilers class has essentially been a game of writing code that writes code that writes code that eventually spits out C-like code, which gcc can compile.

This is a core concept that somehow escaped me when initially learning Racket, so explanations of other Racket features only confused me when they built on this concept. Hopefully it helps somebody!

@atomkirk
Copy link

You're so much more open minded than me. When Dr. Racket wasn't an absolute XCode calibur experience for me, I wrote racket off. Same thing with eclipse and java :)

@pwightman
Copy link
Author

@atomkirk I hated Dr. Racket too, so I just used Vim. One of the many benefits of having a general-purpose editor like Vim as your steady, removes that barrier :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment