Glossary of Racket concepts
This glossary is still very much work in progress. Many entries are missing.
1 Introduction
The Racket documentation describes a lot, often very abstract concepts. For someone starting with the language, it’s often not clear which concepts are widely used and which aren’t. It’s quite easy to lose one’s way by getting distracted by relatively obscure concepts.
basic: These are basic concepts you should know to write Racket libraries and programs. If you’re starting to learn Racket, focus on these concepts.
Note that “basic” here doesn’t nessarily mean “trivial.” Don’t be discouraged if you don’t understand these glossary entries immediately. Experiment with the code examples or revisit the respective glossary entry later when you have more Racket experience.
intermediate: You can write most Racket software without these features, but you may need them depending on your problem. One example would be threads to execute different tasks concurrently.
advanced: Likely you won’t need these features, but they may improve your software. Look into these if you’re comfortable with the entries in the “basic” and “intermediate” categories.
Not all Racket users will agree on this categorization and the assigned levels for individual terms, but the categorization should give you a rough idea which concepts are more foundational than others.
This glossary isn’t a book for learning Racket, nor is it a reference. Some information may be simplified, for example some special cases may not be mentioned. If you want all the details, check out the Racket Reference.
2 Entries
Arity
Arity refers only to positional arguments, not keyword arguments.
Procedure in this glossary
Keywords and Arity in the Racket Reference
Assignment
Level: basic
However, in Racket and other functional languages, assignment is used much less than in imperative languages. The “normal” approach in functional languages is to transform immutable values to other immutable values.
To change a value via assignment, you need a name (binding) and a storage location for it. Usually, the binding and location are created with define, but they can also be created by one of the let forms.
Assignment: set! in the Racket Guide
Assignment: set! and set!-values in the Racket Reference
Binding
Note that the bound value is the result of the expression, not the expression itself.
Binding, Definition in this glossary
Definitions in the Racket Guide
Identifiers, Binding, and Scopes in the Racket Reference
Boolean
#f false
#t true
If a value is interpreted as a condition (as in if or cond forms), only the constant #f is interpreted as false, all other values are interpreted as true.
> (for ([value (list #t 1 0 "false" '() map (void) #f)]) (displayln (if value "true" "false")))
true
true
true
true
true
true
true
false
Booleans in the Racket Reference
Box
> (define (func value-box) (define old-value (unbox value-box)) (set-box! value-box (add1 old-value))) > (define a-box (box 7)) > (func a-box) > (displayln (unbox a-box)) 8
In Racket, using boxes is kind of awkward compared to passing arguments by reference in other languages. However, in practice this isn’t a problem since it’s unidiomatic in Racket to use mutable values. Instead you usually transform immutable values to other immutable values.
Boxes in the Racket Reference
Byte string
Call
Channel
Level: intermediate
Chaperone
Level: intermediate
Character
Class
Level: intermediate
Closure
> (define (make-incrementer increment) (lambda (value) (+ value increment))) > (define add3 (make-incrementer 3)) > (add3 5) 8
The return value of make-incrementer is the closure. The lambda expression doesn’t define the increment; the value is taken from the scope outside the lambda expression.
Let over lambda in this glossary
Closure Wikipedia article
Collection
Level: basic
Combinator
Level: intermediate
Comprehension
Type of the generated sequence. For example, for/list creates a list and for/vector creates a vector.
Parallel or nested iteration. This is only relevant if the form uses more than one input sequence. The for forms iterate in parallel; the for* forms iterate in a nested fashion.
; Parallel iteration creating a list.
> (for/list ([index (in-range 1 4)] [word '("one" "two" "three")]) (format "~a/~a" index word)) '("1/one" "2/two" "3/three")
; Nested iteration creating a vector. Note the `*` in `for*/vector`.
> (for*/vector ([color '("green" "red")] [fruit '("apple" "berry")]) (format "~a ~a" color fruit)) '#("green apple" "green berry" "red apple" "red berry")
There are a lot of sequences that can be iterated over, for example, strings (iterating over characters) or ports (iterating over characters, bytes or lines).
The “loop body” doesn’t have to be single expression. See the examples in the Racket Guide.
If sequences are iterated over in parallel, the shortest input sequence determines the elements used for the comprehension.
The for forms support several keyword arguments. For example, #:when makes it possible to include only certain elements in the result sequence.
Forms like for, for/fold or for/and may not be considered comprehensions because they don’t map input elements to output elements but only create a single element. But of course these forms can be useful, too.
Form, Sequence, stream, generator in this glossary
Iterations and Comprehensions in the Racket Guide
Iterations and Comprehensions: for, for/list, ... in the Racket Reference
Cons cell
Continuation
Level: advanced
Contract
Level: intermediate
Core form
Level: advanced
Currying
Custodian
Level: advanced
Debugging
Level: basic
Definition
By far the two most used ways to define something use define, but in different ways.
(define name expression)
(define (name arguments) body)
> (define (hello who) (displayln (string-append "Hello, " who "!"))) > (hello "Mike") Hello, Mike!
define-values is similar to the first define form above, but it can create several bindings at the same time in case a procedure returns more than one value.
define-syntax defines macros (forms). (But there are many more ways to define macros.)
define-check defines custom checks for automated tests.
define-runtime-path defines runtime paths.
define/public defines a public method for a class.
> (struct point (x y)) > point-x #<procedure:point-x>
> point-y #<procedure:point-y>
Assignment, Binding, Form, Let, Procedure, Struct, Values in this glossary
Definitions: define in the Racket Guide
Definitions: define, define-syntax, ... in the Racket Reference
Display
DrRacket
Level: basic
DSL (domain-specific language)
Level: advanced
Environment
Level: intermediate
Equality
Unless you need to distinguish between different number types, use = instead of the three functions described above.
The name makes it clear what’s compared, without looking at surrounding code.
Functions of this form provided by Racket check that the arguments have the correct type, e.g. string for string=?.
There’s no == function in Racket, but you could define a function with this name. (But probably you shouldn’t.)
Other languagesThe eq? function behaves like the is operator in Python.
Exact number
Executor
Level: advanced
Exception
Level: intermediate
Expression
; Literal values > 3 3
> "foo" "foo"
; A name bound to a value > (define foo 3) > foo 3
; Functions are just a special case of this. > + #<procedure:+>
> map #<procedure:map>
; Function application > (+ 2 3) 5
> (even? 6) #t
; Macro applications that evaluate to a value > (or #t #f) #t
> (if (> 5 2) "foo" "bar") "foo"
; Combinations > (* (+ 2 3) (- 7 3)) 20
> (map even? '(1 2 3)) '(#f #t #f)
A way to test if something is an expression is to feed it to a function that accepts an expression regardless of its type. If you don’t get an exception, the argument is an expression. We don’t care about the return value of the function for the test.
; Actual expressions > (number? 2) #t
> (number? "foo") #f
> (number? map) #f
> (number? (or #t #f)) #f
; No expressions > (number? if) eval:53:0: if: bad syntax
in: if
> (number? (define foo 3)) eval:54:0: define: not allowed in an expression context
in: (define foo 3)
; The expression itself raises an exception. > (number? (/ 1 0)) /: division by zero
; The expression returns multiple values. > (number? (values 3 4)) result arity mismatch;
expected number of values not received
expected: 1
received: 2
Note: Although many expressions don’t have a side effect, some do. Therefore, don’t evaluate expressions if they may have “dangerous” side effects, like deleting a file (unless that’s what you want, of course).
Evaluation Model in the Racket Reference
Field
Fixnum
Note that fixnums in various Scheme implementations or in Racket can’t store 32 or 64 bits. Some of the bits are used as flags that distinguish fixnums from other values. Even for the same Racket version, different platforms may have a different number of available fixnum bits.
Usually, you should use regular integers and consider the existence of fixnums an implementation detail. That said, using fixnum-specific operations can help with runtime optimizations. Then again, don’t optimize anything until you have profiled your software and therefore are sure that the specific integer calculation is an actual bottleneck.
Other languagesEspecially statically-typed languages often have integer types corresponding to 8, 16, 32 and 64 bits. However, in Racket you can’t expect that all bits of a machine integer (for example with 64 bits) are available for calculations (see above).
Number in this glossary
Fixnum and Flonum Optimizations in the Racket Guide
Fixnums in the Racket Reference
Flat contract
Level: advanced
Flonum
Number in this glossary
Fixnum and Flonum Optimizations in the Racket Guide
Flonums in the Racket Reference
Fold
Racket provides the foldl and foldr functions for this. In Scheme implementations, these functions may be called differently. Both fold variants take a function, an initial value and one or more lists. To keep things simple, let’s discuss the case of foldl with a single list argument first.
The provided function is first called with the first list item and the initial value and must return an accumulated value for these arguments. For each subsequent item from the input list the provided function is called again with the list item and the so-far accumulated value.
The difference between foldl and foldr is that for foldl the input list is processed from left to right and for foldr from right to left.
> (define (map-foldr func lst) (foldr (lambda (item current-list) (cons (func item) current-list)) '() lst)) > (map-foldr add1 '(1 2 3 4 5)) '(2 3 4 5 6)
> (define (filter-foldr pred? lst) (foldr (lambda (item current-list) (if (pred? item) (cons item current-list) current-list)) '() lst)) > (filter-foldr even? '(1 2 3 4 5)) '(2 4)
> (foldr (lambda (item1 item2 current-list) (cons (+ item1 item2) current-list)) '() '(1 2 3 4 5) '(1 3 5 7 9)) '(2 5 8 11 14)
> (define (map-for/fold func lst) (for/fold ([current-list '()]) ([item (reverse lst)]) (cons (func item) current-list))) > (map-for/fold add1 '(1 2 3 4 5)) '(2 3 4 5 6)
Comprehension in this glossary
Form
Level: basic
Formatting and output
Basics
> (display "Hello world!") Hello world!
A line break can be added with a following newline call. The function displayln combines display and newline, that is, it prints the string and then a line break.
All of these functions take an additional optional argument, which is the port the string or newline should be sent to. By default, this is standard output.
Formatting
> (format "Hello world!") "Hello world!"
> (format "Hello ~a!~n" "world") "Hello world!\n"
> (format "First line~nSecond line~n") "First line\nSecond line\n"
> (format "~a plus ~a is ~a" 2.5 3 5.5) "2.5 plus 3 is 5.5"
; ~x, ~o and ~b can only be used with exact numbers. > (format "decimal: ~a, hexadecimal: ~x, octal: ~o, binary: ~b" 12 -12 13/10 12/7) "decimal: 12, hexadecimal: -c, octal: 15/12, binary: 1100/111"
Note that using ~n is different from using the string "\n" in that ~n will result in the character combination "\r\n" on Windows.
There are two placeholders, ~v and ~s, which are similar to ~a, but behave differently for strings and compound data.
The placeholder ~v formats values as if they would be output in an interactive interpreter (REPL).
> (format "~v" 2) "2"
> (format "~v" 'abc) "'abc"
> (format "~v" "abc") "\"abc\""
> (format "~v" (list 1 2 3)) "'(1 2 3)"
> (format "~v" map) "#<procedure:map>"
On the other hand, the placeholder ~s is the counterpart of the read function, which converts a string of Racket code, usually to an atomic value or a list of symbols. The following examples use a helper function roundtrip that uses the ~v placeholder from the previous paragraph to show the data returned by the read function. The function open-input-string is explained in the Port glossary entry.
> (define (roundtrip str) (define read-data (read (open-input-string str))) (displayln (format "~v" read-data)) (format "~s" read-data)) > (roundtrip "2") 2
"2"
> (roundtrip "map") 'map
"map"
> (roundtrip "\"abc\"") "abc"
"\"abc\""
> (roundtrip "(+ 1 2)") '(+ 1 2)
"(+ 1 2)"
Note that the argument of roundtrip and the result from format in the helper function are the same.
Apart from using format, there’s another approach to format values, the functions ~a, ~v and ~s. There’s also a function ~r for detailed formatting of numbers. The first three functions can take multiple arguments, but the behavior may be surprising if used with keyword arguments for width and alignment (see below).
; Use ~a, ~v and ~s on different types of values. > (~a 2) "2"
> (~v 2) "2"
> (~s 2) "2"
> (~a "abc") "abc"
> (~v "abc") "\"abc\""
> (~s "abc") "\"abc\""
> (~a '(+ 2 3)) "(+ 2 3)"
> (~v '(+ 2 3)) "'(+ 2 3)"
> (~s '(+ 2 3)) "(+ 2 3)"
> (~a map) "#<procedure:map>"
> (~v map) "#<procedure:map>"
> (~s map) "#<procedure:map>"
; `~a` doesn't insert spaces on its own, so you need to add them. > (~a 2.5 " plus " 3 " is " 5.5) "2.5 plus 3 is 5.5"
; Use keywords for alignment. > (~a 1.23 #:width 10 #:align 'right) " 1.23"
; But keywords are _not_ applied to _each_ of the arguments. > (~a 1 2 3 #:width 10 #:align 'right) " 123"
; Number formatting. > (~r 12.3456 #:notation 'exponential #:precision 2 #:min-width 10) " 1.23e+01"
Output
So far, we’ve only used display and displayln to actually output something as we’d do it in a program. Other outputs in the examples were formatted strings in the examples’ REPL.
; `printf` is like `display` and `format`. > (display (format "~a and ~v" "foo" "foo")) foo and "foo"
> (printf "~a and ~v" "foo" "foo") foo and "foo"
; `print` is like `display` and `format` with ~v formatting. > (display (format "~v" '(1 2 3))) '(1 2 3)
> (display (~v '(1 2 3))) '(1 2 3)
> (print '(1 2 3)) '(1 2 3)
; `write` is like `display` and `format` with ~s formatting. > (display (format "~s" '(1 2 3))) (1 2 3)
> (display (~s '(1 2 3))) (1 2 3)
> (write '(1 2 3)) (1 2 3)
All of display, print, write and their variants with an ln suffix accept an optional port argument, so you can write the data to a file or network socket, for example. There’s also a function fprintf, which is like printf but takes a port as first argument.
Serialization and deserialization
If you want to save a data structure and restore it later, you can use serialize and write to save the data and read and deserialize to restore it.
Pretty-printing
> (define data '("This is an example line" "Here's another one" "And some nested data" '(1 2 3) "And some more text")) > (pretty-print data)
'("This is an example line"
"Here's another one"
"And some nested data"
'(1 2 3)
"And some more text")
> (define data '("foo" '(1 2 3) "bar")) > (pretty-print data) '("foo" '(1 2 3) "bar")
> (define data '("foo" '(1 2 3) "bar"))
> (parameterize ([pretty-print-columns 10]) (pretty-print data))
'("foo"
'(1 2 3)
"bar")
> (define data '("foo" '(1 2 3) "bar"))
> (parameterize ([pretty-print-columns 1]) (pretty-print data))
'("foo"
'(1
2
3)
"bar")
Summary
The following table summarizes some of the previous information:
Main purposes |
|
| Formatting function |
| Output functions | |
Human-readable output, formatted string output |
| ~a |
|
| ||
REPL, debugging |
| ~v |
|
| ||
Processing Racket code (usually used with read) |
| ~s |
|
| ||
Number formatting |
| – |
|
| – |
> (define first-name "Bilbo") > (define last-name "Baggins") > (displayln (format "~a ~a" first-name last-name)) Bilbo Baggins
> (displayln (~a first-name " " last-name)) Bilbo Baggins
> (printf "~a ~a~n" first-name last-name) Bilbo Baggins
If the placeholder handling in format and printf isn’t enough, you can get more control over the formatting with the ~a, ~v, ~s and ~r functions, for example to set a width or alignment.
Debugging, Parameter, Port, Stream, String, character, byte string in this glossary
Reading and Writing Racket Data in the Racket Guide
Reading, Writing, Additional String Functions, Serialization, Pretty Printing, The Reader, The Printer in the Racket Reference
Function
Functional programming (FP)
; The actual value of `condition` doesn't matter for the example. > (define condition #t) > (define number 0)
> (if condition (set! number 1) (set! number 2)) > number 1
A desired property of a function is that it’s “pure”, that is, it always returns the same result if it’s called with the same arguments, and the function doesn’t have any side effects (like I/O). That way, it’s easier to reason about and test the code. However, since a program that doesn’t have any effects on the outside world is useless, some side effects are needed. To still make the most of pure functions, try to put as much code as possible into pure functions and use them from the code with side effects.
> (define condition #t)
> (define my-function (if condition (lambda (x) (+ x 2)) (lambda (x) (- x 3)))) > (my-function 3) 5
Comprehension, Fold, Functional update, Higher-order function, Let in this glossary
Functional programming Wikipedia article
Functional update
; Create a hash table for imperative updates. > (define imperative-hash (make-hash '((1 . a) (2 . b)))) > imperative-hash '#hash((1 . a) (2 . b))
; Modify the hash by storing another pair in it. ; The exclamation point at the end of `hash-set!` means ; that the hash is modified in-place. > (hash-set! imperative-hash 3 'c) > imperative-hash '#hash((1 . a) (2 . b) (3 . c))
; Create a hash table for functional updates. > (define functional-hash (make-immutable-hash '((1 . a) (2 . b)))) > functional-hash '#hash((1 . a) (2 . b))
; Return a modified copy of the hash. > (define new-hash (hash-set functional-hash 3 'c)) ; The original hash is unchanged. > functional-hash '#hash((1 . a) (2 . b))
; The change is only visible in the new value. > new-hash '#hash((1 . a) (2 . b) (3 . c))
; Besides, immutable hashes don't allow imperative changes ; (as you probably expected). > (hash-set! functional-hash 4 'd) hash-set!: contract violation
expected: (and/c hash? (not/c immutable?))
given: '#hash((1 . a) (2 . b))
argument position: 1st
other arguments...:
4
'd
The concept of functional updates doesn’t only apply to hashes, but is very common in Functional Programming in general.
Binding, Functional programming (FP), Hash, Naming conventions in this glossary
Future
Level: advanced
Generator
Generic API
Level: advanced
GUI programming
Level: intermediate
Hash
> (define my-hash (hash #(1 2 3) '(a b c) '(1 2) #t #f map)) > (hash-ref my-hash #(1 2 3)) '(a b c)
> (hash-ref my-hash '(1 2)) #t
> (hash-ref my-hash #f) #<procedure:map>
Combination |
| Construction (1, 2) |
| Set or update value (3) |
| Get value |
equal?/immutable |
| (hash key1 value1 key2 value2 ...)
|
| (hash-set hash key value) |
| (hash-ref hash key) |
eq?/immutable |
| (hasheq key1 value1 key2 value2 ...)
|
| (hash-set hash key value) |
| (hash-ref hash key) |
equal?/mutable |
| (make-hash pair1 pair2 ...) |
| (hash-set! hash key value) |
| (hash-ref hash key) |
eq?/mutable |
| (make-hasheq pair1 pair2 ...) |
| (hash-set! hash key value) |
| (hash-ref hash key) |
(1) You can create empty hashes by calling the constructor without arguments. For example, (hash) creates an empty immutable hash with equal? key comparison.
(2) A pair here is a regular Scheme/Racket pair, for example (cons 1 'a). Pairs that contain only literals can also be written as '(1 . a).
(3) Setting or updating a value in an immutable hash may sound contradictory. The solution is that hash-set causes a so-called functional update. That is, it returns a new hash with the modification applied and leaves the hash argument unchanged. This is the same principle that cons or struct-copy use.
If a hash entry has a mutable key (for example a vector) don’t change the key in-place.
Don’t change a hash while iterating over it.
Collection, Equality, Functional update, List, Pair, Struct, Vector in this glossary
Hash Tables in the Racket Guide
Hash Tables in the Racket Reference
Higher-order function
map takes a function that transforms a list item to another item for the result list.
filter takes a predicate function that determines which list items should be included in the returned list.
sort takes a comparison function that determines the sort order of the returned list.
compose takes one or more functions and returns a function that applies the functions one after another.
Function, Functional programming (FP), Predicate, Procedure in this glossary
Hygiene
Level: intermediate
Identifier
> (define a-name 2) > a-name 2
> (define predicate/with/slashes? (lambda (v) (and (even? v) (> v 4)))) > predicate/with/slashes? #<procedure:predicate/with/slashes?>
> (define obscure-name-!$%^&*-_=+<.>/? "a string") > obscure-name-!$%^&*-_=+<.>/? "a string"
Binding, Form, Naming conventions in this glossary
Identifiers in the Racket Guide
Identity
; Obviously identical > (define list1 '(1 2 3)) > (eq? list1 list1) #t
; `list2` refers to the same list object. > (define list2 list1) > (eq? list1 list2) #t
; `list3` and `list2` are _equal_, but not identical. > (define list3 '(1 2 3)) > (eq? list1 list3) #f
; Changes in one vector are reflected in the "other" vector. > (define vector1 (vector 1 2 3)) > (define vector2 vector1) > (eq? vector1 vector2) #t
> (vector-set! vector1 0 4) > vector1 '#(4 2 3)
> vector2 '#(4 2 3)
; These vectors are equal, but not identical, so changes to one ; vector don't affect the other vector. > (define vector3 (vector 1 2 3)) > (define vector4 (vector 1 2 3)) > (eq? vector3 vector4) #f
> (vector-set! vector3 0 4) > vector3 '#(4 2 3)
> vector4 '#(1 2 3)
Impersonator
Level: advanced
Inexact number
Inspector
Level: advanced
Interface (API)
Level: basic
Interface (OOP)
Level: intermediate
Keyword
As an example, the following procedure takes a keyword argument to control whether an exclamation point should be added at the end of a greeting:
> (define (greeting name #:exclamation? exclamation?) (string-append "Hello " name (if exclamation? "!" ""))) > (greeting "Alice" #:exclamation? #f) "Hello Alice"
> (greeting "Alice" #:exclamation? #t) "Hello Alice!"
Like positional arguments, keyword arguments can be defined as optional:
> (define (greeting name #:exclamation? [exclamation? #f]) (string-append "Hello " name (if exclamation? "!" ""))) > (greeting "Bob") "Hello Bob"
It’s conventional to use the same name for the keyword and the argument name, but it’s not required.
> (define (greeting name #:exclamation? [exclamation-flag #f]) (string-append "Hello " name (if exclamation-flag "!" ""))) > (greeting "Mike") "Hello Mike"
> (greeting "Mike" #:exclamation? #t) "Hello Mike!"
Note that the call uses the keyword name (#:exclamation?), but the function body uses the argument name (exclamation-flag).
If a function allows several keyword arguments, they can be specified in any order, and they can be interleaved with positional arguments. For example, consider the function ~r in the Racket standard library, which is used for formatting numbers:
> (~r 1.2345) "1.2345"
> (~r 1.2345 #:precision 2) "1.23"
> (~r #:precision 2 1.2345) "1.23"
> (~r #:precision 2 1.2345 #:min-width 10) " 1.23"
That said, usually the code is easier to read if all the positional arguments occur before all the keyword arguments:
> (~r 1.2345 #:precision 2 #:min-width 10) " 1.23"
Racket clearly distinguishes between positional and keyword arguments. You can’t use a positional arguments in place of a keyword argument and vice versa:
> (define (greeting name #:exclamation? exclamation?) (string-append "Hello " name (if exclamation? "!" ""))) > (greeting "Bob" #:exclamation? #t) "Hello Bob!"
> (greeting "Bob" #t) greeting: arity mismatch;
the expected number of arguments does not match the given
number
expected: 1 plus an argument with keyword #:exclamation?
given: 2
arguments...:
"Bob"
#t
> (greeting #:name "Bob" #:exclamation? #t) application: procedure does not expect an argument with
given keyword
procedure: greeting
given keyword: #:name
arguments...:
#:exclamation? #t
#:name "Bob"
Other languagesUnlike Racket, many languages don’t have a clear distinction between positional and keyword arguments. For example, the following Python code is valid:
def greeting(name, exclamation):
suffix = "!" if exclamation else ""
return f"Hello {name}{suffix}"
greeting("Bob", exclamation=True)
Procedure in this glossary
Keyword Arguments in the Racket Guide
Keywords in the Racket Reference
Lambda
> (lambda ([name "world"]) (string-append "Hello " name "!")) #<procedure:eval:189:0>
> ((lambda ([name "world"]) (string-append "Hello " name "!")) "Alice") "Hello Alice!"
The second example above defines and directly calls the function.
The above examples are a bit artifical. Normally, you use a function defined with lambda as a function argument for a Higher-order function or in a Let expression.
Definition, Higher-order function, Let in this glossary
Functions: lambda in the Racket Guide
Procedure Expressions: lambda and case-lambda in the Racket Reference
Lang (as in ‘#lang‘)
Level: advanced
Language-oriented programming
Level: advanced
Let
; These `even?` and `odd?` functions are only valid for n >= 0
> (letrec ([even? (lambda (n) (if (= n 0) #t (odd? (sub1 n))))] [odd? (lambda (n) (if (= n 0) #f (even? (sub1 n))))]) (even? 6)) #t
There’s also a “named let” form which uses the name let, but has the list of bindings prefixed with a name. Essentially, this form defines a – usually recursive – function and calls it. The result of the named let expression is the return value of the function call that started the recursion.
let creates “parallel” bindings, i.e. the bindings can’t refer to each other.
let* creates nested bindings, i.e. later bindings can refer to earlier bindings.
letrec creates bindings where all bindings can refer to all others (even themselves).
A named let defines a function and calls it. The bindings specify the arguments for the initial call.
Racket has a few more let forms, in particular for handling multiple values. See the below sections in the Racket Guide and the Racket Reference.
Local Binding in the Racket Guide
Local Binding: let, let*, letrec, ... in the Racket Reference
Let over lambda
Here, let creates the outer environment whereas lambda defines the function using that environment.
Environment, Procedure, Lambda, Closure in this glossary
List
Scheme and Racket lists are implemented as singly-linked lists (with the exception of the empty list, which is an atomic value). Singly-linked lists consist of pairs where the first value of a pair is a list item and the second value of the pair points to the next pair. The end of the list is denoted by an empty list as the second value of the last pair.
For example, the list '(1 2 3 4) can be drawn as
This data structure looks much more complicated than an array, where items are stored in adjacent memory locations. A singly-linked list also has the disadvantage that accessing list items by index requires traversing the list until the pair with the given index is found.
or, in one picture,
So each of the lists looks as you would expect, but it’s not necessary to make any copies of the list data. Instead the lists share some data, without this being visible to code that uses these lists. (An exception is if list items are changeable values, e.g. vectors, and are actually changed in-place. In that case, all lists that share this changed data “see” the change. But usually you should avoid mutating data, partly for this reason.)
Changing lists with cons maximizes the data that can be shared between lists. The situation is different if you change data “further down” the list. In this case, it may be necessary to copy a part of the list data.
Other languagesNote that Scheme/Racket lists are different from “lists” in many other programming languages. Typically those lists store data in adjacent memory locations and have a fast append operation. Racket lists, on the other hand, are singly-linked lists and have a fast prepend operation (cons), but appending items is rather slow because it has to iterate through the whole list and make a copy. The closest equivalent in Racket to the above-mentioned lists in some languages are called growable vectors (see gvector). However, Racket lists are the idiomatic approach for most list processing, so use them if you can.
Collection, Functional update, Pair in this glossary
Pairs and Lists in the Racket Guide
Pairs and Lists in the Racket Reference
Location
> (define (change-argument arg) (displayln arg) (set! arg 5) (displayln arg)) > (change-argument 3)
3
5
> (let ([v (vector 1 2 3)]) (displayln v) (vector-set! v 2 5) (displayln v))
#(1 2 3)
#(1 2 5)
However, usually you should avoid mutation in functional programming.
Macro
Match
Level: intermediate
Match transformer
Level: advanced
Method
Level: intermediate
Module
Level: basic
Named let
Namespace
Level: intermediate
Naming conventions
Parts of identifiers are separated by hypens (kebap case, e.g. vector-length). Snake case (vector_length) or camel case (vectorLength) are not used. Sometimes module-level constants are in all uppercase, for example (define GRID-ROW-COUNT 10). Other names are all lowercase.
Abbreviations are rare; almost everything is “spelled out.” This can result in rather long names like make-input-port/read-to-peek or call-with-default-reading-parameterization. Most names turn out reasonably short, though.
If a name describes a type and an operation on it, the type usually goes first, as in vector-ref or string-copy. The accessor functions generated by struct also follow this pattern.
Pattern |
| Meaning |
| Examples |
name? |
| Predicate (1) |
| |
name=? |
| Comparison predicate (1) |
| |
name/c |
| Contract predicate (1) |
| or/c, one-of/c |
name! |
| Mutation |
| |
name* |
| Repetition |
| |
name* |
| Nesting |
| |
name* |
| Other variant |
| |
name% |
| Class name |
| frame% |
part/part |
| “for” or “with” |
| call/cc, define/match, |
type->other-type |
| Conversion |
| |
make-type |
| Create new value of type |
|
(1) Since the ? or /c suffix already identifies a predicate, using prefixes like “is-”, “has-” etc. is redundant.
Contract, Functional update, Predicate, Struct in this glossary
Racket Style Guide for other Racket coding conventions
Number
integer
rational
real
complex
“Technical” number types
An “integer” is an integer number with arbitrarily many digits. Examples: -123, 0, 2, 12345, 26525285981219105863630848.
A “rational” number consists of an integer nominator and a positive integer denominator. Examples: -41/4, -2/3, 5854679515581645/4503599627370496.
A “float” number is an (IEEE 754) floating point number. Examples: -12.34 (=-1.234e1=0.1234e2 etc.), 0.0, 0.0123, 2.0. Float numbers also include the special values -inf.0 (–∞), +inf.0 (+∞), +nan.0 (–nan) and +nan.0 (+nan).
A “complex” number has a real and an imaginary part. If one part is a float value, the other part must also be a float value. Otherwise the real and imaginary parts can be integer or rational numbers. Examples: 1+2i, 2+3/4i, 1.0+2i (immediately converted to 1.0+2.0i).
Integer and rational numbers are called exact because they can use arbitrarily many digits for calculations and therefore aren’t affected by rounding errors (as long as operations involve only exact values, of course). Float values are called inexact because many float operations lead to rounding errors.
A complex number is exact if both the real and the imaginary part are exact, otherwise the complex number is inexact.
The following diagram shows the relationships between the technical types.
“Mathematical” number types
; 2.0 is the same as the integer 2. > (integer? 2.0) #t
; 1.23 is mathematically the same as 123/100, which is a ; rational (and a real) number. > (rational? 1.23) #t
> (real? 1.23) #t
; However, special values like `+inf.0` are `real?`, but not ; `rational?`. > (rational? +inf.0) #f
> (real? +inf.0) #t
; All integer and real values are also complex values. > (complex? 2) #t
> (complex? 2.0) #t
> (complex? 1.23) #t
> (complex? +inf.0) #t
; Numbers with a non-zero imaginary part are obviously ; complex values. > (complex? 1+2i) #t
Type notions combined
In Racket discussions, “integer” is usually understood as the technical type, i.e. an exact integer. Use “exact integer” as an exact wording. ;-) For the technical floating point type the term “float” avoids ambiguities with the mathematical “real” type.
You can express the technical types in terms of the mathematical predicates if necessary (v means “value”):
Technical type |
| Predicate combination |
integer |
| (and (exact? v) (integer? v)) = (exact-integer? v) |
rational |
| |
float |
| (and (inexact? v) (real? v)) = (inexact-real? v) |
complex |
| |
exact complex |
| |
inexact complex |
|
Tips and gotchas
Use exact integers for counts, or indices for lists and vectors. Actually, there’s a special predicate exact-nonnegative-integer? to check for numbers that can be used as an index.
Use floats for physical properties like lengths, times and so on.
Use floats for values that come from measurements or non-trivial calculations, especially if they involve physical properties. Such numbers are bound to be inexact.
Use fractions if you want exact calculations, but exact integers aren’t enough. Note, however, that many non-basic operations return an inexact result even for most exact arguments. For example, Racket evaluates (sin 1) to 0.8414709848078965.
Related to the previous point, it’s not always obvious when a Racket function will return an exact or inexact result. For example, (sin 1), as shown above, is inexact, but (sin 0) gives an exact 0.
- When using floats, beware of rounding errors, including overflow and underflow. “Overflow” means that a result can’t be represented as a “normal” float and becomes +∞ or –∞. “Underflow” means that a result evaluates to 0.0, even if it wouldn’t be zero in terms of an exact mathematical calculation.
- The behavior of division by zero differs depending on whether the denominator is exact or inexact.Examples:Usually it’s better not to rely on a particular behavior for division by zero. Instead test the denominator and handle zero values explicitly.
You can use the prefixes #e and #i to evaluate literal numbers to exact and inexact values, respectively. For example, #e1e3 is exact 1000 and #i5 is inexact 5.0.
To ensure that a value is exact or inexact, you can use inexact->exact and exact->inexact, respectively. Despite the names of these functions, you can feed them any number. For example, (inexact->exact 1) just returns an exact 1.
When reading numbers from a text file, you can convert them to actual numbers with string->number. The function handles the same syntaxes that you can use in Racket code.
- In addition to the previous tip, convert input numbers to the types/exactness they’re supposed to have. Assume you have the following data, where each row represents a set of input data to the same calculation:
1 2 3
4 5.6 7
Since all inputs in the first row are exact, the calculation may give 90354914438/1636837392063 whereas the data from the second row may give 6.468137108187422.If the second column is supposed to contain float values, you should ensure this with exact->inexact. In some situations, you may want to signal an error instead of converting a number. For example, if a number is supposed to be an integer (for example a count), you proabably shouldn’t accept a float instead.
Fixnum, Flonum, Numeric tower in this glossary
Numbers in the Racket Guide
Numbers in the Racket Reference
Numeric tower
For example, for every number for which rational? gives #t, real? and complex? also give #t.
You could add “number” as the bottom of the tower, but at least in Racket, number? is equivalent to complex?.
Number in this glossary
Numerical tower Wikipedia article
Opaque
Package
Level: intermediate
Pair
> (cons 1 "one") '(1 . "one")
List in this glossary
Pairs and Lists in the Racket Guide
Pairs and Lists in the Racket Reference
Parameter
; The `make-parameter` argument is the initial value. > (define new-parameter (make-parameter 3)) > (new-parameter) 3
> (new-parameter 4) > (new-parameter) 4
> (new-parameter) 4
> (parameterize ([new-parameter 5]) (new-parameter)) 5
> (new-parameter) 4
Thread in this glossary
Dynamic Binding: parameterize in the Racket Guide
Parameters in the Racket Reference
Partial application and currying
The following example defines a function draw-line and a curried version draw-line-from-origin, which hard-codes the from-x and from-y arguments:
> (define (draw-line from-x from-y to-x to-y) ; Keep it simple; just print a string instead of drawing a line. (displayln (format "Drawing a line from (~a, ~a) to (~a, ~a)" from-x from-y to-x to-y))) > (draw-line 1 2 3 4) Drawing a line from (1, 2) to (3, 4)
; Hard-code the first two arguments of `draw-line`.
> (define (draw-line-from-origin to-x to-y) (draw-line 0 0 to-x to-y)) > (draw-line-from-origin 2 3) Drawing a line from (0, 0) to (2, 3)
Currying is similar, but it hard-codes only one argument and, as long as the arguments aren’t complete, returns a function that also takes only one argument.
> (curry draw-line 1) #<procedure:curried:draw-line>
> ((curry draw-line 1) 2) #<procedure:curried:draw-line>
> (((curry draw-line 1) 2) 3) #<procedure:curried:draw-line>
> ((((curry draw-line 1) 2) 3) 4) Drawing a line from (1, 2) to (3, 4)
> (curry draw-line 1) #<procedure:curried:draw-line>
> ((curry draw-line 1) 2) #<procedure:curried:draw-line>
> (((curry draw-line 1) 2) 3 4) Drawing a line from (1, 2) to (3, 4)
Note that the last call passes two arguments, 3 and 4.
There’s also a function curryr, which hard-codes arguments from the right instead of the left of the argument list.
Other languagesUnlike Haskell, Scheme/Racket doesn’t have implicit currying, so the following code raises an exception:
> (define draw-line-from-origin (draw-line 0 0)) draw-line: arity mismatch;
the expected number of arguments does not match the given
number
expected: 4
given: 2
Pattern (in regular expressions)
Level: basic
Pattern (in macro definitions)
Level: intermediate
Phase
Level: advanced
Place
Level: advanced
Polymorphism
Level: intermediate
Port
Basics
A port represents an input or output channel; ports correspond to file handles or file objects in other programming languages.
Create a port for input or output.
Read data from the port or write data to the port, depending on whether the port was created for input or output. You can read or write data as strings, characters or bytes or a mixture of them, regardless of how you created the port.
Close the port.
The ports for standard input, standard output or standard error already exist on program start, so there’s no need to create them. Also, you shouldn’t close any of these ports.
How do you use these special ports? Normally, you don’t need to specify them since they’re the default port arguments of functions like display or read-line.
Here are a few useful applications of the above three steps:
- To read data from a file in one go, you can use
(define input-port (open-input-port "data.txt")) (define content (port->string input-port)) (close-input-port) - If the file is large and therefore you want to read line by line, use
(define input-port (open-input-port "data.txt")) (for [line (in-lines input-port)] (work-with line)) (close-input-port) - To write a string to a file, use
(define output-port (open-output-port "data.txt")) (display content output-port) (close-output-port)
The examples above describe only the basic use cases. Racket has many more functions to read and write data as strings, characters or bytes. More on that below.
(call-with-input-file* "data.txt" (lambda (input-port) (port->string input-port)))
Port types
File ports. Created by functions like open-input-file and open-output-file.
String ports. Created by open-input-string and open-output-string. They’re useful if an API requires a port, but you already have the data in memory (instead of, say, a file). You can read the data accumulated in the output string with get-output-string.
TCP connections. Used for network connections. For example, tcp-connect connects to a host and port and returns an input port and an output port.
Process pipes. For example, the subprocess function can create ports for communication with its launched process.
You might also see the term “file stream ports.” It refers to both file ports and process pipes.
Port parameters
Racket provides the parameters current-input-port, current-output-port and current-error-port. At program start, these parameters correspond to standard input, standard output and standard error, respectively.
(parameterize ([current-output-port (open-output-port "data.txt")]) (displayln "This goes to the file."))
Function overview
Here are some Racket functions that work with input and output ports.
Creating ports
Port type |
| Input |
| Output |
File |
|
| ||
| ||||
|
| |||
|
| |||
String port |
|
| ||
TCP connection |
| tcp-connect (5) | ||
| tcp-accept (6) | |||
Process pipe |
| subprocess (7) |
(1) The created port is passed to the proc argument. After executing proc, the file is closed, even if there was an exception in the proc.
(2) The created port is installed as current-input-port. After executing the thunk argument, the file is closed, even if there was an exception in the thunk.
(3) The created port is installed as current-output-port. After executing the thunk argument, the file is closed, even if there was an exception in the thunk.
(4) The current string value can be queried with get-output-string.
(5) For network clients. Returns an input port and an output port as two values.
(6) For network servers. Returns an input port and an output port as two values. Used with tcp-listen.
(7) If the arguments stdin, stdout and stderr are passed as #f, subprocess creates and returns new corresponding input and output ports.
Reading from and writing to ports
Data type |
| Input |
| Output |
String |
|
| ||
|
| |||
| in-lines (1) |
| ||
| port->string |
| ||
| port->lines |
| ||
Char |
|
| ||
Byte string |
|
| ||
| in-bytes-lines (2) |
| ||
Byte |
|
|
(1) Generates a sequence of strings (see Comprehension)
(2) Generates a sequence of byte strings (see Comprehension)
Closing ports
Input |
| Output |
|
Tips and gotchas
You can read strings, chars, byte strings and bytes, but most of the time, you want to process the contents as strings.
If you’re sure that a file is small, you can use port->string or port->lines to read it in one go. If the file may be larger, read and process it line by line so you don’t need to keep the whole content in memory.
If an input port runs out of data, read operations return the special value eof, which you can check with eof-object?.
If you try to open a file for writing and the file already exists, you’ll get an exception. Use the #:exists keyword to deal with this situation.
The #:exists values ’truncate and ’update seem very similar. However, there’s an important difference: ’truncate’ removes all data from the existing file. On the other hand, ’update overwrites existing content in the file, but leaves everything outside the new content intact. Hence you could accidentally keep data you thought to be overwritten. So if in doubt, use ’truncate.
Functions like with-input-from-file and with-output-to-file implicitly close the ports they create, even in case of an exception. Therefore, such functions are more robust and hence preferable over explicit calls of close-input-port and close-output-port.
Usually, you don’t want to close current-output-port or current-error-port if they refer to a terminal. You can use the predicates output-port? and terminal-port? to check for this situation.
You can use copy-port to copy data from one port to another.
This glossary entry lists a lot of APIs, but they’re still only a small part of the existing port-related APIs. So if you miss something, check the Racket Reference.
Formatting and output, Parameter, Stream, String, character, byte string, Thunk, Values in this glossary
Input and Output in the Racket Guide
Input and Output, Operating System in the Racket Reference
Predicate
Checking for a type: procedure?, string?, symbol?, ...
Checking properties: even?, immutable?, char-lower-case?, ...
Others: directory-exists?, file-exists?, ...
Lambda, Partial application and currying, Procedure in this glossary
Procedure
> (add1 2) 3
If a function should be called without arguments, it’s written (func). Since the parentheses cause the function call, you can’t just add brackets around expressions. For example,
> ((add1 2)) application: not a procedure;
expected a procedure that can be applied to arguments
given: 3
calculates 3 through the function call (add1 2) and tries to call the result 3 as a function, (3), which gives an error saying that 3 isn’t callable.
On the other hand, when using a function in another position, it’s just a “passive” value:
> add1 #<procedure:add1>
> (procedure? add1) #t
> (list add1 add1) '(#<procedure:add1> #<procedure:add1>)
As using too many brackets, using too few can lead to problems, although they tend to be less obvious:
> (define (foo) add1 2) > (foo) 2
You might have expected 3 as the result of calling foo. However, since the parentheses around add1 2 were missing and the result of a function is the value of the last expression, the function returned 2.
Depending on how a function is defined, it can take zero arguments to an almost unlimited number of arguments (usually done with a special syntax in the function definition).
The following function takes an optional argument and returns a string to greet someone:
> (define (greeting [name "world"]) (string-append "Hello " name "!")) > (greeting) "Hello world!"
> (greeting "Bob") "Hello Bob!"
Simple Definitions and Expressions in the Racket Guide
Definitions: define, define-syntax, ... in the Racket Reference
Profiling
Level: intermediate
Prompt
Level: advanced
Provide
Quasiquote and unquote
> '(+ 3 4) '(+ 3 4)
> `(+ 3 4) '(+ 3 4)
> 'map 'map
> `map 'map
> `(1 2 ,(+ 1 1 1)) '(1 2 3)
Quote in this glossary
quasiquote and unquote in the Racket Reference
Quote
> '123 123
> '"foo" "foo"
> '#\space #\space
> '#t #t
> 'truncate 'truncate
> '(1 2 3) '(1 2 3)
> '("foo" "bar" "baz") '("foo" "bar" "baz")
> (list 1 2 'foo) '(1 2 foo)
> '(1 2 'foo) '(1 2 'foo)
Although the expression ''foo looks like foo “quoted twice,” it’s more complicated.
If you see an expression starting with two apostrophes, it’s most likely a bug.
Procedure, Quasiquote and unquote, Symbol in this glossary
Quoting: quote and ’ in the Racket Guide
Literals: quote and #%datum in the Racket Reference
RnRS (as in R5RS, R6RS etc.)
Level: intermediate
Raco
Level: basic
Reader (for parsing code)
Level: advanced
Record
Require
Rule (in macros; probably other uses, which ones?)
Level: intermediate
Safe operation
Scheme
Level: basic
Scope
Level: basic
Scribble
Level: intermediate
Sequence, stream, generator
Sequences
> (for/list ([item '(1 2 3)]) item) '(1 2 3)
> (for/list ([(key value) (hash 'a 1 'b 2)]) (cons key value)) '((b . 2) (a . 1))
; Limit the iterated-over values with a sequence of limited length, ; otherwise `for/list` would never finish.
> (for/list ([unused '(a b c d e)] [item (in-naturals)]) item) '(0 1 2 3 4)
> (sequence? '(1 2 3)) #t
> (sequence? (hash 'a 1 'b 2)) #t
> (sequence? (in-range 2 5)) #t
> (sequence? (in-naturals)) #t
; An integer n is equivalent to `(in-range n)` > (sequence? 5) #t
A useful feature of sequences as used in for forms, is that they’re evaluated on demand (“lazily”). This is especially visible in in-naturals, which would never finish if it was fully evaluated and then iterated over. (The iteration would never happen because the evaluation would take infinite time.)
Streams
In case you want to implement a lazy or even infinite sequence, the easiest approach often is a stream.
List operation |
| Stream operation |
| Purpose of stream operation |
|
| Create stream from the given items (which aren’t evaluated by default) | ||
|
| Create empty stream | ||
|
| Check if stream is empty | ||
|
| Create stream from first item and another stream | ||
|
| Evaluate and return first item of stream | ||
|
| Return stream with all items but the first | ||
| stream-map, stream-filter, ... |
| Higher-level operations (not all list operations have a corresponding stream operation) |
> (define a-stream (stream (begin (displayln "one") 1) (begin (displayln "two") 2) (begin (displayln "three") 3))) ; Implicit evaluation by `stream-first` > (stream-first a-stream) one
1
; No evaluation by `stream-rest` > (stream-rest a-stream) #<stream>
; The result of an evaluation is cached, so this call ; does _not_ display "one". > (stream-first a-stream) 1
> (define (squares-list) (let loop ([i 0]) (if (> i 10) '() (cons (* i i) (loop (add1 i)))))) > (squares-list) '(0 1 4 9 16 25 36 49 64 81 100)
> (define (squares-stream) (let loop ([i 0]) (if (> i 10) empty-stream (stream-cons (* i i) (loop (add1 i)))))) > (squares-stream) #<stream>
> (stream->list (squares-stream)) '(0 1 4 9 16 25 36 49 64 81 100)
> (define (squares-stream) (let loop ([i 0]) (stream-cons (* i i) (loop (add1 i))))) > (squares-stream) #<stream>
; Since the stream is infinite, use `stream-take` to limit ; the evaluation to the first 11 items. > (stream->list (stream-take (squares-stream) 11)) '(0 1 4 9 16 25 36 49 64 81 100)
> (sequence? (squares-stream)) #t
; Use a parallel `in-range` to limit the generated items.
> (for/list ([unused (in-range 11)] [item (squares-stream)]) item) '(0 1 4 9 16 25 36 49 64 81 100)
Generators
> (define a-generator (generator () (yield 1) (yield 2) (yield 3))) ; Three items from the `yield`s. > (a-generator) 1
> (a-generator) 2
> (a-generator) 3
; After that, the generator yields `(values)`. > (a-generator) > (a-generator) ; This can be checked with `call-with-values`. > (call-with-values a-generator list) '()
> (define a-generator (generator () (yield 1) (yield 2) (yield 3) ; Stop signal #f))
> (for/list ([item (in-producer a-generator #f)]) item) '(1 2 3)
> (define squares-generator (generator () (for ([i (in-naturals)]) (yield (* i i))))) > (sequence? (in-producer squares-generator #f)) #t
; Use a parallel `in-range` to limit the generated items.
> (for/list ([unused (in-range 11)] ; No stop value since we want an infinite sequence [item (in-producer squares-generator)]) item) '(0 1 4 9 16 25 36 49 64 81 100)
Other languages
Some Scheme implementations also have stream and/or sequence concepts, but the APIs and semantics may differ from Racket’s.
Racket generators are similar to Python’s generator functions, but there are a few differences. See the section Generators in the Racket Reference for details.
Comprehension, Form, Hash, Lambda, Values in this glossary
Sequence Constructors in the Racket Guide
Sequences and Streams in the Racket Reference
Structure and interpretation of computer programs, section “Streams”
Set
Level: intermediate
Shadowing
Splicing
Level: basic
SRFI
Level: intermediate
Standard library
Stream
String, character, byte string
Strings and characters
; String content enclosed in quotes > "a string" "a string"
> (string? "a string") #t
; A quote inside a string literal can be escaped with a backslash. > "a string \"with\" quotes" "a string \"with\" quotes"
> (string-length "\"foo\"") 5
; Use `display` or `displayln` to output a string for human readers. ; `displayln` adds a newline. > (displayln "a string \"with\" quotes") a string "with" quotes
; Concatenate strings with `string-append`. > (string-append "Hello " "world" "!") "Hello world!"
; Strings can contain non-ASCII characters. > "Michael Müller" "Michael Müller"
> (string-length "Michael Müller") 14
; You can get individual characters with `string-ref`. > (string-ref "Müller" 1) #\ü
> (char? #\ü) #t
; Convert a string to a character list and back. > (string->list "Müller") '(#\M #\ü #\l #\l #\e #\r)
> (list->string '(#\M #\ü #\l #\l #\e #\r)) "Müller"
Byte strings, bytes and encodings
; Not the only encoding function in the standard library > (define encoded-string (string->bytes/utf-8 "Müller")) ; ü has been encoded to two bytes. > encoded-string #"M\303\274ller"
> (bytes? encoded-string) #t
; Not the only decoding function in the standard library > (bytes->string/utf-8 encoded-string) "Müller"
Fortunately, as long as the content is in UTF-8 encoding, you don’t need to encode or decode it yourself. For example, file->string decodes the file contents implicitly.
> (bytes->list encoded-string) '(77 195 188 108 108 101 114)
Type relationships
The following diagram shows the relationships between the types:
Both strings and byte strings come in mutable and immutable versions. Literals, e.g. "foo" or #"foo", are immutable.
String functions
Caveats
The term “character” is ambiguous in the unicode context. It can mean a code point (as in Racket), but also a symbol on the screen that’s perceived as self-contained. The second meaning is sometimes called a “grapheme“ or “grapheme cluster.”
- The distinction between the meanings of “character” are important because not all grapheme clusters can be expressed in a single code point. For example, the German flag 🇩🇪 consists of two code points:
> (string->list "🇩🇪") '(#\🇩 #\🇪)
In some cases, the same grapheme cluster can be expressed in different code point combinations. For example, the string "ü" from above can be expressed in a single code point, but alternatively in two code points, where the first is for the letter "u" and the second for the diacritic (the dots above the u). So two strings that look the same on the screen may not be the same according to Racket’s string=? function, which works on code point sequences.
The normalization functions can convert strings so that they have the “combined” or the “separate” code points and can be meaningfully compared with string=?.
Formatting and output, Port in this glossary
Strings (Unicode), Bytes and Byte Strings in the Racket Guide
Strings, Byte Strings, Characters, Encodings and Locales in the Racket Reference
Struct
> (struct person (name age))
Here, person is the name of the struct and name and age are the fields of the struct.
Creating a struct like above creates several bindings. One is for the name of the struct, which can be used to create instances of the struct:
> (define bilbo (person "Bilbo Baggins" 111)) > bilbo #<person>
Moreover, each field results in a binding named struct-name-field-name to access the value of each field in a given struct instance:
> (person-name bilbo) "Bilbo Baggins"
> (person-age bilbo) 111
It’s also possible to declare a struct as mutable by adding a #:mutable keyword. This creates additional accessor functions to set values in a struct instance. However, you usually should avoid mutation in Racket. Instead, use struct-copy to create a new struct instance based on another instance:
> (define younger-bilbo (struct-copy person bilbo [age 100])) > (person-name younger-bilbo) "Bilbo Baggins"
> (person-age younger-bilbo) 100
See also Functional update. You can use more than one field name/value pair.
By default, structs are created as opaque. This means that printing a struct instance doesn’t show the values. More important is the gotcha that comparing opaque struct instances with equal? compares by identity (like eq?), not by field values!
> (define bilbo2 (person "Bilbo Baggins" 111)) > (equal? (person-name bilbo) (person-name bilbo2)) #t
> (equal? (person-age bilbo) (person-age bilbo2)) #t
> (equal? bilbo bilbo2) #f
Adding the #:transparent keyword to the struct definition avoids this problem:
> (struct person (name age) #:transparent) > (define frodo (person "Frodo Baggins" 33)) > frodo (person "Frodo Baggins" 33)
> (define frodo2 (person "Frodo Baggins" 33)) > (equal? (person-name frodo) (person-name frodo2)) #t
> (equal? (person-age frodo) (person-age frodo2)) #t
> (equal? frodo frodo2) #t
Printing frodo shows the struct values, not just #<person>.
Comparing two struct instances of the same transparent struct type with the same values with equal? gives #t.
The latter is not only more intuitive, but it’s also very useful when comparing calculated and expected struct instances in automated tests.
That said, values of different struct types compare as #f, even if the field names are the same.
Although Racket uses opaque structs for stronger encapsulation and backward-compatibility, many Racket users nowadays think that defining structs as transparent usually gives the better tradeoff.
Other languagesNote that field names are only known at compile time, not at runtime. This means that a function like Python’s getattr doesn’t exist in Racket.
Binding, Functional update in this glossary
Programmer-Defined Datatypes, Classes and Objects in the Racket Guide
Defining Structure Types: struct, Classes and Objects in the Racket Reference
Symbol
> "a-string" "a-string"
> 'a-symbol 'a-symbol
Symbols are idiomatically used for enumeration values. See the open-output-file function for an example, which uses symbols for the mode and exists keyword arguments.
By default, symbols are interned, while there’s no such guarantee for strings. Interning has the consequence that two “same” symbols compare equal with eq?, not just with equal?. Therefore, hashes that have symbols as keys can use the eq? variants, which speeds up the hash operations.
Because of the different uses of strings and symbols, the standard library has no APIs to search in symbols or concatenate them. If you need such functionality, you can convert between symbols and strings with symbol->string and string->symbol.
Syntactic form
Syntax (different meanings)
Level: intermediate
Syntax transformer
Tail call
Level: intermediate
Tail position
Level: intermediate
Testing
Level: basic
Thread
Level: intermediate
Thunk
> (define (verbose-if condition true-branch false-branch) (if condition (begin (displayln "condition is true") true-branch) (begin (displayln "condition is false") false-branch)))
> (verbose-if (> 5 4) (displayln "yes") (displayln "no"))
yes
no
condition is true
doesn’t work because in the call of verbose-if both the true-branch and the false-branch are already evaluated before executing the body of verbose-if.
However, you can control the execution of the true-branch and the false-branch by turning them into thunks:
> (define (verbose-if condition true-branch false-branch) (if condition (begin (displayln "condition is true") (true-branch)) (begin (displayln "condition is false") (false-branch))))
> (verbose-if (> 5 4) (lambda () (displayln "yes")) (lambda () (displayln "no")))
condition is true
yes
Note that you need to change both the function and the use of it.
Even if the thunks need data from the surrounding scope, you don’t need to define and pass arguments because the thunks still have access to that scope even if they’re executed in verbose-if:
> (define (call-verbose-if) (define true-string "yes") (define false-string "no") (verbose-if (> 5 4) (lambda () (displayln true-string)) (lambda () (displayln false-string)))) > (call-verbose-if)
condition is true
yes
Transparent
Trust level
Level: advanced
Trusted code
Typed Racket
Level: advanced
Undefined
Level: advanced
Unit
Level: advanced
Unsafe operation
Level: intermediate
Untrusted code
Level: advanced
Value
Level: basic
Values
> (values 1 2)
1
2
> (define a-name (values 1 2)) result arity mismatch;
expected number of values not received
expected: 1
received: 2
> (define-values (foo bar) (values 1 2)) > foo 1
> bar 2
> (let-values ([(foo bar) (values 1 2)]) (+ foo bar)) 3
By far the most functions in the standard library return a single value. Some functions that return multiple values, apart from values, are split-at, drop-common-prefix and split-common-prefix.
> (list) '()
> (list 1) '(1)
> (list 1 2) '(1 2)
> (list (values 1 2)) result arity mismatch;
expected number of values not received
expected: 1
received: 2
Other languagesSome documentation on other programming languages, for example Python, sometimes uses the term “multiple values” to mean a single compound value. This is different from the “multiple values” concept in Scheme and Racket.Here’s a Python example:
def return_tuple():
return 1, 2
result = return_tuple()
print(type(result)) # tuple
print(result[0]) # 1
print(result[1]) # 2
Definition, Let in this glossary
Vector
; Simple vector with some numbers. > #(1 2 3) '#(1 2 3)
; Empty vector > #() '#()
; Items are quoted. This vector does _not_ contain the `map` function. > (define vec1 #(map)) > (vector-ref vec1 0) 'map
; Use `vector` to avoid quoting. > (define vec2 (vector map)) > (vector-ref vec2 0) #<procedure:map>
> (define vec1 #(1 2 3)) > (vector-set! vec1 1 5) vector-set!: contract violation
expected: (and/c vector? (not/c immutable?))
given: '#(1 2 3)
> (define vec2 (vector-immutable 1 2 3)) > (vector-set! vec2 1 5) vector-set!: contract violation
expected: (and/c vector? (not/c immutable?))
given: '#(1 2 3)
> (define vec3 (vector 1 2 3)) > (vector-set! vec3 1 5) > vec3 '#(1 5 3)
There are several vector functions (e.g. vector-map or vector-filter) that correspond to similar list functions. If the existing vector functions aren’t enough, it may make sense to convert a vector to a list with vector->list, process the list with list functions and finally convert the list back to a vector with list->vector.
Collection, List in this glossary
Vectors in the Racket Guide
Vectors in the Racket Reference
Void
Experimenting with #<void> can be confusing because the Racket REPL (interactive interpreter) doesn’t show it, but you can check the result for being #<void> with the void? predicate.
Another curiosity is that you can’t enter the #<void> value in source code or the REPL. However, you can create the value with (void).
Other languagesPython has a value None, whose semantics are similar to #<void>. However, while the use of None in Python is idiomatic to denote an unset optional argument, Scheme and Racket code typically uses #f for the same purpose.Example:
> (define (hello [who #f]) (string-append "Hello, " (if who who "world") "!")) > (hello) "Hello, world!"
> (hello "Mike") "Hello, Mike!"
Since #f is the only value that’s treated as false in conditions, this usage normally makes sense.That said, it’s fine to use (void) as default and check the argument with void? if #f could be an actual value for the argument.
Will
Level: advanced
Write
Writer
Level: advanced