This guide is made to explain Racket in a nice, simple way. It’s aimed at people new to Lisp and it’s dialects, and assumes you have no previous programming background. All you need in order to follow this guide is a basic understanding of English and Racket’s main programming environment, DrRacket. You can find that on the main Racket website.
This guide isn’t complete. I’ll add more as I get the opportunity to.
Lisp (List Processing) is a programming language that was developed in the late ’50s. It contains a few unique concepts, such as making all data and code take the form of bracketed lists. The original Lisp isn’t used nowadays, but there are many dialects which take it’s place. Racket is derived from Scheme, one of Lisp’s dialects Racket itself was developed as a way to teach Scheme, as well as experiment with more features. It’s grown a lot since then, and is mainly used in web development and occasionally in making games. Because of it’s adaptibility it’s also used as a base for making new dialects.
Dictionary.com defines a list as "a series of names or other items written or printed together in a meaningful grouping or sequence so as to constitute a record" To put it simply, a list is a collection of references to a collection of things. For example, let’s use a shopping list. A shopping list is a list of all the things you need to buy.
In Racket, and any Lisp variant, lists are written in a pair of parentheses, with a space between them, like so
(Bread Milk Lightbulb Thread Pillow Hammer)
A list can also have another list inside it. Let’s say the list is subdivided into lists based off the shop you have to go for each thing.
In Racket, this would be shown as
((Bread Milk) (Lightbulb Hammer) (Thread Pillow))
This starts to get hard to follow, but luckily for us Racket ignores extra spaces and line breaks
We can have an empty set as well. An empty set contains no data. It might seem useless now but they can come in handy.
The S-expression is how Lisp and it’s variants consider data and instructions. It’s basically a list in parentheses, just like the examples above. Here’s an example of using a s-expression to store data
; Animals on a farm
Of course, there’s no standard for using these to store data. This is just as valid
((cow horse goat chicken duck)
(3 2 5 6 4))
When using a s-expression to store data you don’t need to follow any Lisp rules. As long as things are stored as bracketed lists it will work fine. The only limitation being that if you’re writing a program that reads and writes them you’re better off making them simple than have to deal with extra work.
Lisp code is made of s-expressions. Because of how Lisp and it’s variants work one rule is added for writing them. When the system looks at a bracketed list it assumes the first member is actually a command to execute, and the rest are extra pieces of data to be used in the list (referred to as arguments). For example
(buy Bread Milk)
Here we assume the buy command will buy everything defined to it. This command will buy bread and milk, because those were the arguments given to it. We can use this with math as well
(add 1 5 6)
Here we have a command that takes numbers and adds them. It then returns the answer. This would work in DrRacket, except for the fact that "add" isn’t a real command. It does understand the following though
(+ 1 5 6)
This works in Racket, but brings up one of the pitfalls of Lisp. Because of the fact that only the first element of a list is considered a command, the command has to be written first. This makes little sense with mathematical operators, and gets confusing with subtraction and relational operators
(3 + 4)
(+ 3 4)
(- 5 1)
(5 - 1)
(< 5 6)
(> 5 6)
(5 < 6)
So far we’ve looked at using numbers and "things" to represent data. Let’s cover that in more detail.
Strings, or words, refer to any text. Strings are usually denoted with double quotes. For example, "test" is a string which represents the word test. " " is also a string, which represents a space. "" is a string as well, which is empty. "5" is a string despite containing a number.
Numbers are numbers, there isn’t much to say. They’re digits without double quotes around them.
The reason there’s a distinction between strings and numbers is because there are commands that don’t make sense on certain types. For example, using a spell checker command on a number makes on sense, and trying to multiply words doesn’t work either. In other words, some commands only work on certain inputs.
Because of this it’s possible to convert between numbers and strings.
You might have noticed there’s a #f in there, and that brings us to the next datatype. A boolean is basically a true or a false. It’s a very simple type of data. #f represents false and #t represents true, although any command that takes a boolean for an input will assume that a non-boolean is true.
(string? "Is this a string?"
The main reason Lisp and it’s variants use S-expressions to represent data is because they can be nested inside themselves.
(+ 5 (+ 3 3 2))
(string? (number->string 45))
The idea of nesting s-expressions inside others is what makes Lisp such a powerful language. It’s also the key to writing Lisp code. Just make sure to use new lines to make code easier to read.
(+ 3 3 2))
When evaluating code like this the innermost s-expressions are solved before the rest.
This is the cool part. Now that we know how to piece together code we need to be able to name certain pieces of code. This is done using the define function. Define has two uses:
(define pi 3.14)
(define is-window-open #t)
(define name-of-cat "Sammy")
With these examples we see that define can take a name, and assign a value to it. This is very similar to algebra, and how variables are assigned values there. The first example takes the name pi and assigns it the value of 3.14. Second sets is-window-open to true. Third names a cat Sammy. The stored data is retrived by calling the name of the variable.
Notice how the variables aren’t enclosed in brackets. This is because variables don’t represent a list with a command in it. They are used in the same way their data type is used.
(define tree1 10)
(define tree2 14)
(define tree3 17)
(/ (+ tree1 tree2 tree3) 3)
-> 13 2/3
The other use of define is to define a set of actions.
(define (give-me-6) 6)
-> this is a procedure
In this example give-me-6 isn’t equal to 6, but calling it will give you a 6 anyway. This isn’t so useful though, because you can’t feed information to this function.
(define (square x)
(* x x))
This is a more useful function. It takes a number, which it internally refers to as x. It then multiplies x with itself. Of course, there’s no reason we couldn’t have one that takes two inputs.
(define (vehicle x y)
(string-append "I saw a " x " " y))
(vehicle "green" "car")
-> I saw a green car
(vehicle "white" "truck")
-> I saw a white truck
You can do this with as many inputs as you like.