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.
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:
- A list of two elements:
foo
andbar
. - A method,
foo
, being passed the parameterbar
.
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!
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 :)