Racket features

Last updated

Racket 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.

Contents

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 support

Garbage collection, tail calls, space safety

Racket 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 scripting

Racket'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(definelistener(tcp-listen12345))(letecho-server();; create a TCP server(define-values(inout)(tcp-acceptlistener));; handle an incoming connection in a (green) thread(thread(λ()(copy-portinout)(close-output-portout)));; 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 in-directory function to construct a sequence that walks the tree. The for form binds path to each path in the sequence, and regexp-match? tests these paths against the given regexp pattern.

#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-refsawline#f)(displaylnline))(hash-set!sawline#t)))

Both of these programs can be run in DrRacket, or on the command line, via the racket executable. Racket ignores an initial shebang line, making it possible to turn such programs to executable scripts. The following script demonstrates this, in addition to using Racket's library for command-line argument parsing:

#!/usr/bin/env racket#lang racket(command-line#:args(base-dirextre)(for([p(in-directory)]#:when(regexp-match?(string-append"[.]"ext"$")p)[(linenum)(in-indexed(file->linesp))])(when(regexp-match?(pregexpre)line)(printf"~a:~a: ~a~n"p(+num1)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 sandboxing

Racket 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(definelistener(tcp-listen12345));; per-connection handler(define(handlerinout)(copy-portinout)(close-output-portout))(letecho-server()(define-values(inout)(tcp-acceptlistener))(thread(λ()(let([c(make-custodian)])(parameterize([current-custodianc])(handlerinout)(custodian-shutdown-allc)))))(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 racket/sandbox library provides this kind of functionality in a simple way. The following example creates a read–eval–print loop (REPL) server on the specified port; connecting to this port will look like a plain Racket REPL, except that the evaluation is subject to the various protection aspects of the sandbox. For example, it is not possible to access the file system from this REPL, create network connection, run subprocesses, or use too much time or memory. (In fact, this REPL is safe enough to be given out publicly.)

#lang racket(requireracket/sandbox)(definee(make-evaluator'racket/base))(let-values([(io)(tcp-accept(tcp-listen9999))])(parameterize([current-input-porti][current-output-porto][current-error-porto][current-evale][current-read-interaction(λ(xin)(readin))])(read-eval-print-loop)(fprintfo"\nBye...\n")(close-output-porto)))

Web and network programming

The next example implements a web server using the web-server/insta language. Each time a connection is made to the server, the start function is called to get the HTML to send back to the client.

#lang web-server/insta;; A tiny "hello world" web server(define(startrequest)(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(requirenet/urlnet/uri-codec)(define(let-me-google-thatstr)(let*([g"http://www.google.com/search?q="][u(string-appendg(uri-encodestr))][rx#rx"(?<=<h3 class=\"r\">).*?(?=</h3>)"])(regexp-match*rx(get-pure-port(string->urlu)))))

The library also includes support for protocols other than http:

#lang racket;; Sending a timed email alert from racket(requirenet/sendmail)(sleep(*(-(*604)15)60)); wait 3h 45m(send-mail-message(getenv"EMAIL")"Parking meter alert!"(list(getenv"EMAIL"))nullnull'("Time to go out and move the car."))

Graphics

Graphic capabilities come in several different flavors that are intended for different audiences. The 2htdp/image library provides convenient functions for constructing images. This library is mainly used by students in How to Design Programs (HtDP) based courses. In the following example, a sierpinski function is defined and called (at the same time) to generate a Sierpinski triangle of depth 8.

#lang racket;; A picture(require2htdp/image)(letsierpinski([n8])(if(zero?n)(triangle2'solid'red)(let([t(sierpinski(-n1))])(freeze(abovet(besidett))))))

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 plot library constructs image values for more mature audiences and needs. For example, the following program plots the sum of two (three-dimensional) Gaussians, as concentric, partially transparent surfaces:

#lang racket;; Visualize a sum of two 3D Gaussians as concentric isosurfaces;; Note: this example requires Racket 5.2 or later(requireplot);; Returns an R x R x R -> R Gaussian function centered at (cx,cy,cz)(define((gaussiancxcycz)xyz)(exp(-(+(sqr(-xcx))(sqr(-ycy))(sqr(-zcz))))));; Lifts + to operate on three-argument functions(define((f3+gh)xyz)(+(gxyz)(hxyz)));; Constructs an image value representing the sum of two Gaussians(plot3d(isosurfaces3d(f3+(gaussian000)(gaussian1.5-1.50))-12.5-2.51-11#:label"g")); labeling adds a legend

Here, the isosurfaces3d function requires a three-argument function for its first argument, which the curried f3+ supplies. Besides constructing image values, plot can also write files in Portable Network Graphics (PNG), Portable Document Format (PDF), PostScript and Scalable Vector Graphics (SVG) formats.

GUI programming

Racket 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 frame% class implements a top-level window, and button% implements a button. The check function defined here produces a function that is used for the button's callback action.

#lang racket/gui;; A GUI guessing game(definesecret(random5))(definef(newframe%[label"Guessing game"])); toplevel window(definet(newmessage%[parentf][label"Can you guess the number I'm thinking about?"]))(definep(newhorizontal-pane%[parentf])); horizontal container(define((make-checki)btnevt)(message-box"."(cond[(<isecret)"Too small"][(>isecret)"Too big"][else"Exactly!"]))(when(=isecret)(sendfshow#f))); success => close window(for([i(in-range10)]); create all buttons(make-objectbutton%(format"~a"i)p(make-checki)))(sendfshow#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]

Slideshow

Slide-based presentations can also be developed in Racket using the slideshow language, much like Beamer, but with Racket's programming facilities. Elements of the slides are pictures that can be combined.

For example, the following program displays in full-screen a title slide, followed by a slide with some pictures. The vc-append and hc-append functions combine pictures vertically and horizontally, respectively, and centered on the other axis.

#lang slideshow(slide(text"Slideshow"'roman56)(text"Making presentations in Racket"'roman40))(slide#:title"Some pictures"(applyvc-append(for/list([i5])(define(scale+colorpc)(colorize(scalep(/(add1i)5))c))(hc-append(scale+color(filled-rectangle10050)"darkblue")(scale+color(disk100)"darkgreen")(scale+color(arrow100(/pi6))"darkred")))))

Extension packages also exist on PLaneT, [2] for example to include LaTeX elements.

Foreign function interface

Racket 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(requireffi/unsafe)(definemci-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 extensions

Racket'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.

Scribble

Scribble, 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 scribble executable on the file.

#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 Racket

Typed 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-typeStr-or-Num(UStringNumber))(:tog((ListofStr-or-Num)->String))(define(togl)(applystring-append(filterstring?l)))(tog(list5"hello "1/2"world"(sqrt-1)))

Lazy Racket

The lazy language is a language with lazy evaluation semantics, similar to Haskell. In the following example, fibs is an infinite list which 1000th element will not be computed until its value is needed for the printout.

#lang lazy;; An infinite list:(definefibs(list*11(map+fibs(cdrfibs))));; Print the 1000th Fibonacci number:(print(list-reffibs1000))

Logic programming

Racket 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.

#langdatalogancestor(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 tools

The 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 htdp/bsl—the "beginning student language". It uses the 2htdp/image library for creating pictures in the teaching languages, and the 2htdp/universe library for interactive animations.

#lang htdp/bsl;; Any key inflates the balloon(require2htdp/image)(require2htdp/universe)(define(balloonb)(circleb"solid""red"))(define(blow-upbk)(+b5))(define(deflateb)(max(-b1)1))(big-bang50(on-keyblow-up)(on-tickdeflate)(to-drawballoon200200))

ALGOL

Racket comes with a full implementation of the ALGOL 60 language.

#langalgol60beginintegerprocedureSIGMA(x,i,n);valuen;integerx,i,n;beginintegersum;sum:=0;fori:=1step1untilndosum:=sum+x;SIGMA:=sum;end;integerq;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 languages

Finally, the following example is an implementation of a new language:

#lang racket(provide(except-out(all-from-outracket)#%top#%app)(rename-out[top#%top][app#%app]))(define-syntax-rule(top.x)'x)(define-syntax-rule(appf.xs)(if(hash?f)(hash-reff.xs)(f.xs)))

This language:

If this code is stored in a mylang.rkt file, it can be used as follows:

#lang s-exp"mylang.rkt"; sexpr syntax, using mylang semantics(defineh(make-hasheq))(hash-set!hAB); A and B are self-evaluating here(hA); the hash table is used as a function

Related Research Articles

Eiffel is an object-oriented programming language designed by Bertrand Meyer and Eiffel Software. Meyer conceived the language in 1985 with the goal of increasing the reliability of commercial software development; the first version becoming available in 1986. In 2005, Eiffel became an ISO-standardized language.

<span class="mw-page-title-main">PHP</span> Scripting language created in 1994

PHP is a general-purpose scripting language geared towards web development. It was originally created by Danish-Canadian programmer Rasmus Lerdorf in 1993 and released in 1995. The PHP reference implementation is now produced by the PHP Group. PHP was originally an abbreviation of Personal Home Page, but it now stands for the recursive initialism PHP: Hypertext Preprocessor.

<span class="mw-page-title-main">Ruby (programming language)</span> General-purpose programming language

Ruby is an interpreted, high-level, general-purpose programming language which supports multiple programming paradigms. It was designed with an emphasis on programming productivity and simplicity. In Ruby, everything is an object, including primitive data types. It was developed in the mid-1990s by Yukihiro "Matz" Matsumoto in Japan.

Java Platform, Standard Edition is a computing platform for development and deployment of portable code for desktop and server environments. Java SE was formerly known as Java 2 Platform, Standard Edition (J2SE).

Berkeley sockets is an application programming interface (API) for Internet sockets and Unix domain sockets, used for inter-process communication (IPC). It is commonly implemented as a library of linkable modules. It originated with the 4.2BSD Unix operating system, which was released in 1983.

ABAP is a high-level programming language created by the German software company SAP SE. It is currently positioned, alongside Java, as the language for programming the SAP NetWeaver Application Server, which is part of the SAP NetWeaver platform for building business applications.

<span class="mw-page-title-main">Factor (programming language)</span> Stack-oriented programming language

Factor is a stack-oriented programming language created by Slava Pestov. Factor is dynamically typed and has automatic memory management, as well as powerful metaprogramming features. The language has a single implementation featuring a self-hosted optimizing compiler and an interactive development environment. The Factor distribution includes a large standard library.

xHarbour is a free multi-platform extended Clipper compiler, offering multiple graphic terminals (GTs), including console drivers, GUIs, and hybrid console/GUIs. xHarbour is backward-compatible with Clipper and supports many language syntax extensions, greatly extended run-time libraries, and extensive third party support.

Harbour is a computer programming language, primarily used to create database/business programs. It is a modernized, open source and cross-platform version of the older Clipper system, which in turn developed from the dBase database market of the 1980s and 1990s.

<span class="mw-page-title-main">Racket (programming language)</span> Lisp dialect

Racket is a general-purpose, multi-paradigm programming language and a multi-platform distribution that includes the Racket language, compiler, large standard library, IDE, development tools, and a set of additional languages including Typed Racket, Swindle, FrTime, Lazy Racket, R5RS & R6RS Scheme, Scribble, Datalog, Racklog, Algol 60 and several teaching languages.

Haxe is a high-level cross-platform programming language and compiler that can produce applications and source code for many different computing platforms from one code-base. It is free and open-source software, released under the MIT License. The compiler, written in OCaml, is released under the GNU General Public License (GPL) version 2.

In computer programming, an anonymous function is a function definition that is not bound to an identifier. Anonymous functions are often arguments being passed to higher-order functions or used for constructing the result of a higher-order function that needs to return a function. If the function is only used once, or a limited number of times, an anonymous function may be syntactically lighter than using a named function. Anonymous functions are ubiquitous in functional programming languages and other languages with first-class functions, where they fulfil the same role for the function type as literals do for other data types.

<span class="mw-page-title-main">Vala (programming language)</span> Programming language

Vala is an object-oriented programming language with a self-hosting compiler that generates C code and uses the GObject system.

This comparison of programming languages (array) compares the features of array data structures or matrix processing for various computer programming languages.

<span class="mw-page-title-main">Go (programming language)</span> Programming language

Go is a statically typed, compiled high-level programming language designed at Google by Robert Griesemer, Rob Pike, and Ken Thompson. It is syntactically similar to C, but also has memory safety, garbage collection, structural typing, and CSP-style concurrency. It is often referred to as Golang because of its former domain name, golang.org, but its proper name is Go.

CUBRID ( "cube-rid") is an open-source SQL-based relational database management system (RDBMS) with object extensions developed by CUBRID Corp. for OLTP. The name CUBRID is a combination of the two words cube and bridge, cube standing for a space for data and bridge standing for data bridge.

Nemerle is a general-purpose, high-level, statically typed programming language designed for platforms using the Common Language Infrastructure (.NET/Mono). It offers functional, object-oriented, aspect-oriented, reflective and imperative features. It has a simple C#-like syntax and a powerful metaprogramming system.

Seed7 is an extensible general-purpose programming language designed by Thomas Mertes. It is syntactically similar to Pascal and Ada. Along with many other features, it provides an extension mechanism. Seed7 supports introducing new syntax elements and their semantics into the language, and allows new language constructs to be defined and written in Seed7. For example, programmers can introduce syntax and semantics of new statements and user defined operator symbols. The implementation of Seed7 differs significantly from that of languages with hard-coded syntax and semantics.

<span class="mw-page-title-main">Elm (programming language)</span> Functional programming language

Elm is a domain-specific programming language for declaratively creating web browser-based graphical user interfaces. Elm is purely functional, and is developed with emphasis on usability, performance, and robustness. It advertises "no runtime exceptions in practice", made possible by the Elm compiler's static type checking.

<span class="mw-page-title-main">Nim (programming language)</span> Programming language

Nim is a general-purpose, multi-paradigm, statically typed, compiled high-level systems programming language, designed and developed by a team around Andreas Rumpf. Nim is designed to be "efficient, expressive, and elegant", supporting metaprogramming, functional, message passing, procedural, and object-oriented programming styles by providing several features such as compile time code generation, algebraic data types, a foreign function interface (FFI) with C, C++, Objective-C, and JavaScript, and supporting compiling to those same languages as intermediate representations.

References

  1. Tobin-Hochstadt, S.; St-Amour, V.; Culpepper, R.; Flatt, M.; Felleisen, M. (2011). "Languages as Libraries" (PDF). Programming Language Design and Implementation.
  2. 1 2 PLaneT: Racket's centralized package distribution system
  3. "Rebuilding Racket's Graphics Layer". 2010-12-08. Archived from the original on 2013-02-02. Retrieved 2013-07-07.
  4. Barzilay, E. (2009). "The Scribble Reader" (PDF). Scheme and Functional Programming.
  5. Tobin-Hochstadt, S.; Felleisen, M. (2008). "The Design and Implementation of Typed Scheme". Principles of Programming Languages.
  6. Tobin-Hochstadt, S.; Felleisen, M. (2006). "Interlanguage Migration: From Scripts to Programs". Dynamic Languages Symposium.
  7. "1 PLAI Scheme".
  8. Krishnamurthi, Shriram. "Programming Languages: Application and Interpretation." Programming Languages: Application and Interpretation. Brown University, n.d. Web. 14 Mar. 2016. <http://cs.brown.edu/courses/cs173/2012/book/>.
  9. Note that #%app is a macro that is used in all function calls, making this language not too efficient, as every function call incurs an added condition. Also, the macro evaluates the function expression twice, so it should not be taken as an example of good macro-programming.