Racket featuresRacket has been under active development as a vehicle for programming language research since the mid-1990s, and has accumulated many features over the years. This article describes and demonstrates some of these features. Note that one of Racket's main design goals is to accommodate creating new programming languages, both domain-specific languages and completely new languages.[1] Therefore, some of the following examples are in different languages, but they are all implemented in Racket. Please refer to the main article for more information. The core Racket implementation is highly flexible. Even without using dialects, it can function as a full-featured scripting language, capable of running both with and without windows-native graphical user interface (GUI), and capable of tasks from web server creation to graphics. Runtime supportGarbage collection, tail calls, space safetyRacket can use three different garbage collectors:
Like all implementations in the Scheme family, Racket implements full tail call elimination. Racket takes this further: the language is made fully safe-for-space, via live variable analysis. This complements the precise garbage collector and in some cases, like in the implementation of Lazy Racket, the two features are crucial for proper execution. This is in addition to further compiler optimizations such as lambda lifting and just-in-time compilation. System interface and scriptingRacket's system interface includes asynchronous non-blocking I/O, green threads, synchronization channels, semaphores, sub-processes, and Transmission Control Protocol (TCP) sockets. The following program starts an "echo server" on port 12345. #lang racket
(define listener (tcp-listen 12345))
(let echo-server ()
;; create a TCP server
(define-values (in out) (tcp-accept listener))
;; handle an incoming connection in a (green) thread
(thread (λ () (copy-port in out) (close-output-port out)))
;; and immediately loop back to accept more clients
(echo-server))
The combination of dynamic compilation and a rich system interface makes Racket a capable scripting language, similar to Perl or Python. The following example demonstrates walking a directory tree, starting at the current directory. It uses the #lang racket
;; Finds Racket sources in all subdirs
(for ([path (in-directory)]) ; iterate over the current tree
(when (regexp-match? #rx"[.]rkt$" path)
(printf "source file: ~a\n" path)))
The next example uses a hash table to record previously seen lines and print only unique ones. #lang racket
;; Report each unique line from stdin
(let ([saw (make-hash)])
(for ([line (in-lines)])
(unless (hash-ref saw line #f)
(displayln line))
(hash-set! saw line #t)))
Both of these programs can be run in DrRacket, or on the command line, via the #!/usr/bin/env racket
#lang racket
(command-line
#:args (base-dir ext re)
(for ([p (in-directory)]
#:when (regexp-match? (string-append "[.]" ext "$") p)
[(line num) (in-indexed (file->lines p))])
(when (regexp-match? (pregexp re) line)
(printf "~a:~a: ~a~n" p (+ num 1) line))))
The script is a grep-like utility, expecting three command-line arguments: a base directory, a filename extension, and a (perl-compatible) regular expression. It scans the base directory for files with the given suffix, and print lines matching the regexp pattern. Resource management and sandboxingRacket features the concept of a "custodian": a kind of value that acts as a resource manager. This is often used in network servers, where each connection is dealt with in a new custodian, making it easy to "clean-up" all resources that might have been left open by the handler (e.g., open ports). The following extends the "echo server" example with such a custodian use: #lang racket
(define listener (tcp-listen 12345))
;; per-connection handler
(define (handler in out)
(copy-port in out)
(close-output-port out))
(let echo-server ()
(define-values (in out) (tcp-accept listener))
(thread (λ () (let ([c (make-custodian)])
(parameterize ([current-custodian c])
(handler in out)
(custodian-shutdown-all c)))))
(echo-server))
Custodians, combined with the memory accounting feature of the 3m garbage collector, and several added runtime parameters that control more aspects of the runtime, make it possible to create fully safe sandboxed execution contexts. The #lang racket
(require racket/sandbox)
(define e (make-evaluator 'racket/base))
(let-values ([(i o) (tcp-accept (tcp-listen 9999))])
(parameterize ([current-input-port i]
[current-output-port o]
[current-error-port o]
[current-eval e]
[current-read-interaction (λ (x in) (read in))])
(read-eval-print-loop)
(fprintf o "\nBye...\n")
(close-output-port o)))
Web and network programmingThe next example implements a web server using the #lang web-server/insta
;; A tiny "hello world" web server
(define (start request)
(response/xexpr '(html (body "Hello World"))))
Racket also includes the functions needed to write scrapers and robots. As an example, the following function lists the Google results for a search string. #lang racket
;; Simple web scraper
(require net/url net/uri-codec)
(define (let-me-google-that str)
(let* ([g "http://www.google.com/search?q="]
[u (string-append g (uri-encode str))]
[rx #rx"(?<=<h3 class=\"r\">).*?(?=</h3>)"])
(regexp-match* rx (get-pure-port (string->url u)))))
The library also includes support for protocols other than http: #lang racket
;; Sending a timed email alert from racket
(require net/sendmail)
(sleep (* (- (* 60 4) 15) 60)) ; wait 3h 45m
(send-mail-message
(getenv "EMAIL") "Parking meter alert!"
(list (getenv "EMAIL")) null null
'("Time to go out and move the car."))
GraphicsGraphic capabilities come in several different flavors that are intended for different audiences. The #lang racket
;; A picture
(require 2htdp/image)
(let sierpinski ([n 8])
(if (zero? n)
(triangle 2 'solid 'red)
(let ([t (sierpinski (- n 1))])
(freeze (above t (beside t t))))))
DrRacket editors can contain images, and DrRacket displays image values just like any other type of value (such as integers or lists). Running the above program, for example, actually displays a Sierpinski triangle, which can be cut and pasted into another program. The #lang racket
;; Visualize a sum of two 3D Gaussians as concentric isosurfaces
;; Note: this example requires Racket 5.2 or later
(require plot)
;; Returns an R x R x R -> R Gaussian function centered at (cx,cy,cz)
(define ((gaussian cx cy cz) x y z)
(exp (- (+ (sqr (- x cx)) (sqr (- y cy)) (sqr (- z cz))))))
;; Lifts + to operate on three-argument functions
(define ((f3+ g h) x y z) (+ (g x y z) (h x y z)))
;; Constructs an image value representing the sum of two Gaussians
(plot3d (isosurfaces3d (f3+ (gaussian 0 0 0) (gaussian 1.5 -1.5 0))
-1 2.5 -2.5 1 -1 1
#:label "g")) ; labeling adds a legend
Here, the GUI programmingRacket implements a portable GUI layer which the libraries mentioned above build on. It is implemented via the native Windows application programming interface (API), via Cocoa on macOS, and via GTK+ on Linux and others. The Racket API is a class-based toolkit, somewhat related to wxWidgets which was used originally. The following simple guessing game demonstrates coding with the GUI toolkit. The #lang racket/gui
;; A GUI guessing game
(define secret (random 5))
(define f (new frame% [label "Guessing game"])) ; toplevel window
(define t (new message% [parent f]
[label "Can you guess the number I'm thinking about?"]))
(define p (new horizontal-pane% [parent f])) ; horizontal container
(define ((make-check i) btn evt)
(message-box "." (cond [(< i secret) "Too small"]
[(> i secret) "Too big"]
[else "Exactly!"]))
(when (= i secret) (send f show #f))) ; success => close window
(for ([i (in-range 10)]) ; create all buttons
(make-object button% (format "~a" i) p (make-check i)))
(send f show #t) ; show the window to start the application
The GUI can be hand-coded in this way or with the help of a GUI designer program available on PLaneT.[2] SlideshowSlide-based presentations can also be developed in Racket using the For example, the following program displays in full-screen a title slide, followed by a slide with some pictures. The #lang slideshow
(slide
(text "Slideshow" 'roman 56)
(text "Making presentations in Racket"
'roman 40))
(slide
#:title "Some pictures"
(apply vc-append
(for/list ([i 5])
(define (scale+color p c)
(colorize (scale p (/ (add1 i) 5)) c))
(hc-append
(scale+color (filled-rectangle 100 50) "darkblue")
(scale+color (disk 100) "darkgreen")
(scale+color (arrow 100 (/ pi 6)) "darkred")
))))
Extension packages also exist on PLaneT,[2] for example to include LaTeX elements. Foreign function interfaceRacket features a foreign function interface that is based on libffi. The interface allows writing unsafe low-level C-like code, that can allocate memory, dereference pointers, call out to functions in shared libraries, and send out callbacks to Racket functions (using libffi closures). The core implementation is a thin layer atop libffi (written in C), and the full interface is then implemented via Racket code. The interface uses macros extensively, resulting in an expressive Racket-based interface description language. This language has a number of useful features, such as uniform representation for higher-order functions (avoiding the pitfalls when callbacks and callouts are different), struct definitions that are similar to plain Racket structs, and custom function types that can represent input and output pointers, implicit arguments (e.g., an argument that provides the number of elements in a vector that is passed as another argument). By using this interface to access underlying GUI toolkits, Racket implements its own GUI layer, in Racket.[3] The FFI can be used in a number of different ways: from writing a complete glue layer for a library (as done for Racket's OpenGL binding), to quickly pulling out a single foreign function. An example of the latter approach: #lang racket/base
;; Simple use of the FFI
(require ffi/unsafe)
(define mci-send-string
(get-ffi-obj "mciSendStringA" "Winmm"
(_fun _string [_pointer = #f] [_int = 0] [_pointer = #f]
-> [ret : _int])))
(mci-send-string "play sound.wav wait")
Language extensionsRacket's most notable feature is its ability to build new domain-specific and general-purpose languages. This is the result of combining a number of important features:
The module system plays an important role in combining these features, and making it possible to write code that spans across a number of modules, where each can be written in a different language. Such languages are used extensively in the Racket distribution and in user libraries. In fact, creating a new language is so straightforward, that there are some languages that have less than a handful of uses. Racket comes with a number of useful languages, some are very different from Racket's default language. ScribbleScribble, Racket's documentation system, comes in the form of a number of languages that are used to write prose. It is used for Racket's documentation, as well as writing books and articles. Actually, rather than a single "scribble" language, it is a family of (very similar) dialects, each for a different purpose. To run the following example, copy it into DrRacket and click one of the two scribble rendering buttons that will appear (PDF rendering requires pdfTeX). Alternatively, use the #lang scribble/base
@; Generate a PDF or an HTML document using `scribble'
@(require (planet neil/numspell))
@title{99 Bottles of Beer}
In case you need some @emph{blah blah} in your life.
@(apply itemlist
(for/list ([n (in-range 99 0 -1)])
(define N (number->english n))
(define N-- (number->english (sub1 n)))
@item{@string-titlecase[N] bottles of beer on the wall,
@N bottles of beer.
Take one down, pass it around,
@N-- bottles of beer on the wall.}))
The most striking feature of the Scribble languages is their use of a new syntax, which is designed specifically for textually rich code.[4] The syntax allows free-form text, string interpolation, customizable quotations, and is useful in other applications such as preprocessing text, generating text, and HTML template systems. Note that the syntax extends plain S-expressions, and is implemented as an alternative input for such expressions. #lang scribble/text
Hi,
I'm a text file -- run me.
@(define (thrice . text) @list{@text, @text, @text})
@thrice{SPAM}!
@thrice{HAM}!
Typed RacketTyped Racket is a statically typed variant of Racket. The type system that it implements is unique in that the motivation in developing it was accommodating as much idiomatic Racket code as possible—as a result, it includes subtypes, unions, and much more.[5] Another goal of Typed Racket is to allow migration of parts of a program into the typed language, so it accommodates calling typed code from untyped code and vice versa, generating dynamic contracts to enforce type invariants.[6] This is considered a desirable feature of an application's lifetime stages, as it matures from "a script" to "an application", where static typing helps in maintenance of a large body of code. #lang typed/racket
;; Using higher-order occurrence typing
(define-type Str-or-Num (U String Number))
(: tog ((Listof Str-or-Num) -> String))
(define (tog l)
(apply string-append (filter string? l)))
(tog (list 5 "hello " 1/2 "world" (sqrt -1)))
Lazy RacketThe #lang lazy
;; An infinite list:
(define fibs
(list* 1 1 (map + fibs (cdr fibs))))
;; Print the 1000th Fibonacci number:
(print (list-ref fibs 1000))
Logic programmingRacket comes with three logic programming languages: Racklog, a Prolog-like language; a Datalog implementation; and a miniKanren port. Unlike the Scribble syntax, the first two of these languages use an all-new syntax rather than an extension of S-expressions. If used it in DrRacket, it provides proper highlighting, the usual host of tools check syntax, and a Prolog/Datalog REPL. #lang datalog
ancestor(A, B) :- parent(A, B).
ancestor(A, B) :-
parent(A, C), D = C, ancestor(D, B).
parent(john, douglas).
parent(bob, john).
ancestor(A, B)?
Education toolsThe PLT group which develops Racket has traditionally been involved in education at all levels. One of the earliest research ideas that the group promoted is the use of language levels, which restrict new students while providing them with helpful error messages that fit the student's level of knowledge. This approach is heavily used in How to Design Programs (HtDP), the textbook that several PLT developers have authored, and in the ProgramByDesign project. The following program uses the #lang htdp/bsl
;; Any key inflates the balloon
(require 2htdp/image)
(require 2htdp/universe)
(define (balloon b) (circle b "solid" "red"))
(define (blow-up b k) (+ b 5))
(define (deflate b) (max (- b 1) 1))
(big-bang 50 (on-key blow-up) (on-tick deflate)
(to-draw balloon 200 200))
ALGOLRacket comes with a full implementation of the ALGOL 60 language. #lang algol60
begin
integer procedure SIGMA(x, i, n);
value n;
integer x, i, n;
begin
integer sum;
sum := 0;
for i := 1 step 1 until n do
sum := sum + x;
SIGMA := sum;
end;
integer q;
printnln(SIGMA(q*2-1, q, 7));
end
Plai and plai-typed #lang plai
#lang plai-typed
Another supported language is plai which like racket can be typed or untyped. "Modules written in plai export every definition (unlike scheme)."[7] "The Typed PLAI language differs from traditional Racket most importantly by being statically typed. It also gives some useful new constructs: define-type, type-case, and test."[8] Creating languagesFinally, the following example is an implementation of a new language: #lang racket
(provide (except-out (all-from-out racket)
#%top #%app)
(rename-out [top #%top] [app #%app]))
(define-syntax-rule (top . x) 'x)
(define-syntax-rule (app f . xs)
(if (hash? f) (hash-ref f . xs) (f . xs)))
This language:
If this code is stored in a #lang s-exp "mylang.rkt" ; sexpr syntax, using mylang semantics
(define h (make-hasheq))
(hash-set! h A B) ; A and B are self-evaluating here
(h A) ; the hash table is used as a function
References
External links |